import { _getAdapter, transaction, } from "./nerf-db.js";
import { atomicWriteSync } from "./atomic-write.js";
import { readFileSync, existsSync, mkdirSync } from "node:fs";
import { join } from "node:path";
// ─── helpers ─────────────────────────────────────────────────────────────
function requireDb() {
    const db = _getAdapter();
    if (!db)
        throw new Error("workflow-manifest: No database open");
    return db;
}
// ─── snapshotState ───────────────────────────────────────────────────────
/**
 * Capture complete DB state as a StateManifest.
 * Reads all rows from milestones, slices, tasks, decisions, verification_evidence.
 *
 * Note: rows returned from raw queries are plain objects with TEXT columns for
 * JSON arrays. We parse them into typed Row objects using the same logic as
 * nerf-db helper functions.
 */
export function snapshotState() {
    const db = requireDb();
    // Wrap all reads in a deferred transaction so the snapshot is consistent
    // (all SELECTs see the same DB state even if a concurrent write lands between them).
    db.exec("BEGIN DEFERRED");
    try {
        const rawMilestones = db.prepare("SELECT * FROM milestones ORDER BY id").all();
        const milestones = rawMilestones.map((r) => ({
            id: r["id"],
            title: r["title"],
            status: r["status"],
            depends_on: JSON.parse(r["depends_on"] || "[]"),
            created_at: r["created_at"],
            completed_at: r["completed_at"] ?? null,
            vision: r["vision"] ?? "",
            success_criteria: JSON.parse(r["success_criteria"] || "[]"),
            key_risks: JSON.parse(r["key_risks"] || "[]"),
            proof_strategy: JSON.parse(r["proof_strategy"] || "[]"),
            verification_contract: r["verification_contract"] ?? "",
            verification_integration: r["verification_integration"] ?? "",
            verification_operational: r["verification_operational"] ?? "",
            verification_uat: r["verification_uat"] ?? "",
            definition_of_done: JSON.parse(r["definition_of_done"] || "[]"),
            requirement_coverage: r["requirement_coverage"] ?? "",
            boundary_map_markdown: r["boundary_map_markdown"] ?? "",
        }));
        const rawSlices = db.prepare("SELECT * FROM slices ORDER BY milestone_id, sequence, id").all();
        const slices = rawSlices.map((r) => ({
            milestone_id: r["milestone_id"],
            id: r["id"],
            title: r["title"],
            status: r["status"],
            risk: r["risk"],
            depends: JSON.parse(r["depends"] || "[]"),
            demo: r["demo"] ?? "",
            created_at: r["created_at"],
            completed_at: r["completed_at"] ?? null,
            full_summary_md: r["full_summary_md"] ?? "",
            full_uat_md: r["full_uat_md"] ?? "",
            goal: r["goal"] ?? "",
            success_criteria: r["success_criteria"] ?? "",
            proof_level: r["proof_level"] ?? "",
            integration_closure: r["integration_closure"] ?? "",
            observability_impact: r["observability_impact"] ?? "",
            sequence: r["sequence"] ?? 0,
            replan_triggered_at: r["replan_triggered_at"] ?? null,
        }));
        const rawTasks = db.prepare("SELECT * FROM tasks ORDER BY milestone_id, slice_id, sequence, id").all();
        const tasks = rawTasks.map((r) => ({
            milestone_id: r["milestone_id"],
            slice_id: r["slice_id"],
            id: r["id"],
            title: r["title"],
            status: r["status"],
            one_liner: r["one_liner"] ?? "",
            narrative: r["narrative"] ?? "",
            verification_result: r["verification_result"] ?? "",
            duration: r["duration"] ?? "",
            completed_at: r["completed_at"] ?? null,
            blocker_discovered: r["blocker_discovered"] === 1,
            deviations: r["deviations"] ?? "",
            known_issues: r["known_issues"] ?? "",
            key_files: JSON.parse(r["key_files"] || "[]"),
            key_decisions: JSON.parse(r["key_decisions"] || "[]"),
            full_summary_md: r["full_summary_md"] ?? "",
            description: r["description"] ?? "",
            estimate: r["estimate"] ?? "",
            files: JSON.parse(r["files"] || "[]"),
            verify: r["verify"] ?? "",
            inputs: JSON.parse(r["inputs"] || "[]"),
            expected_output: JSON.parse(r["expected_output"] || "[]"),
            observability_impact: r["observability_impact"] ?? "",
            full_plan_md: r["full_plan_md"] ?? "",
            sequence: r["sequence"] ?? 0,
        }));
        const rawDecisions = db.prepare("SELECT * FROM decisions ORDER BY seq").all();
        const decisions = rawDecisions.map((r) => ({
            seq: r["seq"],
            id: r["id"],
            when_context: r["when_context"] ?? "",
            scope: r["scope"] ?? "",
            decision: r["decision"] ?? "",
            choice: r["choice"] ?? "",
            rationale: r["rationale"] ?? "",
            revisable: r["revisable"] ?? "",
            made_by: r["made_by"] ?? "agent",
            superseded_by: r["superseded_by"] ?? null,
        }));
        const rawEvidence = db.prepare("SELECT * FROM verification_evidence ORDER BY id").all();
        const verification_evidence = rawEvidence.map((r) => ({
            id: r["id"],
            task_id: r["task_id"],
            slice_id: r["slice_id"],
            milestone_id: r["milestone_id"],
            command: r["command"],
            exit_code: r["exit_code"] ?? null,
            verdict: r["verdict"] ?? "",
            duration_ms: r["duration_ms"] ?? null,
            created_at: r["created_at"],
        }));
        const result = {
            version: 1,
            exported_at: new Date().toISOString(),
            milestones,
            slices,
            tasks,
            decisions,
            verification_evidence,
        };
        db.exec("COMMIT");
        return result;
    }
    catch (err) {
        try {
            db.exec("ROLLBACK");
        }
        catch { /* ignore rollback failure */ }
        throw err;
    }
}
// ─── restore ─────────────────────────────────────────────────────────────
/**
 * Atomically replace all workflow state from a manifest.
 * Runs inside a transaction — if any insert fails, no tables are modified.
 * Only touches engine tables + decisions. Does NOT modify artifacts or memories.
 */
