/**
 * File-backed sandbox-abuse-report store. Mirrors
 * `src/services/feedback/local_store.ts` so the sandbox reporting pipeline
 * behaves identically to product feedback on the local transport.
 *
 * The public sandbox (sandbox.neotoma.io) is wiped weekly, so in production
 * the HTTP transport forwards reports to the agent-site Netlify pipeline for
 * durability. The local store is still used:
 *   - as a hot buffer before the forwarder runs
 *   - for integration tests
 *   - for self-hosted Neotoma installations that want a local fallback
 */
import { createHash, randomBytes } from "node:crypto";
import { promises as fs } from "node:fs";
import path from "node:path";
export function resolveSandboxReportStorePath(dataDir) {
    const base = dataDir ??
        process.env.NEOTOMA_SANDBOX_REPORT_STORE_PATH ??
        process.env.NEOTOMA_DATA_DIR ??
        "./data";
    return path.join(base, "sandbox_reports", "records.json");
}
export function generateSandboxReportId() {
    const t = Date.now().toString(36);
    const r = randomBytes(6).toString("hex");
    return `sbx_${t}_${r}`;
}
export function hashSandboxToken(token) {
    return createHash("sha256").update(token).digest("hex");
}
export function hashSubmitterIp(ip) {
    return createHash("sha256").update(ip).digest("hex").slice(0, 16);
}
async function readStore(storePath) {
    try {
        const raw = await fs.readFile(storePath, "utf8");
        const parsed = JSON.parse(raw);
        return {
            records: parsed.records ?? {},
            token_index: parsed.token_index ?? {},
        };
    }
    catch (err) {
        if (err.code === "ENOENT") {
            return { records: {}, token_index: {} };
        }
        throw err;
    }
}
async function writeStore(storePath, store) {
    await fs.mkdir(path.dirname(storePath), { recursive: true });
    const tmp = storePath + ".tmp";
    await fs.writeFile(tmp, JSON.stringify(store, null, 2), "utf8");
    await fs.rename(tmp, storePath);
}
export class LocalSandboxReportStore {
    storePath;
    constructor(storePath = resolveSandboxReportStorePath()) {
        this.storePath = storePath;
    }
    async getById(id) {
        const store = await readStore(this.storePath);
        return store.records[id] ?? null;
    }
    async getByTokenHash(tokenHash) {
        const store = await readStore(this.storePath);
        const id = store.token_index[tokenHash];
        if (!id)
            return null;
        return store.records[id] ?? null;
    }
    async upsert(record, tokenHashForIndex) {
        const store = await readStore(this.storePath);
        store.records[record.id] = record;
        if (tokenHashForIndex) {
            store.token_index[tokenHashForIndex] = record.id;
        }
        await writeStore(this.storePath, store);
    }
    async listUnforwarded(limit = 100) {
        const store = await readStore(this.storePath);
        return Object.values(store.records)
            .filter((r) => !r.forwarded_at)
            .sort((a, b) => a.submitted_at.localeCompare(b.submitted_at))
            .slice(0, limit);
    }
    async listAll() {
        const store = await readStore(this.storePath);
        return Object.values(store.records);
    }
}
//# sourceMappingURL=local_store.js.map