import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
import { chmodSync } from "node:fs";
import { dirname } from "node:path";
import { randomUUID } from "node:crypto";
import { config } from "./config.js";
/** Hard cap on the stored addendum. Keeps a runaway paste from
 * silently bloating every system prompt by tens of KB and matches
 * what users will realistically write (a paragraph or two). The
 * route layer enforces the same cap so the wire surface fails
 * fast with 400 rather than persisting unbounded text. */
export const MAX_ADDENDUM_BYTES = 20_000;
async function ensureDir() {
    await mkdir(dirname(config.systemPromptOverridesFile), { recursive: true });
}
async function atomicWrite(data) {
    await ensureDir();
    const path = config.systemPromptOverridesFile;
    const tmp = `${path}.${randomUUID()}.tmp`;
    await writeFile(tmp, JSON.stringify(data, null, 2), { mode: 0o600 });
    try {
        chmodSync(tmp, 0o600);
    }
    catch {
        // best-effort
    }
    try {
        await rename(tmp, path);
    }
    catch (err) {
        await unlink(tmp).catch(() => undefined);
        throw err;
    }
}
async function readAll() {
    try {
        const raw = await readFile(config.systemPromptOverridesFile, "utf8");
        if (raw.trim().length === 0)
            return { projects: {} };
        const parsed = JSON.parse(raw);
        if (typeof parsed !== "object" || parsed === null || !("projects" in parsed)) {
            return { projects: {} };
        }
        const projects = parsed.projects;
        if (typeof projects !== "object" || projects === null)
            return { projects: {} };
        const out = { projects: {} };
        for (const [pid, val] of Object.entries(projects)) {
            if (typeof val !== "string")
                continue;
            out.projects[pid] = val;
        }
        return out;
    }
    catch (err) {
        if (err.code === "ENOENT")
            return { projects: {} };
        throw err;
    }
}
/**
 * Return the project's saved addendum, or an empty string if the
 * project has no override. Empty string and "no entry at all" are
 * intentionally collapsed — the wire surface treats both as "no
 * addendum to append" and there is no third tri-state to preserve.
 */
export async function getProjectSystemPromptAddendum(projectId) {
    const cur = await readAll();
    return cur.projects[projectId] ?? "";
}
/**
 * Set the project's addendum. Passing an empty string (or a string
 * that trims to empty) clears the entry so the file doesn't grow
 * with effectively-empty keys after a user clears the textbox.
 *
 * Caller is responsible for length validation — the route layer does
 * this against `MAX_ADDENDUM_BYTES` before writing.
 */
export async function setProjectSystemPromptAddendum(projectId, text) {
    const cur = await readAll();
    const trimmed = text.trim();
    if (trimmed.length === 0) {
        if (cur.projects[projectId] === undefined)
            return;
        delete cur.projects[projectId];
    }
    else {
        cur.projects[projectId] = text;
    }
    await atomicWrite(cur);
}
/**
 * Drop the project's addendum entry — called from the project
 * delete path so the overrides file doesn't accumulate orphaned
 * entries after a project is removed.
 */
export async function clearProjectSystemPromptAddendum(projectId) {
    const cur = await readAll();
    if (cur.projects[projectId] === undefined)
        return;
    delete cur.projects[projectId];
    await atomicWrite(cur);
}
//# sourceMappingURL=system-prompt-overrides.js.map