/**
 * File-backed feedback store used by the local transport and by
 * `neotoma triage` when run against local storage.
 *
 * Rationale: the plan calls for `submit_feedback` in local mode to write a
 * `product_feedback` entity through `store_structured`. For MVP we write to
 * a simple JSON file under the Neotoma data dir, which is always-accessible
 * to `neotoma triage` running on the same machine without forcing the whole
 * entity-store pipeline (schemas, observations, snapshotting) through the
 * feedback path. Upgrade path: swap this for `storeStructuredForApi` once
 * the `product_feedback` schema is seeded.
 */
import { createHash, randomBytes } from "node:crypto";
import { promises as fs } from "node:fs";
import path from "node:path";
export function resolveFeedbackStorePath(dataDir) {
    const base = dataDir ??
        process.env.NEOTOMA_FEEDBACK_STORE_PATH ??
        process.env.NEOTOMA_DATA_DIR ??
        "./data";
    return path.join(base, "feedback", "records.json");
}
export function generateFeedbackId() {
    const t = Date.now().toString(36);
    const r = randomBytes(6).toString("hex");
    return `fbk_${t}_${r}`;
}
export function hashToken(token) {
    return createHash("sha256").update(token).digest("hex");
}
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 LocalFeedbackStore {
    storePath;
    constructor(storePath = resolveFeedbackStorePath()) {
        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 listPending(limit = 100) {
        const store = await readStore(this.storePath);
        return Object.values(store.records)
            .filter((r) => r.status === "submitted" || r.status === "triaged")
            .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);
    }
    async findByCommitSha(sha) {
        const store = await readStore(this.storePath);
        return Object.values(store.records)
            .filter((r) => (r.resolution_links.commit_shas ?? []).includes(sha))
            .map((r) => r.id);
    }
}
//# sourceMappingURL=local_store.js.map