/**
 * CAE persistence helpers against unified kit SQLite (ADR-cae-persistence-v1).
 */
import fs from "node:fs";
import path from "node:path";
import Database from "better-sqlite3";
import { planningSqliteDatabaseRelativePath } from "../../../modules/task-engine/planning-config.js";
import { prepareKitSqliteDatabase } from "../../state/kit-sqlite/planning-sqlite-kernel.js";
function kitDbPath(workspacePath, effective) {
    const rel = planningSqliteDatabaseRelativePath({
        workspacePath,
        effectiveConfig: effective
    });
    return path.join(workspacePath, rel);
}
export function openKitSqliteReadWrite(workspacePath, effective) {
    const abs = kitDbPath(workspacePath, effective);
    if (!fs.existsSync(abs)) {
        return null;
    }
    const db = new Database(abs);
    prepareKitSqliteDatabase(db);
    return db;
}
/** When persistence is on, write trace+bundle to kit SQLite (no-op if DB missing). */
export function persistCaeTraceIfEnabled(workspacePath, effective, persistenceEnabled, traceId, trace, bundle, summary) {
    if (!persistenceEnabled)
        return;
    const db = openKitSqliteReadWrite(workspacePath, effective);
    if (!db)
        return;
    try {
        persistCaeTraceSnapshot(db, traceId, trace, bundle, summary);
    }
    finally {
        db.close();
    }
}
export function persistCaeTraceSnapshot(db, traceId, trace, bundle, summary) {
    const now = new Date().toISOString();
    const traceJson = JSON.stringify(trace);
    const bundleJson = JSON.stringify(bundle);
    const summaryJson = summary
        ? JSON.stringify({ ...summary, traceId, createdAt: now, storage: "sqlite" })
        : null;
    db.prepare(`INSERT OR REPLACE INTO cae_trace_snapshots (trace_id, trace_json, bundle_json, summary_json, created_at) VALUES (?, ?, ?, ?, ?)`).run(traceId, traceJson, bundleJson, summaryJson, now);
    pruneCaeTraceSnapshots(db, 2000);
}
export function loadCaeTraceSnapshot(db, traceId) {
    const row = db
        .prepare(`SELECT trace_json, bundle_json FROM cae_trace_snapshots WHERE trace_id = ?`)
        .get(traceId);
    if (!row)
        return null;
    try {
        return {
            trace: JSON.parse(row.trace_json),
            bundle: JSON.parse(row.bundle_json)
        };
    }
    catch {
        return null;
    }
}
export function listCaeTraceSnapshotSummaries(db, filters) {
    const limit = Math.min(200, Math.max(1, Math.floor(filters?.limit ?? 25)));
    try {
        const rows = db
            .prepare(`SELECT trace_id, trace_json, bundle_json, summary_json, created_at
         FROM cae_trace_snapshots
         ORDER BY created_at DESC, trace_id ASC
         LIMIT ?`)
            .all(limit);
        const out = [];
        for (const row of rows) {
            try {
                out.push({
                    traceId: row.trace_id,
                    createdAt: row.created_at,
                    trace: JSON.parse(row.trace_json),
                    bundle: JSON.parse(row.bundle_json),
                    summary: typeof row.summary_json === "string" && row.summary_json.length > 0
                        ? JSON.parse(row.summary_json)
                        : null
                });
            }
            catch {
                // Skip corrupt rows; callers surface registry / DB health separately.
            }
        }
        return out;
    }
    catch {
        return [];
    }
}
export function pruneCaeTraceSnapshots(db, maxRows) {
    const n = db.prepare(`SELECT COUNT(*) AS c FROM cae_trace_snapshots`).get();
    const excess = Number(n.c) - maxRows;
    if (excess <= 0)
        return;
    db.prepare(`DELETE FROM cae_trace_snapshots WHERE trace_id IN (
       SELECT trace_id FROM cae_trace_snapshots ORDER BY created_at ASC LIMIT ?
     )`).run(excess);
}
export function countCaeTraceRows(db) {
    try {
        const row = db.prepare(`SELECT COUNT(*) AS c FROM cae_trace_snapshots`).get();
        return Number(row.c) || 0;
    }
    catch {
        return 0;
    }
}
export function countCaeAckRows(db) {
    try {
        const row = db.prepare(`SELECT COUNT(*) AS c FROM cae_ack_satisfaction`).get();
        return Number(row.c) || 0;
    }
    catch {
        return 0;
    }
}
export function listCaeAckSatisfactions(db, filters) {
    const where = [];
    const params = [];
    const traceId = filters?.traceId?.trim();
    const activationId = filters?.activationId?.trim();
    if (traceId) {
        where.push("trace_id = ?");
        params.push(traceId);
    }
    if (activationId) {
        where.push("activation_id = ?");
        params.push(activationId);
    }
    const limit = Math.min(200, Math.max(1, Math.floor(filters?.limit ?? 50)));
    const whereSql = where.length ? `WHERE ${where.join(" AND ")}` : "";
    try {
        const rows = db
            .prepare(`SELECT id, trace_id, ack_token, activation_id, satisfied_at, actor
         FROM cae_ack_satisfaction
         ${whereSql}
         ORDER BY satisfied_at DESC, id DESC
         LIMIT ?`)
            .all(...params, limit);
        return rows.map((r) => ({
            id: r.id,
            traceId: r.trace_id,
            ackToken: r.ack_token,
            activationId: r.activation_id,
            satisfiedAt: r.satisfied_at,
            actor: r.actor
        }));
    }
    catch {
        return [];
    }
}
export function insertCaeAckSatisfaction(db, row) {
    const now = new Date().toISOString();
    db.prepare(`INSERT INTO cae_ack_satisfaction (trace_id, ack_token, activation_id, satisfied_at, actor) VALUES (?, ?, ?, ?, ?)`).run(row.traceId, row.ackToken, row.activationId, now, row.actor);
}
/** True when `cae_registry_versions` exists (post kit SQLite v12 migration). */
export function caeRegistryTablesReady(db) {
    try {
        db.prepare(`SELECT 1 FROM cae_registry_versions LIMIT 1`).get();
        return true;
    }
    catch {
        return false;
    }
}
/** Active version id, or null when none is marked active. */
export function getActiveCaeRegistryVersionId(db) {
    try {
        const row = db
            .prepare(`SELECT version_id FROM cae_registry_versions WHERE is_active = 1 ORDER BY created_at DESC LIMIT 1`)
            .get();
        return row?.version_id ?? null;
    }
    catch {
        return null;
    }
}
export function listCaeRegistryVersionIds(db) {
    try {
        const rows = db
            .prepare(`SELECT version_id FROM cae_registry_versions ORDER BY created_at DESC`)
            .all();
        return rows.map((r) => r.version_id);
    }
    catch {
        return [];
    }
}
/**
 * Insert a registry version row. When `setActive` is true, clears other active flags first
 * (at most one active version).
 */
