import fs from "node:fs";
import path from "node:path";
/**
 * Per-staff JSON-file store of inbox entries.
 *
 * Persistence layout: `<stateDir>/inbox/<staffId>.json` — one file per staff,
 * containing a JSON object `{ staffId, entries: InboxEntry[] }` with entries
 * in insertion (FIFO) order.
 *
 * Loads are lazy: a staff's file is read on first access and cached in
 * memory. Writes flush the full file synchronously. The class mirrors
 * `StaffStore` semantics (synchronous fs, no migrations) so that the store
 * is safe to use from any code path that already deals with `StaffStore`.
 */
export class InboxStore {
    inboxDir;
    /** staffId → entries (in-memory cache; populated on first access). */
    byStaff = new Map();
    /** Marks the per-staff files we have already attempted to load. */
    loaded = new Set();
    constructor(stateDir) {
        this.inboxDir = path.join(stateDir, "inbox");
    }
    fileFor(staffId) {
        return path.join(this.inboxDir, `${staffId}.json`);
    }
    ensureLoaded(staffId) {
        if (this.loaded.has(staffId)) {
            return this.byStaff.get(staffId) ?? [];
        }
        this.loaded.add(staffId);
        const file = this.fileFor(staffId);
        try {
            if (fs.existsSync(file)) {
                const raw = JSON.parse(fs.readFileSync(file, "utf-8"));
                if (raw && Array.isArray(raw.entries)) {
                    const entries = raw.entries.filter((e) => e && typeof e.id === "string");
                    this.byStaff.set(staffId, entries);
                    return entries;
                }
            }
        }
        catch (err) {
            console.error(`[inbox-store] Failed to load inbox for staff ${staffId}:`, err);
        }
        this.byStaff.set(staffId, []);
        return this.byStaff.get(staffId);
    }
    save(staffId) {
        const entries = this.byStaff.get(staffId) ?? [];
        try {
            if (!fs.existsSync(this.inboxDir)) {
                fs.mkdirSync(this.inboxDir, { recursive: true });
            }
            const file = this.fileFor(staffId);
            const payload = JSON.stringify({ staffId, entries }, null, 2);
            fs.writeFileSync(file, payload, "utf-8");
        }
        catch (err) {
            console.error(`[inbox-store] Failed to save inbox for staff ${staffId}:`, err);
        }
    }
    /** Insert or replace an entry. Last-writer-wins on id collision. */
    put(entry) {
        const entries = this.ensureLoaded(entry.staffId);
        const idx = entries.findIndex((e) => e.id === entry.id);
        if (idx >= 0) {
            entries[idx] = entry;
        }
        else {
            entries.push(entry);
        }
        this.save(entry.staffId);
    }
    get(staffId, entryId) {
        const entries = this.ensureLoaded(staffId);
        return entries.find((e) => e.id === entryId);
    }
    /** All entries for a staff, in FIFO (insertion) order. */
    list(staffId) {
        return this.ensureLoaded(staffId).slice();
    }
    listPending(staffId) {
        return this.ensureLoaded(staffId).filter((e) => e.state === "pending");
    }
    /**
     * Apply a shallow patch to an entry. `id`, `staffId` and `createdAt` are
     * immutable. Undefined values in `updates` are skipped; explicit `null`
     * deletes the field. Returns false if the entry doesn't exist.
     */
    update(staffId, entryId, updates) {
        const entries = this.ensureLoaded(staffId);
        const idx = entries.findIndex((e) => e.id === entryId);
        if (idx < 0)
            return false;
        const existing = entries[idx];
        const rec = existing;
        for (const [k, v] of Object.entries(updates)) {
            if (v === undefined)
                continue;
            if (v === null) {
                delete rec[k];
            }
            else {
                rec[k] = v;
            }
        }
        this.save(staffId);
        return true;
    }
    remove(staffId, entryId) {
        const entries = this.ensureLoaded(staffId);
        const idx = entries.findIndex((e) => e.id === entryId);
        if (idx < 0)
            return false;
        entries.splice(idx, 1);
        this.save(staffId);
        return true;
    }
    /** Wipe the entire inbox for a staff (used when a staff is deleted). */
    removeAll(staffId) {
        this.byStaff.set(staffId, []);
        this.loaded.add(staffId);
        const file = this.fileFor(staffId);
        try {
            if (fs.existsSync(file))
                fs.unlinkSync(file);
        }
        catch (err) {
            console.error(`[inbox-store] Failed to remove inbox for staff ${staffId}:`, err);
        }
    }
}
//# sourceMappingURL=inbox-store.js.map