import { mkdir, readFile, writeFile, rename, unlink } from "fs/promises";
import { dirname, join } from "path";
import { validateRunId } from "../util/run-store.js";
import { redactSecrets as redactSecretText } from "../mcp/secret-scanner.js";
const SECRET_KEY_EXACT = ["apikey", "token", "password", "secret", "authorization", "bearer"];
const SECRET_KEY_SUFFIXES = ["_api_key", "_token", "_password", "_secret", "_auth", "_bearer"];
function isSecretKey(key) {
    const lk = key.toLowerCase();
    return SECRET_KEY_EXACT.includes(lk) ||
        SECRET_KEY_SUFFIXES.some((s) => lk.endsWith(s));
}
export function redactSecrets(obj) {
    if (typeof obj === "string")
        return redactSecretText(obj).redacted;
    if (typeof obj !== "object" || obj === null)
        return obj;
    if (Array.isArray(obj))
        return obj.map(redactSecrets);
    const result = {};
    for (const [key, value] of Object.entries(obj)) {
        if (typeof value === "string" && isSecretKey(key)) {
            result[key] = "***";
        }
        else {
            result[key] = redactSecrets(value);
        }
    }
    return result;
}
async function withTimeout(promise, ms, reason) {
    return Promise.race([
        promise,
        new Promise((_, reject) => {
            const timer = setTimeout(() => reject(new Error(`Timeout after ${ms}ms: ${reason}`)), ms);
            if (typeof timer.unref === "function") {
                timer.unref();
            }
        }),
    ]);
}
export function createStatePersister(basePath = ".omk/runs") {
    return {
        async load(runId) {
            const valid = validateRunId(runId);
            const filePath = join(basePath, valid, "state.json");
            try {
                const content = await withTimeout(readFile(filePath, "utf-8"), 10_000, `load ${filePath}`);
                const parsed = JSON.parse(content);
                return redactSecrets(parsed);
            }
            catch {
                return null;
            }
        },
        async save(state) {
            const valid = validateRunId(state.runId);
            const filePath = join(basePath, valid, "state.json");
            const cloned = JSON.parse(JSON.stringify(state));
            const toSave = { ...redactSecrets(cloned), schemaVersion: 1 };
            const tempPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
            try {
                await withTimeout((async () => {
                    await mkdir(dirname(filePath), { recursive: true });
                    await writeFile(tempPath, JSON.stringify(toSave, null, 2), "utf-8");
                    await rename(tempPath, filePath);
                })(), 30_000, `save ${filePath}`);
            }
            catch (err) {
                try {
                    await unlink(tempPath);
                }
                catch { /* ignore cleanup error */ }
                throw err;
            }
        },
    };
}
