import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React from "react";
import { Box, Static, Text, render } from "ink";
import { describe, expect, it } from "vitest";
import { UserMessage } from "./components/UserMessage.js";
import { getScrollStabilizationDecision, getStaticHistoryKey, hasParagraphBreakLiveUserMessage, isTallLiveUserMessage, } from "./App.js";
function renderHarnessItem(item) {
    if (item.kind === "banner")
        return _jsx(Text, { children: "EZ CODER BANNER" });
    if (item.kind === "user")
        return _jsx(UserMessage, { text: item.text });
    return _jsx(Text, { children: item.text });
}
function LongPromptHarness({ history, liveItems, resizeKey, staticRenderProbe, }) {
    const hasTallLiveUserMessage = liveItems.some((item) => item.kind === "user" && isTallLiveUserMessage(item.text, 24));
    const hasParagraphBreakUserMessage = liveItems.some((item) => item.kind === "user" && hasParagraphBreakLiveUserMessage(item.text));
    const scrollStabilizationDecision = getScrollStabilizationDecision({
        isUserScrolled: false,
        hasNewOutput: liveItems.length > 0,
        hasTallLiveUserMessage,
        hasParagraphBreakLiveUserMessage: hasParagraphBreakUserMessage,
    });
    const staticHistoryKey = scrollStabilizationDecision.preserveStatic
        ? getStaticHistoryKey({ resizeKey: 0 })
        : getStaticHistoryKey({ resizeKey });
    return (_jsxs(Box, { flexDirection: "column", width: 80, children: [_jsx(Static, { items: history, children: (item) => {
                    staticRenderProbe?.(item);
                    return _jsx(Box, { children: renderHarnessItem(item) }, item.id);
                } }, staticHistoryKey), _jsx(Box, { flexDirection: "column", children: liveItems.map((item) => (_jsx(Box, { children: renderHarnessItem(item) }, item.id))) })] }));
}
function stripAnsi(value) {
    return value.replace(new RegExp(String.raw `\u001B\[[0-?]*[ -/]*[@-~]`, "g"), "");
}
function count(value, needle) {
    return value.split(needle).length - 1;
}
function splitDebugFrames(output) {
    return stripAnsi(output)
        .split(/\n\s*\n/)
        .map((frame) => frame.trim())
        .filter(Boolean);
}
const tallPrompt = Array.from({ length: 18 }, (_, i) => `LONG_PROMPT_LINE_${String(i + 1).padStart(2, "0")}`).join("\n");
const paragraphBreakPrompt = "This\n\nIs\n\nA\n\nTest...";
describe("long prompt regression harness", () => {
    it("reproduces paragraph-break prompt scrollback instability before assistant output", () => {
        expect(isTallLiveUserMessage(paragraphBreakPrompt, 24)).toBe(false);
        expect(hasParagraphBreakLiveUserMessage(paragraphBreakPrompt)).toBe(true);
        const history = [{ kind: "banner", id: "banner" }];
        const liveSubmittedPrompt = {
            kind: "user",
            id: "submitted-paragraph-breaks",
            text: paragraphBreakPrompt,
        };
        let output = "";
        const staticRenders = [];
        const stdout = {
            columns: 80,
            rows: 24,
            write(chunk) {
                output += chunk;
                return true;
            },
            on() { },
            off() { },
        };
        const { rerender, unmount } = render(_jsx(LongPromptHarness, { history: history, liveItems: [liveSubmittedPrompt], resizeKey: 0, staticRenderProbe: (item) => staticRenders.push(item.id) }), { stdout, columns: 80, rows: 24, debug: true });
        // Model immediate post-submit terminal churn before any assistant output: a resize/remount
        // during a short paragraph-break live prompt must not replay Static or duplicate the prompt.
        rerender(_jsx(LongPromptHarness, { history: history, liveItems: [liveSubmittedPrompt], resizeKey: 1, staticRenderProbe: (item) => staticRenders.push(item.id) }));
        const frames = splitDebugFrames(output);
        const scrollback = stripAnsi(output);
        const lastFullScreenFrame = [...frames].reverse().find((frame) => frame.includes("EZ CODER BANNER")) ?? "";
        const lastPromptFrame = [...frames].reverse().find((frame) => frame.includes("> This")) ?? "";
        expect(staticRenders).toEqual(["banner"]);
        expect(lastFullScreenFrame).toContain("EZ CODER BANNER");
        expect(lastPromptFrame).toContain("> This");
        expect(scrollback).toContain("> This ⏎ Is ⏎ A ⏎ Test...");
        unmount();
    });
    it("captures a tall submitted prompt through live updates/remount/resize without clipping or duplication", () => {
        expect(isTallLiveUserMessage(tallPrompt, 24)).toBe(true);
        const history = [{ kind: "banner", id: "banner" }];
        const liveSubmittedPrompt = { kind: "user", id: "submitted", text: tallPrompt };
        let output = "";
        const stdout = {
            columns: 80,
            rows: 24,
            write(chunk) {
                output += chunk;
                return true;
            },
            on() { },
            off() { },
        };
        const instance = render(_jsx(LongPromptHarness, { history: history, liveItems: [liveSubmittedPrompt], resizeKey: 0 }), { stdout, columns: 80, rows: 24, debug: true });
        const { rerender, unmount } = instance;
        rerender(_jsx(LongPromptHarness, { history: history, liveItems: [
                liveSubmittedPrompt,
                { kind: "assistant", id: "agent-1", text: "agent live update 1" },
            ], resizeKey: 0 }));
        rerender(_jsx(LongPromptHarness, { history: history, liveItems: [
                liveSubmittedPrompt,
                { kind: "assistant", id: "agent-2", text: "agent live update 2 after remount" },
            ], resizeKey: 1 }));
        const frames = stripAnsi(output).split("EZ CODER BANNER");
        const frame = `EZ CODER BANNER${frames.at(-1) ?? ""}`;
        expect(count(frame, "EZ CODER BANNER")).toBe(1);
        expect(count(frame, "> LONG_PROMPT_LINE_01")).toBe(1);
        expect(count(frame, "LONG_PROMPT_LINE_02")).toBe(1);
        expect(count(frame, "LONG_PROMPT_LINE_18")).toBe(1);
        expect(frame).toContain("agent live update 2 after remount");
        unmount();
    });
    it("observes duplicate scrollback frames when a tall prompt is followed by live output and Static remounts", () => {
        const history = [{ kind: "banner", id: "banner" }];
        const liveSubmittedPrompt = { kind: "user", id: "submitted", text: tallPrompt };
        let output = "";
        const stdout = {
            columns: 80,
            rows: 24,
            write(chunk) {
                output += chunk;
                return true;
            },
            on() { },
            off() { },
        };
        const { rerender, unmount } = render(_jsx(LongPromptHarness, { history: history, liveItems: [liveSubmittedPrompt], resizeKey: 0 }), { stdout, columns: 80, rows: 24, debug: true });
        rerender(_jsx(LongPromptHarness, { history: history, liveItems: [
                liveSubmittedPrompt,
                { kind: "assistant", id: "agent-1", text: "agent live update before resize" },
            ], resizeKey: 0 }));
        rerender(_jsx(LongPromptHarness, { history: history, liveItems: [
                liveSubmittedPrompt,
                { kind: "assistant", id: "agent-2", text: "agent live update after resize" },
            ], resizeKey: 1 }));
        const frames = splitDebugFrames(output);
        const lastFrame = frames.at(-1) ?? "";
        const lastFullScreenFrame = [...frames].reverse().find((frame) => frame.includes("EZ CODER BANNER")) ?? "";
        expect(lastFullScreenFrame).toContain("EZ CODER BANNER");
        expect(lastFullScreenFrame).toContain("> LONG_PROMPT_LINE_01");
        expect(lastFullScreenFrame).toContain("LONG_PROMPT_LINE_18");
        expect(lastFrame).toContain("> LONG_PROMPT_LINE_01");
        unmount();
    });
    it("keeps Static history keyed stable when tall-message/user-scroll stabilization is active", () => {
        const tallDecision = getScrollStabilizationDecision({
            isUserScrolled: false,
            hasNewOutput: true,
            hasTallLiveUserMessage: true,
        });
        const scrolledDecision = getScrollStabilizationDecision({
            isUserScrolled: true,
            hasNewOutput: true,
            hasTallLiveUserMessage: false,
        });
        const paragraphBreakDecision = getScrollStabilizationDecision({
            isUserScrolled: false,
            hasNewOutput: true,
            hasTallLiveUserMessage: false,
            hasParagraphBreakLiveUserMessage: true,
        });
        expect(tallDecision).toEqual({ preserveStatic: true, autoFollow: false });
        expect(scrolledDecision).toEqual({ preserveStatic: true, autoFollow: false });
        expect(paragraphBreakDecision).toEqual({ preserveStatic: true, autoFollow: true });
        expect(getStaticHistoryKey({ resizeKey: 7 })).toBe(getStaticHistoryKey({ resizeKey: 7 }));
    });
    it("does not replay Static history while live tall-prompt output changes without resize", () => {
        const staticRenders = [];
        const history = [
            { kind: "banner", id: "banner" },
            { kind: "assistant", id: "history-1", text: "already flushed history" },
        ];
        const liveSubmittedPrompt = { kind: "user", id: "submitted", text: tallPrompt };
        const stdout = {
            columns: 80,
            rows: 24,
            write() {
                return true;
            },
            on() { },
            off() { },
        };
        const { rerender, unmount } = render(_jsx(LongPromptHarness, { history: history, liveItems: [liveSubmittedPrompt], resizeKey: 5, staticRenderProbe: (item) => staticRenders.push(item.id) }), { stdout, columns: 80, rows: 24, debug: true });
        expect(staticRenders).toEqual(["banner", "history-1"]);
        rerender(_jsx(LongPromptHarness, { history: history, liveItems: [
                liveSubmittedPrompt,
                { kind: "assistant", id: "agent", text: "new live output" },
            ], resizeKey: 5, staticRenderProbe: (item) => staticRenders.push(item.id) }));
        expect(staticRenders).toEqual(["banner", "history-1"]);
        unmount();
    });
});
//# sourceMappingURL=long-prompt-regression-harness.test.js.map