import { archiveCoversOverlay, messagesEquivalent, pickPreferredMessage, sortMessagesChronologically, } from './chat-display.js';
/** Stable key for upsert across DOM polls (prefer extract id). */
function transcriptKey(m) {
    if (m.id?.trim())
        return m.id;
    return `${m.role}:${m.flatIndex ?? 0}:${(m.text ?? '').slice(0, 80)}`;
}
/**
 * Append-only per-agent transcript built from DOM snapshots.
 * DOM transcript for live overlay until JSONL covers the same turn.
 */
export class DomTranscriptStore {
    rowsByAgent = new Map();
    has(agentId) {
        return (this.rowsByAgent.get(agentId)?.size ?? 0) > 0;
    }
    clear(agentId) {
        this.rowsByAgent.delete(agentId);
    }
    /** Replace transcript (e.g. opt-in JSONL path). Live chat uses ingest only. */
    seed(agentId, rows) {
        if (!rows.length)
            return;
        const map = new Map();
        for (const m of rows) {
            map.set(transcriptKey(m), m);
        }
        this.rowsByAgent.set(agentId, map);
    }
    /** Merge latest DOM extract into transcript; returns sorted display list. */
    ingest(agentId, snapshot) {
        if (!snapshot.length)
            return this.list(agentId);
        let map = this.rowsByAgent.get(agentId);
        if (!map) {
            map = new Map();
            this.rowsByAgent.set(agentId, map);
        }
        for (const m of snapshot) {
            let dupKey;
            for (const [k, prev] of map) {
                if (messagesEquivalent(prev, m)) {
                    dupKey = k;
                    break;
                }
            }
            const key = transcriptKey(m);
            if (dupKey) {
                const prev = map.get(dupKey);
                map.delete(dupKey);
                map.set(key, pickPreferredMessage(prev, m, true));
                continue;
            }
            const prev = map.get(key);
            map.set(key, prev ? pickPreferredMessage(prev, m, true) : m);
        }
        return this.list(agentId);
    }
    list(agentId) {
        const map = this.rowsByAgent.get(agentId);
        if (!map?.size)
            return [];
        return sortMessagesChronologically([...map.values()]);
    }
    /** Drop overlay rows once the same turn appears in JSONL. */
    pruneCoveredBy(agentId, baseline) {
        const map = this.rowsByAgent.get(agentId);
        if (!map?.size || !baseline.length)
            return;
        for (const [key, row] of [...map.entries()]) {
            if (baseline.some((b) => archiveCoversOverlay(b, row))) {
                map.delete(key);
            }
        }
    }
    /** Keep transcript aligned with current CDP viewport (drop scrolled-away bubbles). */
    reconcileToViewport(agentId, snapshot) {
        const map = this.rowsByAgent.get(agentId);
        if (!map?.size)
            return;
        if (!snapshot.length) {
            this.rowsByAgent.delete(agentId);
            return;
        }
        const visibleKeys = new Set(snapshot.map((m) => transcriptKey(m)));
        for (const key of [...map.keys()]) {
            if (!visibleKeys.has(key))
                map.delete(key);
        }
    }
}
