import fs from "node:fs";
import path from "node:path";
import { TASK_ENGINE_TASKS_TABLE } from "../../core/state/workspace-kit-sqlite.js";
import { planningSqliteDatabaseRelativePath } from "./planning-config.js";
import { normalizeTaskStoreDocumentFromUnknown } from "./persistence/task-store-migration.js";
import { loadTaskFeatureLinkMap } from "./persistence/feature-registry-queries.js";
import { rowToTaskEntity } from "./persistence/sqlite-task-row-mapping.js";
import { classifyNativeSqliteErrorMessage, nativeSqliteRecoveryHint } from "../../core/native-sqlite-diagnostics.js";
import { collectTaskStoreSqliteStagedIssues } from "../../core/task-store-git-commit-policy.js";
import { withPlanningSqliteRecoveryHint } from "./planning-sqlite-doctor-remediation.js";
import { hashWorkingTreeTaskStore, probeTaskStoreShaAtGitRef } from "./persistence/sync-task-store-after-merge.js";
import { readWorkspaceStatusSnapshotFromKitSqliteDb, workspaceStatusTableAvailable } from "./persistence/workspace-status-store.js";
/**
 * When effective config selects SQLite for task/wishlist persistence, verify the native addon loads,
 * the DB file exists and can be opened read-only; if a planning row is present, validate embedded task JSON.
 */
