import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
import { compactHistory } from "../item-helpers.js";
import { trimFlushedItems } from "../live-item-flush.js";
export function useTranscriptHistory({ terminalHistoryPrinter, terminalHistoryContext, writeStdout, enqueueStdout, sessionStore, history, setHistory, setLiveItems, compactHistoryItems = (items) => compactHistory(items), trimFlushItems = (items) => trimFlushedItems(items), }) {
    const terminalHistoryContextRef = useRef(terminalHistoryContext);
    const pendingHistoryFlushRef = useRef([]);
    const drainedHistoryFlushRef = useRef([]);
    const streamedAssistantFlushRef = useRef({
        flushedChars: 0,
        text: "",
    });
    const [historyFlushGeneration, setHistoryFlushGeneration] = useState(0);
    useEffect(() => {
        terminalHistoryContextRef.current = terminalHistoryContext;
    }, [terminalHistoryContext]);
    const printHistoryItems = useCallback((items, options) => {
        if (!terminalHistoryPrinter || items.length === 0)
            return;
        terminalHistoryPrinter.print(items, terminalHistoryContextRef.current, {
            reason: options?.reason ?? "print",
            ...options,
            write: enqueueStdout ?? writeStdout,
        });
    }, [enqueueStdout, terminalHistoryPrinter, writeStdout]);
    const queueFlush = useCallback((items) => {
        const flushed = trimFlushItems(items);
        if (flushed.length === 0)
            return;
        pendingHistoryFlushRef.current = [...pendingHistoryFlushRef.current, ...flushed];
        // Render the rows to ANSI and enqueue the bytes NOW, before any state
        // update. With the patched `insertBeforeFrame` the enqueue is passive (no
        // terminal write), so the bytes ride the very next frame write — the one
        // produced by the batched setLiveItems/generation updates below, which is
        // also the write that shrinks the live frame. One write = no footer jump.
        printHistoryItems(flushed, { reason: "flush" });
        if (sessionStore) {
            const queuedIds = new Set(items.map((item) => item.id));
            sessionStore.liveItems = (sessionStore.liveItems ?? []).filter((item) => !queuedIds.has(item.id));
            // Mirror the flushed rows into sessionStore.history SYNCHRONOUSLY.
            // The React-state fold-in below is deferred to effects, but the
            // patched ink's bottom-pinned repaint (slash menu close) can fire on
            // THIS very commit — e.g. submit finalizes deferred assistant rows +
            // the user prompt while the menu close shrinks the frame. Its
            // backfill serializes sessionStore.history; if these rows aren't in
            // it yet, the repaint redraws a stale screen and the just-finalized
            // messages visibly vanish into blank space.
            const knownIds = new Set((sessionStore.history ?? []).map((item) => item.id));
            const newItems = flushed.filter((item) => !knownIds.has(item.id));
            if (newItems.length > 0) {
                sessionStore.history = compactHistoryItems([
                    ...(sessionStore.history ?? []),
                    ...newItems,
                ]);
            }
        }
        // Remove the flushed rows from the live frame in the SAME React batch as
        // the generation bump. Ink emits one frame write per commit (throttled,
        // leading edge), so deferring this shrink to a later effect would split
        // enqueue and shrink across two writes — the second one (shrink without
        // compensating scrollback bytes) is exactly what strands the footer.
        const flushedIds = new Set(flushed.map((item) => item.id));
        setLiveItems((prev) => prev.filter((item) => !flushedIds.has(item.id)));
        setHistoryFlushGeneration((generation) => generation + 1);
    }, [compactHistoryItems, printHistoryItems, sessionStore, setLiveItems, trimFlushItems]);
    useEffect(() => {
        printHistoryItems(history, { reason: "history-effect" });
    }, [history, printHistoryItems]);
    useLayoutEffect(() => {
        // Printing and live-row removal already happened synchronously inside
        // queueFlush (they must batch into the commit that carries the enqueued
        // scrollback bytes). This effect only moves the queue along so the
        // follow-up effect below can fold the flushed rows into React history.
        const flushed = pendingHistoryFlushRef.current;
        if (flushed.length === 0)
            return;
        pendingHistoryFlushRef.current = [];
        drainedHistoryFlushRef.current = flushed;
    }, [historyFlushGeneration]);
    useEffect(() => {
        const flushed = drainedHistoryFlushRef.current;
        if (flushed.length === 0)
            return;
        drainedHistoryFlushRef.current = [];
        setHistory((prev) => {
            const existingIds = new Set(prev.map((item) => item.id));
            const nextItems = flushed.filter((item) => !existingIds.has(item.id));
            if (nextItems.length === 0)
                return prev;
            const next = compactHistoryItems([...prev, ...nextItems]);
            if (sessionStore)
                sessionStore.history = next;
            return next;
        });
    }, [historyFlushGeneration, printHistoryItems, sessionStore, setHistory]);
    const finalizeSubmittedUserItem = useCallback((item, deferredLiveItems = []) => {
        streamedAssistantFlushRef.current = { flushedChars: 0, text: "" };
        const priorLiveItems = trimFlushItems([...deferredLiveItems]);
        const finalizedItems = [...priorLiveItems, item];
        // queueFlush renders + enqueues the rows synchronously (the printer
        // dedupes by id), so the deferred final assistant output is anchored in
        // scrollback before the next prompt clears the live frame.
        queueFlush(finalizedItems);
        setLiveItems([]);
    }, [queueFlush, setLiveItems, trimFlushItems]);
    const clearPendingHistory = useCallback(() => {
        pendingHistoryFlushRef.current = [];
        terminalHistoryPrinter?.clear();
    }, [terminalHistoryPrinter]);
    return {
        pendingHistoryFlushRef,
        streamedAssistantFlushRef,
        printHistoryItems,
        queueFlush,
        finalizeSubmittedUserItem,
        clearPendingHistory,
    };
}
//# sourceMappingURL=useTranscriptHistory.js.map