import { createRequire } from "node:module";
import path from "node:path";
import { assertSupportedStatePayload, chmodPrivateFile, ensurePrivateDir, readJsonFileWithBackup, StatePersistenceError, updateJsonFileAtomic, writeJsonFileAtomic } from "./persistence.js";
import { resolveNordRelayHome } from "./nordrelay-home.js";
const require = createRequire(import.meta.url);
export function createDocumentStore(options) {
    if (options.backend === "sqlite") {
        return createSqliteDocumentStore(options);
    }
    return createJsonDocumentStore(options);
}
export function checkStateBackendAvailability(workspace, backend) {
    if (backend === "json") {
        return { ok: true, detail: "JSON state backend is available." };
    }
    let Database;
    try {
        Database = require("better-sqlite3");
    }
    catch (error) {
        return {
            ok: false,
            detail: `SQLite state backend requires better-sqlite3: ${error instanceof Error ? error.message : String(error)}`,
        };
    }
    const filePath = stateBackendPath(workspace, "sqlite");
    let db;
    try {
        ensurePrivateDir(path.dirname(filePath));
        db = new Database(filePath);
        chmodPrivateFile(filePath);
        db.exec("SELECT 1");
        return { ok: true, detail: `SQLite state backend is available at ${filePath}.` };
    }
    catch (error) {
        return {
            ok: false,
            detail: `SQLite state backend failed at ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
        };
    }
    finally {
        db?.close();
    }
}
export function stateBackendPath(workspace, backend, fileName) {
    const baseDir = stateBackendDirectory(workspace);
    if (backend === "sqlite") {
        return path.join(baseDir, "state.sqlite");
    }
    return path.join(baseDir, fileName ?? "state.json");
}
export function stateBackendDirectory(_workspace) {
    return resolveNordRelayHome();
}
function createJsonDocumentStore(options) {
    const filePath = stateBackendPath(options.workspace, "json", options.fileName);
    return {
        kind: "json",
        filePath,
        read() {
            return readJsonFileWithBackup(filePath).value;
        },
        write(value) {
            writeJsonFileAtomic(filePath, value);
        },
        update(updater) {
            return updateJsonFileAtomic(filePath, updater);
        },
    };
}
function createSqliteDocumentStore(options) {
    let Database;
    try {
        Database = require("better-sqlite3");
    }
    catch (error) {
        throw new Error(`SQLite state backend is configured, but better-sqlite3 is not available: ${error instanceof Error ? error.message : String(error)}`);
    }
    const filePath = stateBackendPath(options.workspace, "sqlite");
    let db;
    try {
        ensurePrivateDir(path.dirname(filePath));
        db = new Database(filePath);
        chmodPrivateFile(filePath);
        db.exec([
            "CREATE TABLE IF NOT EXISTS documents (",
            "key TEXT PRIMARY KEY,",
            "json TEXT NOT NULL,",
            "updated_at TEXT NOT NULL",
            ")",
        ].join(" "));
    }
    catch (error) {
        throw new Error(`SQLite state backend failed at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
    }
    return {
        kind: "sqlite",
        filePath,
        read() {
            return readSqliteDocument(db, options.sqliteKey);
        },
        write(value) {
            db.prepare([
                "INSERT INTO documents (key, json, updated_at) VALUES (?, ?, ?)",
                "ON CONFLICT(key) DO UPDATE SET json = excluded.json, updated_at = excluded.updated_at",
            ].join(" ")).run(options.sqliteKey, JSON.stringify(value), new Date().toISOString());
        },
        update(updater) {
            db.exec("BEGIN IMMEDIATE");
            try {
                const next = updater(readSqliteDocument(db, options.sqliteKey));
                db.prepare([
                    "INSERT INTO documents (key, json, updated_at) VALUES (?, ?, ?)",
                    "ON CONFLICT(key) DO UPDATE SET json = excluded.json, updated_at = excluded.updated_at",
                ].join(" ")).run(options.sqliteKey, JSON.stringify(next), new Date().toISOString());
                db.exec("COMMIT");
                return next;
            }
            catch (error) {
                try {
                    db.exec("ROLLBACK");
                }
                catch {
                    // Ignore rollback failures and surface the original error.
                }
                throw error;
            }
        },
        close() {
            db.close();
        },
    };
}
function readSqliteDocument(db, key) {
    const row = db.prepare("SELECT json FROM documents WHERE key = ?").get(key);
    if (typeof row?.json !== "string") {
        return undefined;
    }
    try {
        const value = JSON.parse(row.json);
        assertSupportedStatePayload(value, `sqlite:${key}`);
        return value;
    }
    catch (error) {
        if (error instanceof StatePersistenceError) {
            throw error;
        }
        throw new StatePersistenceError(`Cannot read SQLite state document ${key}: ${error instanceof Error ? error.message : String(error)}`, `sqlite:${key}`, error instanceof Error ? error.message : String(error));
    }
}