function restore(manifest) {
    const db = requireDb();
    transaction(() => {
        // Clear engine tables (order matters for foreign-key-like consistency)
        db.exec("DELETE FROM verification_evidence");
        db.exec("DELETE FROM tasks");
        db.exec("DELETE FROM slices");
        db.exec("DELETE FROM milestones");
        db.exec("DELETE FROM decisions WHERE 1=1");
        // Restore milestones
        const msStmt = db.prepare(`INSERT INTO milestones (id, title, status, depends_on, created_at, completed_at,
        vision, success_criteria, key_risks, proof_strategy,
        verification_contract, verification_integration, verification_operational, verification_uat,
        definition_of_done, requirement_coverage, boundary_map_markdown)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
        for (const m of manifest.milestones) {
            msStmt.run(m.id, m.title, m.status, JSON.stringify(m.depends_on), m.created_at, m.completed_at, m.vision, JSON.stringify(m.success_criteria), JSON.stringify(m.key_risks), JSON.stringify(m.proof_strategy), m.verification_contract, m.verification_integration, m.verification_operational, m.verification_uat, JSON.stringify(m.definition_of_done), m.requirement_coverage, m.boundary_map_markdown);
        }
        // Restore slices
        const slStmt = db.prepare(`INSERT INTO slices (milestone_id, id, title, status, risk, depends, demo,
        created_at, completed_at, full_summary_md, full_uat_md,
        goal, success_criteria, proof_level, integration_closure, observability_impact,
        sequence, replan_triggered_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
        for (const s of manifest.slices) {
            slStmt.run(s.milestone_id, s.id, s.title, s.status, s.risk, JSON.stringify(s.depends), s.demo, s.created_at, s.completed_at, s.full_summary_md, s.full_uat_md, s.goal, s.success_criteria, s.proof_level, s.integration_closure, s.observability_impact, s.sequence, s.replan_triggered_at);
        }
        // Restore tasks
        const tkStmt = db.prepare(`INSERT INTO tasks (milestone_id, slice_id, id, title, status,
        one_liner, narrative, verification_result, duration, completed_at,
        blocker_discovered, deviations, known_issues, key_files, key_decisions,
        full_summary_md, description, estimate, files, verify,
        inputs, expected_output, observability_impact, sequence)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
        for (const t of manifest.tasks) {
            tkStmt.run(t.milestone_id, t.slice_id, t.id, t.title, t.status, t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at, t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues, JSON.stringify(t.key_files), JSON.stringify(t.key_decisions), t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify, JSON.stringify(t.inputs), JSON.stringify(t.expected_output), t.observability_impact, t.sequence);
        }
        // Restore decisions
        const dcStmt = db.prepare(`INSERT INTO decisions (seq, id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
        for (const d of manifest.decisions) {
            dcStmt.run(d.seq, d.id, d.when_context, d.scope, d.decision, d.choice, d.rationale, d.revisable, d.made_by, d.superseded_by);
        }
        // Restore verification evidence
        const evStmt = db.prepare(`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
        for (const e of manifest.verification_evidence) {
            evStmt.run(e.task_id, e.slice_id, e.milestone_id, e.command, e.exit_code, e.verdict, e.duration_ms, e.created_at);
        }
    });
}
// ─── writeManifest ───────────────────────────────────────────────────────
/**
 * Write current DB state to .nerf/state-manifest.json via atomicWriteSync.
 * Uses JSON.stringify with 2-space indent for git three-way merge friendliness.
 */
export function writeManifest(basePath) {
    const manifest = snapshotState();
    const json = JSON.stringify(manifest, null, 2);
    const dir = join(basePath, ".nerf");
    mkdirSync(dir, { recursive: true });
    atomicWriteSync(join(dir, "state-manifest.json"), json);
}
// ─── readManifest ────────────────────────────────────────────────────────
/**
 * Read state-manifest.json and return parsed manifest, or null if not found.
 */
export function readManifest(basePath) {
    const manifestPath = join(basePath, ".nerf", "state-manifest.json");
    if (!existsSync(manifestPath)) {
        return null;
    }
    const raw = readFileSync(manifestPath, "utf-8");
    const parsed = JSON.parse(raw);
    if (parsed.version !== 1) {
        throw new Error(`Unsupported manifest version: ${parsed.version}`);
    }
    // Validate required fields to avoid cryptic errors during restore
    if (!Array.isArray(parsed.milestones) || !Array.isArray(parsed.slices) ||
        !Array.isArray(parsed.tasks) || !Array.isArray(parsed.decisions) ||
        !Array.isArray(parsed.verification_evidence)) {
        throw new Error("Malformed manifest: missing or invalid required arrays");
    }
    return parsed;
}
// ─── bootstrapFromManifest ──────────────────────────────────────────────
/**
 * Read state-manifest.json and restore DB state from it.
 * Returns true if bootstrap succeeded, false if manifest file doesn't exist.
 */
export function bootstrapFromManifest(basePath) {
    const manifest = readManifest(basePath);
    if (!manifest) {
        return false;
    }
    restore(manifest);
    return true;
}
