import Foundation

// MARK: - HistoryCommitSeam

/// **The three places the auto-committer touches the store's write and reload paths**
/// (06-history-undo.md ▸ Rules ▸ Auto-commit, ▸ Flush-before-overwrite).
///
/// ### Why a struct of closures rather than a reference to the committer
///
/// `BoardStore` lives in the live store and must not learn what a repository is: the free tier
/// composes no `HistoryStore`, so the engine has to be *structurally* unreachable there rather than
/// switched off, and a store holding an optional committer would be a store that knows about git.
/// One optional value, `nil` on every board that has no committer, is the same shape `watcherBrackets`
/// and `history` already take, and it keeps the three orderings — before the write, after the
/// bracket, after the landing — stated in one type instead of three properties that could drift.
///
/// It is also what makes the ordering testable without a repository: a test binds a seam that records
/// its calls and asserts that a write flushed before it landed, exactly as `CloseFlushCoordinator`'s
/// closures do for the close sequence.
@MainActor
public struct HistoryCommitSeam {

    /// **Before an app write** — flush the pending auto-commit if this write could overwrite an
    /// external version that is not in history yet, so "both versions exist as commits" holds.
    ///
    /// Synchronous because `performWrite` is: an ordering guarantee *before* a synchronous write can
    /// only be kept synchronously. See `GitAutoCommitter.noteWillWrite()` for the gate that keeps it
    /// rare and for the costs it carries.
    public var willWrite: () -> Void

    /// **After a write bracket closes** — harvest the bracket's receipts and arm the debounce.
    ///
    /// The harvest is why this is a signal of its own: receipts are consumed by the landing reload
    /// that classifies them, and this is the last moment they still describe a completed write
    /// (`EchoLedger.outstandingEntries`).
    public var writeBracketDidClose: () -> Void

    /// **After a reload lands** — arm the debounce, carrying whether the reload revealed anything the
    /// ledger did not vouch for.
    public var reloadDidLand: (_ sawForeignChange: Bool) -> Void

    public init(
        willWrite: @escaping () -> Void,
        writeBracketDidClose: @escaping () -> Void,
        reloadDidLand: @escaping (Bool) -> Void
    ) {
        self.willWrite = willWrite
        self.writeBracketDidClose = writeBracketDidClose
        self.reloadDidLand = reloadDidLand
    }

    /// The seam a session binds for a board that has a committer — the one production composition,
    /// kept beside the type so no call site spells the three wirings out.
    public static func binding(to committer: GitAutoCommitter) -> HistoryCommitSeam {
        HistoryCommitSeam(
            willWrite: { [weak committer] in committer?.noteWillWrite() },
            writeBracketDidClose: { [weak committer] in committer?.noteWriteBracketClosed() },
            reloadDidLand: { [weak committer] saw in committer?.noteReloadLanded(sawForeignChange: saw) }
        )
    }
}
