/**
 * AutoSession — encapsulates all mutable auto-mode state into a single instance.
 *
 * Replaces ~40 module-level variables scattered across auto.ts with typed
 * properties on a class instance. Benefits:
 *
 * - reset() clears everything in one call (was 25+ manual resets in stopAuto)
 * - toJSON() provides diagnostic snapshots
 * - grep `s.` shows every state access
 * - Constructable for testing
 *
 * MAINTENANCE RULE: All new mutable auto-mode state MUST be added here as a
 * class property, not as a module-level variable in auto.ts. If the state
 * needs clearing on stop, add it to reset(). Tests in
 * auto-session-encapsulation.test.ts enforce that auto.ts has no module-level
 * `let` or `var` declarations.
 */
/**
 * Legal state transitions for the engine lifecycle.
 * Each key maps to the set of states it can transition to.
 */
export const VALID_TRANSITIONS = {
    idle: ['starting'],
    starting: ['running', 'stopping'],
    running: ['paused', 'stopping'],
    paused: ['running', 'stopping'],
    stopping: ['stopped'],
    stopped: ['idle'],
};
// ─── Constants ───────────────────────────────────────────────────────────────
export const MAX_UNIT_DISPATCHES = 3;
export const STUB_RECOVERY_THRESHOLD = 2;
export const MAX_LIFETIME_DISPATCHES = 6;
export const NEW_SESSION_TIMEOUT_MS = 30_000;
// ─── AutoSession ─────────────────────────────────────────────────────────────
export class AutoSession {
    // ── Lifecycle ────────────────────────────────────────────────────────────
    engineState = 'idle';
    _stepMode = false;
    verbose = false;
    activeEngineId = null;
    activeRunDir = null;
    cmdCtx = null;
    // ── Paths ────────────────────────────────────────────────────────────────
    basePath = "";
    originalBasePath = "";
    gitService = null;
    // ── Dispatch counters ────────────────────────────────────────────────────
    unitDispatchCount = new Map();
    unitLifetimeDispatches = new Map();
    unitRecoveryCount = new Map();
    // ── Timers ───────────────────────────────────────────────────────────────
    unitTimeoutHandle = null;
    wrapupWarningHandle = null;
    idleWatchdogHandle = null;
    continueHereHandle = null;
    // ── Current unit ─────────────────────────────────────────────────────────
    currentUnit = null;
    currentUnitRouting = null;
    currentMilestoneId = null;
    // ── Model state ──────────────────────────────────────────────────────────
    autoModeStartModel = null;
    currentUnitModel = null;
    originalModelId = null;
    originalModelProvider = null;
    lastBudgetAlertLevel = 0;
    // ── Fallback state ─────────────────────────────────────────────────────
    /** True when the current model is a fallback (not the primary). */
    fallbackActive = false;
    /** Model ID of the active fallback, or null if on primary. */
    fallbackModelId = null;
    // ── Recovery ─────────────────────────────────────────────────────────────
    pendingCrashRecovery = null;
    pendingVerificationRetry = null;
    verificationRetryCount = new Map();
    pausedSessionFile = null;
    resourceVersionOnStart = null;
    lastStateRebuildAt = 0;
    // ── Sidecar queue ─────────────────────────────────────────────────────
    sidecarQueue = [];
    // ── Isolation degradation ────────────────────────────────────────────
    /** Set to true when worktree creation fails; prevents merge of nonexistent branch. */
    isolationDegraded = false;
    // ── Merge guard ──────────────────────────────────────────────────────
    /** Set to true after phases.ts successfully calls mergeAndExit, so that
     *  stopAuto does not attempt the same merge a second time (#2645). */
    milestoneMergedInPhases = false;
    // ── Dispatch circuit breakers ──────────────────────────────────────
    rewriteAttemptCount = 0;
    // ── Execution-layer circuit breaker ──────────────────────────────────
    /** Tracks failure fingerprints and trips after repeated identical failures. */
    circuitBreaker = null;
    // ── Metrics ──────────────────────────────────────────────────────────────
    autoStartTime = 0;
    lastPromptCharCount;
    lastBaselineCharCount;
    pendingQuickTasks = [];
    // ── Signal handler ───────────────────────────────────────────────────────
    sigtermHandler = null;
    // ── Loop promise state ──────────────────────────────────────────────────
    // Per-unit resolve function and session-switch guard live at module level
    // in auto-loop.ts (_currentResolve, _sessionSwitchInFlight).
    // ── Methods ──────────────────────────────────────────────────────────────
    /**
     * Transition the engine to a new lifecycle state.
     * Throws if the transition is not in the VALID_TRANSITIONS graph.
     */
    transition(target) {
        const allowed = VALID_TRANSITIONS[this.engineState];
        if (!allowed?.includes(target)) {
            throw new Error(`Invalid state transition: ${this.engineState} → ${target}`);
        }
        this.engineState = target;
    }
    // ── Computed backward-compatible getters/setters ─────────────────────────
    /** True when the engine is in `running` or `starting` state. */
    get active() {
        return this.engineState === 'running' || this.engineState === 'starting';
    }
    /**
     * Backward-compatible setter. Maps boolean assignment to state transitions.
     * Prefer `transition()` for new code — this exists so unmigrated call sites
     * in auto.ts/auto-start.ts continue to work until T02 replaces them.
     */
    set active(v) {
        if (v) {
            // s.active = true: move to running (or starting→running)
            if (this.engineState === 'idle' || this.engineState === 'stopped') {
                this.engineState = 'running';
            }
            else if (this.engineState === 'paused' || this.engineState === 'starting') {
                this.engineState = 'running';
            }
            // already running: no-op
        }
        else {
            // s.active = false: move toward stopped
            if (this.engineState === 'running' || this.engineState === 'starting' || this.engineState === 'paused') {
                this.engineState = 'stopped';
            }
            // already idle/stopping/stopped: no-op
        }
    }
    /** True only when the engine is in `paused` state. */
    get paused() {
        return this.engineState === 'paused';
    }
    /**
     * Backward-compatible setter for paused state.
     * Prefer `transition()` for new code.
     */
    set paused(v) {
        if (v) {
            if (this.engineState === 'running' || this.engineState === 'starting') {
                this.engineState = 'paused';
            }
            // If stopped/idle and setting paused=true (legacy pattern in stopAuto),
            // force to paused for backward compat
            if (this.engineState === 'stopped' || this.engineState === 'idle') {
                this.engineState = 'paused';
            }
        }
        else {
            // s.paused = false is typically a no-op (clearing the flag), but if
            // currently paused it means resume — don't auto-transition to running
            // here because the caller usually sets active=true right after.
        }
    }
    get stepMode() {
        return this._stepMode;
    }
    set stepMode(v) {
        this._stepMode = v;
    }
    clearTimers() {
        if (this.unitTimeoutHandle) {
            clearTimeout(this.unitTimeoutHandle);
            this.unitTimeoutHandle = null;
        }
        if (this.wrapupWarningHandle) {
            clearTimeout(this.wrapupWarningHandle);
            this.wrapupWarningHandle = null;
        }
        if (this.idleWatchdogHandle) {
            clearInterval(this.idleWatchdogHandle);
            this.idleWatchdogHandle = null;
        }
        if (this.continueHereHandle) {
            clearInterval(this.continueHereHandle);
            this.continueHereHandle = null;
        }
    }
    resetDispatchCounters() {
        this.unitDispatchCount.clear();
        this.unitLifetimeDispatches.clear();
    }
    get lockBasePath() {
        return this.originalBasePath || this.basePath;
    }
    reset() {
        this.clearTimers();
        // Lifecycle
        this.engineState = 'idle';
        this._stepMode = false;
        this.verbose = false;
        this.activeEngineId = null;
        this.activeRunDir = null;
        this.cmdCtx = null;
        // Paths
        this.basePath = "";
        this.originalBasePath = "";
        this.gitService = null;
        // Dispatch
        this.unitDispatchCount.clear();
        this.unitLifetimeDispatches.clear();
        this.unitRecoveryCount.clear();
        // Unit
        this.currentUnit = null;
        this.currentUnitRouting = null;
        this.currentMilestoneId = null;
        // Model
        this.autoModeStartModel = null;
        this.currentUnitModel = null;
        this.originalModelId = null;
        this.originalModelProvider = null;
        this.lastBudgetAlertLevel = 0;
        this.fallbackActive = false;
        this.fallbackModelId = null;
        // Recovery
        this.pendingCrashRecovery = null;
        this.pendingVerificationRetry = null;
        this.verificationRetryCount.clear();
        this.pausedSessionFile = null;
        this.resourceVersionOnStart = null;
        this.lastStateRebuildAt = 0;
        // Metrics
        this.autoStartTime = 0;
        this.lastPromptCharCount = undefined;
        this.lastBaselineCharCount = undefined;
        this.pendingQuickTasks = [];
        this.sidecarQueue = [];
        this.rewriteAttemptCount = 0;
        this.isolationDegraded = false;
        this.milestoneMergedInPhases = false;
        this.circuitBreaker = null;
        // Signal handler
        this.sigtermHandler = null;
        // Loop promise state lives in auto-loop.ts module scope
    }
    toJSON() {
        return {
            engineState: this.engineState,
            active: this.active,
            paused: this.paused,
            stepMode: this.stepMode,
            basePath: this.basePath,
            activeEngineId: this.activeEngineId,
            activeRunDir: this.activeRunDir,
            currentMilestoneId: this.currentMilestoneId,
            currentUnit: this.currentUnit,
            unitDispatchCount: Object.fromEntries(this.unitDispatchCount),
        };
    }
}
