/**
 * @module        state
 * @description   运行时状态文件的原子读写与工作槽管理
 *
 * @author        eddy
 * @organization  wykj
 * @ownership     wykj/eddy
 *
 * @created       2026-03-19
 * @updated       2026-04-03
 *
 * @role          state
 * @layer         core
 * @boundedContext runtime
 */
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
import { dirname } from 'node:path';
export function createIdleWorkerSlot() {
    return {
        status: 'idle',
        seq: null,
        branch: null,
        worktree: null,
        claimedAt: null,
        lastHeartbeat: null,
        mode: null,
        transport: null,
        agent: null,
        sessionId: null,
        runId: null,
        sessionState: null,
        remoteStatus: null,
        lastEventAt: null,
        pid: null,
        outputFile: null,
        exitCode: null,
        mergeRetries: 0,
        completedAt: null,
    };
}
function defaultState(maxWorkers) {
    const workers = {};
    for (let i = 1; i <= maxWorkers; i++) {
        workers[`worker-${i}`] = createIdleWorkerSlot();
    }
    return {
        version: 1,
        generation: 0,
        updatedAt: new Date().toISOString(),
        updatedBy: 'init',
        workers,
        activeCards: {},
        leases: {},
        worktreeEvidence: {},
        worktreeCleanup: [],
        sessions: {},
        integrationQueues: {},
        pendingPMActions: [],
    };
}
function reconcileState(raw, maxWorkers) {
    const workers = { ...(raw.workers || {}) };
    const activeCards = Object.fromEntries(Object.entries(raw.activeCards || {}).map(([seq, card]) => [
        seq,
        {
            ...card,
            retryCount: card.retryCount ?? 0,
        },
    ]));
    const leases = Object.fromEntries(Object.entries(raw.leases || {}).map(([seq, lease]) => [
        seq,
        {
            ...lease,
            retryCount: lease.retryCount ?? 0,
        },
    ]));
    // Grow legacy state files when MAX_CONCURRENT_WORKERS increases.
    // This lets projects scale from 1 -> N workers without deleting state.json.
    for (let i = 1; i <= maxWorkers; i++) {
        const slotName = `worker-${i}`;
        if (!workers[slotName]) {
            workers[slotName] = createIdleWorkerSlot();
            continue;
        }
        workers[slotName] = {
            ...createIdleWorkerSlot(),
            ...workers[slotName],
        };
    }
    return {
        version: raw.version ?? 1,
        generation: raw.generation ?? 0,
        updatedAt: raw.updatedAt || new Date().toISOString(),
        updatedBy: raw.updatedBy || 'migrate',
        workers,
        activeCards,
        leases,
        worktreeEvidence: raw.worktreeEvidence || {},
        worktreeCleanup: raw.worktreeCleanup || [],
        sessions: raw.sessions || {},
        integrationQueues: raw.integrationQueues || {},
        pendingPMActions: raw.pendingPMActions || [],
    };
}
export function readState(stateFile, maxWorkers) {
    if (!existsSync(stateFile)) {
        return defaultState(maxWorkers);
    }
    try {
        const raw = readFileSync(stateFile, 'utf-8');
        return reconcileState(JSON.parse(raw), maxWorkers);
    }
    catch {
        return defaultState(maxWorkers);
    }
}
/**
 * Atomic write: write to temp file, then rename.
 * Increments generation automatically.
 */
export function writeState(stateFile, state, updatedBy) {
    const dir = dirname(stateFile);
    if (!existsSync(dir))
        mkdirSync(dir, { recursive: true });
    state.generation += 1;
    state.updatedAt = new Date().toISOString();
    state.updatedBy = updatedBy;
    const tmpFile = `${stateFile}.${process.pid}.${Date.now()}.tmp`;
    writeFileSync(tmpFile, JSON.stringify(state, null, 2) + '\n');
    renameSync(tmpFile, stateFile);
}
//# sourceMappingURL=state.js.map