import fs from "node:fs";
import path from "node:path";
import { readTasksCanonicalAuthority } from "./persistence/task-state-canonical-authority.js";
import { planningSqliteDatabaseRelativePath } from "./planning-config.js";
import { readTaskStateEventLogJsonl } from "./task-state-events/task-state-event-log-io.js";
import { replayTaskStateEvents } from "./task-state-events/event-applier.js";
import { admitTaskStateEventStream } from "./task-state-events/event-admission.js";
import { readTaskStateProjectionMeta, taskStateProjectionMetaTableAvailable } from "./persistence/task-state-projection-meta-store.js";
function statusMap(tasks) {
    return new Map(tasks.map((t) => [t.id, t.status]));
}
function maxRawSequence(events) {
    let max = 0;
    for (const event of events) {
        if (event !== null &&
            typeof event === "object" &&
            !Array.isArray(event) &&
            typeof event.sequence === "number" &&
            event.sequence > max) {
            max = event.sequence;
        }
    }
    return max;
}
function projectionFreshThroughLog(db, rawEvents) {
    if (!taskStateProjectionMetaTableAvailable(db)) {
        return false;
    }
    const meta = readTaskStateProjectionMeta(db);
    return Boolean(meta &&
        meta.syncStatus === "fresh" &&
        meta.appliedSequence >= maxRawSequence(rawEvents) &&
        rawEvents.length > 0);
}
export async function collectDoctorTaskStateShadowIssues(cwd, effective) {
    if (readTasksCanonicalAuthority(effective) !== "git-event-log") {
        return [];
    }
    const issues = [];
    const rawEvents = readTaskStateEventLogJsonl(cwd);
    if (rawEvents.length === 0) {
        return [];
    }
    let Database;
    try {
        ({ default: Database } = await import("better-sqlite3"));
    }
    catch {
        return issues;
    }
    const ctx = { workspacePath: cwd, effectiveConfig: effective };
    const dbRel = planningSqliteDatabaseRelativePath(ctx);
    const dbAbs = path.resolve(cwd, dbRel);
    if (!fs.existsSync(dbAbs)) {
        issues.push({ path: dbRel, reason: "shadow-sqlite-missing" });
        return issues;
    }
    let db;
    try {
        db = new Database(dbAbs, { readonly: true });
    }
    catch {
        return issues;
    }
    try {
        const admitted = admitTaskStateEventStream(rawEvents);
        if (!admitted.ok) {
            if (projectionFreshThroughLog(db, rawEvents)) {
                return issues;
            }
            issues.push({
                path: ".workspace-kit/tasks/task-state-events.jsonl",
                reason: `shadow-admission-failed: ${admitted.error.message}`
            });
            return issues;
        }
        const replayed = replayTaskStateEvents(admitted.events);
        if (!replayed.ok) {
            if (projectionFreshThroughLog(db, rawEvents)) {
                return issues;
            }
            issues.push({
                path: ".workspace-kit/tasks/task-state-events.jsonl",
                reason: `shadow-replay-failed: ${replayed.error.message}`
            });
            return issues;
        }
        const rows = db
            .prepare("SELECT id, status FROM task_engine_tasks WHERE archived = 0")
            .all();
        const sqliteMap = statusMap(rows);
        const eventMap = statusMap(replayed.result.document.tasks);
        for (const [id, sqliteStatus] of sqliteMap) {
            const eventStatus = eventMap.get(id);
            if (eventStatus === undefined) {
                issues.push({
                    path: dbRel,
                    reason: `shadow-drift: task ${id} in SQLite missing from event projection`
                });
            }
            else if (eventStatus !== sqliteStatus) {
                issues.push({
                    path: dbRel,
                    reason: `shadow-drift: task ${id} status sqlite=${sqliteStatus} event=${eventStatus}`
                });
            }
        }
        for (const id of eventMap.keys()) {
            if (!sqliteMap.has(id)) {
                issues.push({
                    path: dbRel,
                    reason: `shadow-drift: task ${id} in event projection missing from SQLite`
                });
            }
        }
    }
    finally {
        db.close();
    }
    return issues;
}