export async function validatePlanningPersistenceForDoctor(workspacePath, effectiveConfig) {
    const issues = [];
    let Database;
    try {
        ({ default: Database } = await import("better-sqlite3"));
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        const classification = classifyNativeSqliteErrorMessage(msg);
        const hint = nativeSqliteRecoveryHint(classification);
        issues.push({
            path: "better-sqlite3",
            reason: withPlanningSqliteRecoveryHint(`${classification.kind}: ${msg} — ${hint}`)
        });
        return issues;
    }
    const ctx = { workspacePath, effectiveConfig };
    const dbRel = planningSqliteDatabaseRelativePath(ctx);
    const dbAbs = path.resolve(workspacePath, dbRel);
    const relDisplay = path.relative(workspacePath, dbAbs) || dbAbs;
    for (const staged of collectTaskStoreSqliteStagedIssues({
        workspacePath,
        sqliteDatabaseRelativePath: dbRel
    })) {
        issues.push({ path: staged.path, reason: staged.reason });
    }
    if (!fs.existsSync(dbAbs)) {
        issues.push({
            path: relDisplay,
            reason: "sqlite-planning-db-missing (tasks.persistenceBackend is sqlite; run migrate-task-persistence json-to-sqlite or fix tasks.sqliteDatabaseRelativePath)"
        });
        return issues;
    }
    let db;
    try {
        db = new Database(dbAbs, { readonly: true });
    }
    catch (err) {
        issues.push({
            path: relDisplay,
            reason: `sqlite-open-failed: ${err.message} — check DB path permissions, disk, and WAL sidecars; ordered recovery: docs/maintainers/runbooks/native-sqlite-consumer-install.md`
        });
        return issues;
    }
    try {
        const qcRows = db.prepare("PRAGMA quick_check").all();
        for (const row of qcRows) {
            const cell = Object.values(row)[0];
            if (typeof cell === "string" && cell.toLowerCase() !== "ok") {
                issues.push({
                    path: relDisplay,
                    reason: `sqlite-quick_check-failed: ${cell} — see docs/maintainers/runbooks/native-sqlite-consumer-install.md`
                });
                return issues;
            }
        }
        const cols = db.prepare("PRAGMA table_info(workspace_planning_state)").all();
        const colSet = new Set(cols.map((c) => c.name));
        const hasWishlist = colSet.has("wishlist_store_json");
        const hasRelational = colSet.has("relational_tasks") && colSet.has("transition_log_json");
        const hasTaskTable = Boolean(db
            .prepare("SELECT 1 AS ok FROM sqlite_master WHERE type = 'table' AND name = ?")
            .get(TASK_ENGINE_TASKS_TABLE)?.ok);
        const row = hasWishlist
            ? hasRelational
                ? db
                    .prepare("SELECT task_store_json, wishlist_store_json, transition_log_json, mutation_log_json, relational_tasks FROM workspace_planning_state WHERE id = 1")
                    .get()
                : db
                    .prepare("SELECT task_store_json, wishlist_store_json FROM workspace_planning_state WHERE id = 1")
                    .get()
            : hasRelational
                ? db
                    .prepare("SELECT task_store_json, transition_log_json, mutation_log_json, relational_tasks FROM workspace_planning_state WHERE id = 1")
                    .get()
                : db
                    .prepare("SELECT task_store_json FROM workspace_planning_state WHERE id = 1")
                    .get();
        if (row) {
            const relationalOn = hasRelational &&
                "relational_tasks" in row &&
                typeof row.relational_tasks === "number" &&
                row.relational_tasks === 1;
            try {
                normalizeTaskStoreDocumentFromUnknown(JSON.parse(row.task_store_json));
            }
            catch (e) {
                const msg = e instanceof Error ? e.message : String(e);
                issues.push({ path: relDisplay, reason: `sqlite-task_store_json: ${msg}` });
            }
            if (relationalOn && hasTaskTable && "transition_log_json" in row && "mutation_log_json" in row) {
                const envRow = row;
                try {
                    JSON.parse(envRow.transition_log_json);
                    JSON.parse(envRow.mutation_log_json);
                }
                catch (e) {
                    const msg = e instanceof Error ? e.message : String(e);
                    issues.push({ path: relDisplay, reason: `sqlite-envelope-logs: ${msg}` });
                }
                try {
                    const trows = db.prepare(`SELECT * FROM ${TASK_ENGINE_TASKS_TABLE}`).all();
                    const fmap = loadTaskFeatureLinkMap(db);
                    for (const tr of trows) {
                        rowToTaskEntity(tr, { taskFeatureLinkMap: fmap });
                    }
                }
                catch (e) {
                    const msg = e instanceof Error ? e.message : String(e);
                    issues.push({ path: relDisplay, reason: `sqlite-${TASK_ENGINE_TASKS_TABLE}: ${msg}` });
                }
            }
            if (hasWishlist && "wishlist_store_json" in row && typeof row.wishlist_store_json === "string") {
                try {
                    const wishDoc = JSON.parse(row.wishlist_store_json);
                    if (wishDoc.schemaVersion !== 1) {
                        issues.push({
                            path: relDisplay,
                            reason: `sqlite-wishlist_store_json: unsupported schemaVersion (expected 1, got ${wishDoc.schemaVersion})`
                        });
                    }
                }
                catch {
                    issues.push({ path: relDisplay, reason: "sqlite-wishlist_store_json: invalid JSON" });
                }
            }
        }
        const workspaceStatus = workspaceStatusTableAvailable(db)
            ? readWorkspaceStatusSnapshotFromKitSqliteDb(db)
            : null;
        const phaseKey = workspaceStatus?.currentKitPhase?.trim();
        if (phaseKey) {
            const integrationRef = `origin/release/phase-${phaseKey}`;
            const remoteSha = probeTaskStoreShaAtGitRef(workspacePath, integrationRef, dbRel);
            const localSha = hashWorkingTreeTaskStore(workspacePath, dbRel);
            if (remoteSha && localSha && remoteSha !== localSha) {
                issues.push({
                    path: relDisplay,
                    reason: `task-store-git-divergence: working-tree blob SHA ${localSha.slice(0, 12)} differs from ${integrationRef} (${remoteSha.slice(0, 12)}); prefer wk run task-state-hydrate (canonical git-event-log) or legacy wk run sync-task-store-after-merge for recovery`
                });
            }
        }
    }
    catch (err) {
        issues.push({
            path: relDisplay,
            reason: `sqlite-schema-invalid: ${err.message} — see docs/maintainers/runbooks/native-sqlite-consumer-install.md (corruption / migration recovery).`
        });
    }
    finally {
        db.close();
    }
    return issues.map((issue) => {
        if (issue.reason.includes("task-store-sqlite-staged-without-approval") ||
            issue.reason.startsWith("task-store-git-divergence:")) {
            return issue;
        }
        return { ...issue, reason: withPlanningSqliteRecoveryHint(issue.reason) };
    });
}
