// tools/auto-reconcile.ts — Auto-reconciliation for safe spec changes
// Runs without manual approval for safe changes (completion %, status flip, hash update).
// Rate-limited: max 1 auto-reconcile per spec per 5 minutes.
import { specStore, knowledgeStore, hashSnapshotStore } from '../storage/index.js';
import { buildAutoReconcileResult, createLogEntry } from '../engine/living-spec/index.js';
import { analyzeLivingSpec } from '../engine/living-spec-analyzer.js';
import { compactResult } from './output-formatter.js';
const lastReconcileMap = new Map();
const RECONCILE_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes
function isReconcileRateLimited(specId) {
    const last = lastReconcileMap.get(specId);
    if (last !== undefined && Date.now() - last < RECONCILE_COOLDOWN_MS) {
        return true;
    }
    return false;
}
export async function handleAutoReconcile(args) {
    const { projectId, specId, changedFiles } = args;
    // If specId provided, reconcile that spec only
    // If changedFiles provided, find specs that reference those files
    const specIds = specId ? [specId] : await findAffectedSpecs(projectId, changedFiles ?? []);
    if (specIds.length === 0) {
        return compactResult('No specs affected by changed files.\n**results**: 0');
    }
    const results = [];
    for (const sid of specIds) {
        const result = await reconcileSingleSpec(projectId, sid);
        results.push(result);
        // Save reconcile log
        if (result.appliedChanges.length > 0) {
            await hashSnapshotStore.appendReconcileLog(projectId, sid, result.appliedChanges);
        }
    }
    const summary = results
        .map((r) => `- **${r.specId}**: ${r.appliedChanges.length} change(s) applied${r.appliedChanges.length > 0 ? '' : ' (no changes)'}`)
        .join('\n');
    return compactResult(`Auto-reconciled ${String(results.length)} spec(s).\n\n${summary}`);
}
async function reconcileSingleSpec(projectId, specId) {
    if (isReconcileRateLimited(specId)) {
        return buildAutoReconcileResult(specId, []);
    }
    lastReconcileMap.set(specId, Date.now());
    const changes = [];
    const spec = await specStore.getSpec(projectId, specId);
    if (!spec) {
        return buildAutoReconcileResult(specId, []);
    }
    // Run living-spec analysis for completion update
    const knowledge = await knowledgeStore.getKnowledge(projectId);
    if (knowledge?.projectPath) {
        const report = await analyzeLivingSpec(spec, knowledge.projectPath);
        // Completion percentage update is a safe change
        changes.push(createLogEntry(specId, 'completion-update', `Completion updated to ${String(report.completionPercent)}%`, 'unknown', `${String(report.completionPercent)}%`));
        // Timestamp update is safe
        changes.push(createLogEntry(specId, 'timestamp-update', 'Updated spec analysis timestamp', spec.updatedAt, new Date().toISOString()));
    }
    return buildAutoReconcileResult(specId, changes);
}
async function findAffectedSpecs(projectId, changedFiles) {
    if (changedFiles.length === 0) {
        return [];
    }
    // Look up snapshots that reference any of the changed files
    const allSpecs = await specStore.listSpecs(projectId);
    const affected = [];
    for (const spec of allSpecs) {
        const snapshot = await hashSnapshotStore.getSnapshot(projectId, spec.id);
        if (!snapshot) {
            continue;
        }
        const snapshotFiles = new Set(snapshot.files.map((f) => f.file));
        const hasOverlap = changedFiles.some((f) => snapshotFiles.has(f));
        if (hasOverlap) {
            affected.push(spec.id);
        }
    }
    return affected;
}
//# sourceMappingURL=auto-reconcile.js.map