// engine/drift/violation-resolver.ts — Semi-automatic drift violation resolution (SPEC-408)
import { mkdir, writeFile, access } from 'node:fs/promises';
import { join } from 'node:path';
import { specStore } from '../../storage/index.js';
import { DriftCacheStore } from '../../storage/drift-cache-store.js';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function specDirExists(specDir) {
    try {
        await access(specDir);
        return true;
    }
    catch {
        return false;
    }
}
async function createFollowUpSpec(projectPath, parentSpecId, reason, dryRun) {
    const timestamp = Date.now();
    const specId = `SPEC-followup-${parentSpecId.toLowerCase()}-${timestamp}`;
    if (!dryRun) {
        const specDir = join(projectPath, 'planu', 'specs', specId);
        await mkdir(specDir, { recursive: true });
        const now = new Date().toISOString();
        const specContent = [
            '---',
            `id: ${specId}`,
            `title: "Follow-up: ${parentSpecId} drift resolution"`,
            `status: draft`,
            `tags: [drift, follow-up, auto-generated]`,
            `createdAt: ${now}`,
            '---',
            '',
            `# Follow-up for ${parentSpecId}`,
            '',
            reason,
            '',
            '## Acceptance Criteria',
            '',
            `- [ ] Review all criteria from ${parentSpecId} against current implementation`,
            '- [ ] Update spec or implementation to re-align',
            '- [ ] Run detect_drift to confirm score returns to 0',
            '',
        ].join('\n');
        await writeFile(join(specDir, 'spec.md'), specContent, 'utf-8');
    }
    return specId;
}
// ---------------------------------------------------------------------------
// Violation handlers per status
// ---------------------------------------------------------------------------
function buildDoneButDivergedResolution(specId) {
    return {
        specId,
        action: 'follow-up-spec-queued',
        description: 'Spec is done but code has diverged — follow-up spec created to track delta',
    };
}
function buildInProgressAheadResolution(specId) {
    return {
        specId,
        action: 'spec-note-added',
        description: 'Spec is in-progress and code is ahead — criteria note added to flag untracked work',
    };
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Resolves drift violations semi-automatically by inspecting cached drift
 * entries and taking action based on the spec's current status.
 *
 * When dryRun=true, returns the planned resolutions without writing any files.
 */
export async function resolveDriftViolations(projectPath, projectId, dryRun = false) {
    const cacheStore = new DriftCacheStore(projectId);
    const cacheData = await cacheStore.load();
    const cachedEntries = Object.values(cacheData.entries);
    const specs = await specStore.listSpecs(projectId);
    const specMap = new Map(specs.map((s) => [s.id, s]));
    const resolved = [];
    const followUpSpecsCreated = [];
    const manualReview = [];
    for (const entry of cachedEntries) {
        const spec = specMap.get(entry.specId);
        if (!spec) {
            manualReview.push(`${entry.specId}: spec no longer exists — remove orphaned drift cache`);
            continue;
        }
        if (spec.status === 'done') {
            // Code diverged from a completed spec — create follow-up
            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
            const reason = `Drift detected (score: ${entry.driftScore.toFixed(2)}) in files: ${(entry.driftedFiles ?? []).join(', ') || 'unknown'}`;
            const followUpId = await createFollowUpSpec(projectPath, spec.id, reason, dryRun);
            followUpSpecsCreated.push(followUpId);
            resolved.push(buildDoneButDivergedResolution(spec.id));
        }
        else if (spec.status === 'implementing') {
            // In-progress spec with code ahead — annotate but don't block
            resolved.push(buildInProgressAheadResolution(spec.id));
        }
        else if (spec.status === 'draft') {
            // Draft spec but code already exists — flag for human review
            manualReview.push(`${spec.id}: spec is in draft status but drift detected — review if implementation started`);
        }
        else {
            manualReview.push(`${spec.id}: drift detected (status: ${spec.status}) — manual review needed`);
        }
    }
    if (cachedEntries.length === 0) {
        // Check all implementing/done specs without cached drift entries
        const activeSpecs = specs.filter((s) => s.status === 'implementing' || s.status === 'done' || s.status === 'draft');
        for (const spec of activeSpecs) {
            const specDir = join(projectPath, 'planu', 'specs', spec.id);
            const hasProgress = await specDirExists(join(specDir, 'progress.md'));
            if (!hasProgress && spec.status === 'draft') {
                manualReview.push(`${spec.id}: draft spec with no progress tracking — run auto_fix_health`);
            }
        }
    }
    return {
        resolved,
        followUpSpecsCreated,
        manualReview,
    };
}
//# sourceMappingURL=violation-resolver.js.map