/**
 * FeedbackStore.ts — the data-access seam for the feedback factory.
 *
 * The ported processor logic (fingerprint / similarity / cluster / verify /
 * reopen) is pure; the *stateful* operations (read unprocessed items, read active
 * clusters, create/merge/reopen clusters, mark processed, count) live behind this
 * interface so the canonical instance's real adapter (Prisma cloud DB — the
 * blocked, credentials-gated piece) is a thin shim, while everything is buildable
 * and testable today against `InMemoryFeedbackStore`.
 *
 * This is the dependency-injection boundary called out in the migration spec:
 * front → Vercel, processor → Instar job, DB → cloud (Prisma). The interface
 * shape is driven by what the ported drivers + the composition (process.ts) need.
 */
/**
 * In-memory FeedbackStore — the test/reference implementation. Faithfully mirrors
 * the reference's create/merge/reopen field mutations (reportCount increment,
 * recurrenceCount bump on regression, status change, note append) so the
 * composition can be integration-tested without a database.
 */
export class InMemoryFeedbackStore {
    feedback = new Map();
    clusters = new Map();
    counts = { captured: 0, created: 0, merged: 0, reopened: 0 };
    constructor(seed) {
        for (const f of seed?.feedback ?? [])
            this.feedback.set(f.feedbackId, { status: 'unprocessed', ...f });
        for (const c of seed?.clusters ?? [])
            this.clusters.set(c.clusterId, { ...c });
    }
    getUnprocessedFeedback() {
        return [...this.feedback.values()]
            .filter((f) => (f.status ?? 'unprocessed') === 'unprocessed')
            .sort((a, b) => String(a.receivedAt ?? '').localeCompare(String(b.receivedAt ?? '')));
    }
    getActiveClusters() {
        return [...this.clusters.values()].filter((c) => c.status !== 'resolved');
    }
    getCluster(clusterId) {
        return this.clusters.get(clusterId);
    }
    upsertClusterFromItem(clusterId, item) {
        const existing = this.clusters.get(clusterId);
        if (existing) {
            existing.reportCount = (existing.reportCount ?? 0) + 1;
        }
        else {
            this.clusters.set(clusterId, {
                clusterId, title: item.title, description: item.description, type: item.type, reportCount: 1,
            });
            this.counts.created++;
        }
    }
    mergeIntoCluster(clusterId, _item) {
        const c = this.clusters.get(clusterId);
        if (c)
            c.reportCount = (c.reportCount ?? 0) + 1;
        this.counts.merged++;
    }
    applyReopen(clusterId, decision) {
        const c = this.clusters.get(clusterId);
        if (!c)
            return;
        c.status = decision.newStatus;
        if (decision.bumpRecurrence)
            c.recurrenceCount = (c.recurrenceCount ?? 0) + 1;
        const field = decision.annotateField;
        const prior = c[field] ? `${c[field]}\n\n` : '';
        c[field] = prior + decision.note;
        this.counts.reopened++;
    }
    markProcessed(feedbackId, clusterId) {
        const f = this.feedback.get(feedbackId);
        if (f) {
            f.status = 'processing';
            f.clusterId = clusterId;
        }
        this.counts.captured++;
    }
    hasFeedback(feedbackId) {
        return this.feedback.has(feedbackId);
    }
    addFeedback(item) {
        this.feedback.set(item.feedbackId, { status: 'unprocessed', ...item });
    }
    dispatches = new Map();
    listDispatches(filter) {
        return [...this.dispatches.values()]
            .filter((d) => d.active !== false)
            .filter((d) => !filter?.since || String(d.createdAt ?? '') >= filter.since)
            .filter((d) => !filter?.type || d.type === filter.type)
            .sort((a, b) => String(a.createdAt ?? '').localeCompare(String(b.createdAt ?? '')));
    }
    findDispatchByTitle(title) {
        return [...this.dispatches.values()].find((d) => d.title === title);
    }
    createDispatch(record) {
        this.dispatches.set(record.dispatchId, { active: true, ...record });
    }
    metrics() {
        return { ...this.counts };
    }
    /** Test helper: snapshot all clusters. */
    allClusters() {
        return [...this.clusters.values()].map((c) => ({ ...c }));
    }
}
//# sourceMappingURL=FeedbackStore.js.map