import Foundation

/// The per-session snapshot cadence state machine (docs/CARBON_SHARDING.md §6.4, D8):
/// 20 s while dirty, exponential backoff to 2 min across no-op ticks, immediate reset on any
/// activity signal (turn boundary, `sendInput`, a file event). Pure value type — the driver
/// (`AppStore`) owns the timer; this owns only the arithmetic, so the policy is table-testable.
public struct CarbonCadence: Sendable, Equatable {
    public static let baseInterval: TimeInterval = 20
    public static let maxInterval: TimeInterval = 120

    /// Seconds until the next tick for this session.
    public private(set) var interval: TimeInterval

    public init(interval: TimeInterval = CarbonCadence.baseInterval) {
        self.interval = interval
    }

    /// A tick captured a new snapshot — stay hot.
    public mutating func recordSnapshot() {
        interval = Self.baseInterval
    }

    /// A tick no-opped (clean tree or identical trees) — back off, capped.
    public mutating func recordNoOp() {
        interval = min(interval * 2, Self.maxInterval)
    }

    /// Turn boundary / user input / file event: reset AND request an immediate tick.
    /// Turn boundaries always snapshot even if the last tick was < 20 s ago (D8).
    public mutating func recordActivity() {
        interval = Self.baseInterval
    }
}