export function insertCaeRegistryVersion(db, row) {
    const now = new Date().toISOString();
    const run = db.transaction(() => {
        if (row.setActive) {
            db.prepare(`UPDATE cae_registry_versions SET is_active = 0`).run();
        }
        db.prepare(`INSERT INTO cae_registry_versions (version_id, created_at, created_by, is_active, note) VALUES (?, ?, ?, ?, ?)`).run(row.versionId, now, row.createdBy, row.setActive ? 1 : 0, row.note ?? null);
    });
    run();
}
/** Mark `versionId` active; returns false if the version row does not exist. */
export function activateCaeRegistryVersion(db, versionId) {
    try {
        const exists = db.prepare(`SELECT 1 FROM cae_registry_versions WHERE version_id = ?`).get(versionId);
        if (!exists)
            return false;
        const run = db.transaction(() => {
            db.prepare(`UPDATE cae_registry_versions SET is_active = 0`).run();
            db.prepare(`UPDATE cae_registry_versions SET is_active = 1 WHERE version_id = ?`).run(versionId);
        });
        run();
        return true;
    }
    catch {
        return false;
    }
}
export function listCaeRegistryArtifactsForVersion(db, versionId) {
    try {
        return db
            .prepare(`SELECT version_id, artifact_id, artifact_type, path, title, description, metadata_json, retired_at
         FROM cae_registry_artifacts WHERE version_id = ? ORDER BY artifact_id ASC`)
            .all(versionId);
    }
    catch {
        return [];
    }
}
export function listCaeRegistryActivationsForVersion(db, versionId) {
    try {
        return db
            .prepare(`SELECT version_id, activation_id, family, priority, lifecycle_state, scope_json, artifact_refs_json,
                acknowledgement_json, metadata_json, retired_at
         FROM cae_registry_activations WHERE version_id = ? ORDER BY activation_id ASC`)
            .all(versionId);
    }
    catch {
        return [];
    }
}
export function insertCaeRegistryArtifactRow(db, row) {
    db.prepare(`INSERT INTO cae_registry_artifacts (
       version_id, artifact_id, artifact_type, path, title, description, metadata_json, retired_at
     ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(row.versionId, row.artifactId, row.artifactType, row.path, row.title ?? null, row.description ?? null, row.metadataJson ?? "{}", row.retiredAt ?? null);
}
export function insertCaeRegistryActivationRow(db, row) {
    db.prepare(`INSERT INTO cae_registry_activations (
       version_id, activation_id, family, priority, lifecycle_state, scope_json, artifact_refs_json,
       acknowledgement_json, metadata_json, retired_at
     ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.versionId, row.activationId, row.family, row.priority, row.lifecycleState, row.scopeJson, row.artifactRefsJson, row.acknowledgementJson ?? null, row.metadataJson ?? "{}", row.retiredAt ?? null);
}
/** Remove all artifact + activation rows for a version (keeps the version header row). */
export function clearCaeRegistryVersionContents(db, versionId) {
    const run = db.transaction(() => {
        db.prepare(`DELETE FROM cae_registry_activations WHERE version_id = ?`).run(versionId);
        db.prepare(`DELETE FROM cae_registry_artifacts WHERE version_id = ?`).run(versionId);
    });
    run();
}
/**
 * Delete a version row when it is not active (children removed by FK CASCADE).
 * Returns false when missing or still active.
 */
export function deleteInactiveCaeRegistryVersion(db, versionId) {
    const row = db
        .prepare(`SELECT is_active FROM cae_registry_versions WHERE version_id = ?`)
        .get(versionId);
    if (!row || row.is_active === 1)
        return false;
    db.prepare(`DELETE FROM cae_registry_versions WHERE version_id = ?`).run(versionId);
    return true;
}
export function getCaeRegistryVersionMeta(db, versionId) {
    try {
        const row = db
            .prepare(`SELECT version_id, created_at, created_by, is_active, note FROM cae_registry_versions WHERE version_id = ?`)
            .get(versionId);
        return row ?? null;
    }
    catch {
        return null;
    }
}
export function listCaeRegistryVersionsWithCounts(db) {
    try {
        return db
            .prepare(`SELECT v.version_id, v.created_at, v.created_by, v.is_active, v.note,
            (SELECT COUNT(*) FROM cae_registry_artifacts a WHERE a.version_id = v.version_id) AS artifact_count,
            (SELECT COUNT(*) FROM cae_registry_activations c WHERE c.version_id = v.version_id) AS activation_count
         FROM cae_registry_versions v
         ORDER BY v.created_at DESC`)
            .all();
    }
    catch {
        return [];
    }
}
/** Append-only CAE registry mutation audit (kit SQLite v13+). */
export function insertCaeRegistryMutationAudit(db, row) {
    const now = new Date().toISOString();
    const payloadJson = JSON.stringify(row.payload ?? {});
    try {
        const info = db
            .prepare(`INSERT INTO cae_registry_mutations (recorded_at, actor, command_name, version_id, note, payload_json)
         VALUES (?, ?, ?, ?, ?, ?)`)
            .run(now, row.actor, row.commandName, row.versionId, row.note ?? null, payloadJson);
        const id = Number(info.lastInsertRowid);
        return Number.isFinite(id) && id > 0 ? id : undefined;
    }
    catch {
        /* pre-v13 DB or missing table — skip audit rather than failing the mutation */
        return undefined;
    }
}
export function countCaeRegistryMutationAuditRows(db) {
    try {
        const row = db.prepare(`SELECT COUNT(*) AS c FROM cae_registry_mutations`).get();
        return Number(row.c) || 0;
    }
    catch {
        return 0;
    }
}
export function listCaeRegistryMutationAuditRows(db, options) {
    const limit = Number.isFinite(options?.limit) ? Math.max(1, Math.floor(options.limit)) : 25;
    const versionId = typeof options?.versionId === "string" && options.versionId.trim().length > 0
        ? options.versionId.trim()
        : null;
    try {
        if (versionId) {
            return db
                .prepare(`SELECT id, recorded_at, actor, command_name, version_id, note, payload_json
           FROM cae_registry_mutations
           WHERE version_id = ?
           ORDER BY recorded_at DESC, id DESC
           LIMIT ?`)
                .all(versionId, limit);
        }
        return db
            .prepare(`SELECT id, recorded_at, actor, command_name, version_id, note, payload_json
         FROM cae_registry_mutations
         ORDER BY recorded_at DESC, id DESC
         LIMIT ?`)
            .all(limit);
    }
    catch {
        return [];
    }
}
/** Named restore point for a registry version (kit SQLite v22+). */
export function insertCaeRegistryCheckpoint(db, row) {
    const now = new Date().toISOString();
    const idsJson = JSON.stringify([...row.mutationIds]);
    try {
        const info = db
            .prepare(`INSERT INTO cae_registry_checkpoints (recorded_at, label, actor, note, version_id, registry_digest, mutation_ids_json)
         VALUES (?, ?, ?, ?, ?, ?, ?)`)
            .run(now, row.label, row.actor ?? null, row.note ?? null, row.versionId, row.registryDigest, idsJson);
        const id = Number(info.lastInsertRowid);
        return Number.isFinite(id) && id > 0 ? id : undefined;
    }
    catch {
        return undefined;
    }
}
export function listCaeRegistryCheckpointsForVersion(db, versionId, options) {
    const limit = Number.isFinite(options?.limit) ? Math.max(1, Math.floor(options.limit)) : 50;
    const vid = versionId.trim();
    if (!vid.length) {
        return [];
    }
    try {
        return db
            .prepare(`SELECT id, recorded_at, label, actor, note, version_id, registry_digest, mutation_ids_json
         FROM cae_registry_checkpoints
         WHERE version_id = ?
         ORDER BY recorded_at DESC, id DESC
         LIMIT ?`)
            .all(vid, limit);
    }
    catch {
        return [];
    }
}
export function getCaeRegistryCheckpointById(db, checkpointId) {
    if (!Number.isFinite(checkpointId) || checkpointId <= 0) {
        return undefined;
    }
    try {
        const row = db
            .prepare(`SELECT id, recorded_at, label, actor, note, version_id, registry_digest, mutation_ids_json
         FROM cae_registry_checkpoints
         WHERE id = ?`)
            .get(checkpointId);
        return row;
    }
    catch {
        return undefined;
    }
}
export function updateCaeRegistryArtifactFields(db, versionId, artifactId, patch) {
    const fields = [];
    const vals = [];
    if (patch.artifactType !== undefined) {
        fields.push("artifact_type = ?");
        vals.push(patch.artifactType);
    }
    if (patch.path !== undefined) {
        fields.push("path = ?");
        vals.push(patch.path);
    }
    if (patch.title !== undefined) {
        fields.push("title = ?");
        vals.push(patch.title);
    }
    if (patch.description !== undefined) {
        fields.push("description = ?");
        vals.push(patch.description);
    }
    if (patch.metadataJson !== undefined) {
        fields.push("metadata_json = ?");
        vals.push(patch.metadataJson);
    }
    if (!fields.length)
        return false;
    vals.push(versionId, artifactId);
    const sql = `UPDATE cae_registry_artifacts SET ${fields.join(", ")} WHERE version_id = ? AND artifact_id = ?`;
    const res = db.prepare(sql).run(...vals);
    return res.changes > 0;
}
export function retireCaeRegistryArtifact(db, versionId, artifactId, retiredAt) {
    const ts = retiredAt ?? new Date().toISOString();
    const res = db
        .prepare(`UPDATE cae_registry_artifacts SET retired_at = ? WHERE version_id = ? AND artifact_id = ? AND retired_at IS NULL`)
        .run(ts, versionId, artifactId);
    return res.changes > 0;
}
export function updateCaeRegistryActivationFields(db, versionId, activationId, patch) {
    const fields = [];
    const vals = [];
    if (patch.family !== undefined) {
        fields.push("family = ?");
        vals.push(patch.family);
    }
    if (patch.priority !== undefined) {
        fields.push("priority = ?");
        vals.push(patch.priority);
    }
    if (patch.lifecycleState !== undefined) {
        fields.push("lifecycle_state = ?");
        vals.push(patch.lifecycleState);
    }
    if (patch.scopeJson !== undefined) {
        fields.push("scope_json = ?");
        vals.push(patch.scopeJson);
    }
    if (patch.artifactRefsJson !== undefined) {
        fields.push("artifact_refs_json = ?");
        vals.push(patch.artifactRefsJson);
    }
    if (patch.acknowledgementJson !== undefined) {
        fields.push("acknowledgement_json = ?");
        vals.push(patch.acknowledgementJson);
    }
    if (patch.metadataJson !== undefined) {
        fields.push("metadata_json = ?");
        vals.push(patch.metadataJson);
    }
    if (!fields.length)
        return false;
    vals.push(versionId, activationId);
    const sql = `UPDATE cae_registry_activations SET ${fields.join(", ")} WHERE version_id = ? AND activation_id = ?`;
    const res = db.prepare(sql).run(...vals);
    return res.changes > 0;
}
export function retireCaeRegistryActivation(db, versionId, activationId, retiredAt) {
    const ts = retiredAt ?? new Date().toISOString();
    const res = db
        .prepare(`UPDATE cae_registry_activations SET retired_at = ? WHERE version_id = ? AND activation_id = ? AND retired_at IS NULL`)
        .run(ts, versionId, activationId);
    return res.changes > 0;
}
/**
 * Copy artifact + activation rows from one version to another (used by clone).
 * Target version must already exist as a header row with no children (or caller cleared it).
 */
export function copyCaeRegistryVersionContents(db, fromVersionId, toVersionId) {
    const arts = listCaeRegistryArtifactsForVersion(db, fromVersionId);
    for (const r of arts) {
        insertCaeRegistryArtifactRow(db, {
            versionId: toVersionId,
            artifactId: r.artifact_id,
            artifactType: r.artifact_type,
            path: r.path,
            title: r.title,
            description: r.description,
            metadataJson: r.metadata_json,
            retiredAt: r.retired_at
        });
    }
    const acts = listCaeRegistryActivationsForVersion(db, fromVersionId);
    for (const r of acts) {
        insertCaeRegistryActivationRow(db, {
            versionId: toVersionId,
            activationId: r.activation_id,
            family: r.family,
            priority: r.priority,
            lifecycleState: r.lifecycle_state,
            scopeJson: r.scope_json,
            artifactRefsJson: r.artifact_refs_json,
            acknowledgementJson: r.acknowledgement_json,
            metadataJson: r.metadata_json,
            retiredAt: r.retired_at
        });
    }
}
