/**
 * engine/living-spec/bidirectional-sync.ts — AC-9: Bidirectional sync
 *
 * Pure functions for bidirectional synchronization between specs and code:
 * - TODO comment generation (spec-to-code): surface pending criteria as code annotations
 * - Progress updates (code-to-spec): promote criteria status when annotations are found
 * - Conflict detection: flag criteria with annotations whose spec status is "drift" or "regression"
 * - Sync log entries: structured audit trail for each sync operation
 * - Result builders: assemble SyncResult objects for spec-to-code and code-to-spec directions
 */
import { createHash } from 'node:crypto';
// ---------------------------------------------------------------------------
// TODO comment generation (spec-to-code)
// Surfaces pending criteria as inline code annotations for developer visibility.
// ---------------------------------------------------------------------------
/**
 * Generate TODO comments for pending criteria that lack annotations in code.
 */
export function generateTodoComments(pendingCriteria, existingAnnotations, defaultFile) {
    const annotatedSet = new Set();
    for (const ann of existingAnnotations) {
        for (const c of ann.criteria) {
            annotatedSet.add(`${ann.specId}:AC-${c}`);
        }
    }
    const todos = [];
    for (const criterion of pendingCriteria) {
        const key = `${criterion.specId}:AC-${criterion.criterion}`;
        if (annotatedSet.has(key)) {
            continue;
        }
        todos.push({
            file: defaultFile,
            line: 1,
            text: `TODO: @spec ${criterion.specId} AC-${criterion.criterion} — ${criterion.description}`,
            specId: criterion.specId,
            criterion: criterion.criterion,
        });
    }
    return todos;
}
// ---------------------------------------------------------------------------
// Progress updates (code-to-spec)
// ---------------------------------------------------------------------------
/**
 * Generate progress updates based on annotations found in code.
 */
export function generateProgressUpdates(annotations, criteriaStatuses) {
    const updates = [];
    const seen = new Set();
    for (const ann of annotations) {
        for (const criterion of ann.criteria) {
            const key = `${ann.specId}:AC-${criterion}`;
            if (seen.has(key)) {
                continue;
            }
            seen.add(key);
            const currentStatus = criteriaStatuses.get(key) ?? 'pending';
            if (currentStatus === 'pending') {
                updates.push({
                    specId: ann.specId,
                    criterion,
                    previousStatus: 'pending',
                    newStatus: 'met',
                    reason: `Annotation found in ${ann.file}:${ann.line}`,
                });
            }
        }
    }
    return updates;
}
// ---------------------------------------------------------------------------
// Conflict detection
// ---------------------------------------------------------------------------
/**
 * Detect conflicts between code state and spec state.
 */
export function detectConflicts(annotations, criteriaStatuses, direction) {
    const conflicts = [];
    const seen = new Set();
    for (const ann of annotations) {
        for (const criterion of ann.criteria) {
            const key = `${ann.specId}:AC-${criterion}`;
            if (seen.has(key)) {
                continue;
            }
            seen.add(key);
            const specStatus = criteriaStatuses.get(key) ?? 'pending';
            if (specStatus === 'drift' || specStatus === 'regression') {
                const id = createHash('sha256')
                    .update(`${key}:${direction}:${Date.now()}`)
                    .digest('hex')
                    .slice(0, 12);
                conflicts.push({
                    id,
                    specId: ann.specId,
                    criterion,
                    direction,
                    description: `Criterion AC-${criterion} has annotation in code but spec status is "${specStatus}"`,
                    codeState: `annotated in ${ann.file}:${ann.line}`,
                    specState: specStatus,
                    detectedAt: new Date().toISOString(),
                    resolved: false,
                });
            }
        }
    }
    return conflicts;
}
// ---------------------------------------------------------------------------
// Sync log entries
// ---------------------------------------------------------------------------
/**
 * Create a sync log entry.
 */
export function createSyncLogEntry(direction, specId, action, details) {
    return {
        timestamp: new Date().toISOString(),
        direction,
        specId,
        action,
        details,
    };
}
// ---------------------------------------------------------------------------
// Result builders
// ---------------------------------------------------------------------------
/**
 * Build a spec-to-code sync result.
 */
export function buildSpecToCodeResult(specId, todos, conflicts) {
    const logEntries = [
        createSyncLogEntry('spec-to-code', specId, 'generate-todos', `Generated ${todos.length} TODO comment(s)`),
    ];
    if (conflicts.length > 0) {
        logEntries.push(createSyncLogEntry('spec-to-code', specId, 'detect-conflicts', `Found ${conflicts.length} conflict(s)`));
    }
    return {
        direction: 'spec-to-code',
        specId,
        todosGenerated: todos,
        progressUpdates: [],
        conflicts,
        logEntries,
        syncedAt: new Date().toISOString(),
    };
}
/**
 * Build a code-to-spec sync result.
 */
export function buildCodeToSpecResult(specId, updates, conflicts) {
    const logEntries = [
        createSyncLogEntry('code-to-spec', specId, 'update-progress', `Generated ${updates.length} progress update(s)`),
    ];
    if (conflicts.length > 0) {
        logEntries.push(createSyncLogEntry('code-to-spec', specId, 'detect-conflicts', `Found ${conflicts.length} conflict(s)`));
    }
    return {
        direction: 'code-to-spec',
        specId,
        todosGenerated: [],
        progressUpdates: updates,
        conflicts,
        logEntries,
        syncedAt: new Date().toISOString(),
    };
}
//# sourceMappingURL=bidirectional-sync.js.map