import { readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "e2e";
import { APP_DIR } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

// Replay setup only: use ACP's full-message update to deliver an exactly empty reply,
// and publish the synthetic HTML through the running server's real MCP endpoint.
async function installRecording() {
  const agentPath = join(APP_DIR, "apps/server/scripts/dev-replay-agent.ts");
  let agent = await readFile(agentPath, "utf8");
  agent = agent.replace('import * as NodeCrypto', 'import { execFileSync } from "node:child_process";\nimport * as NodeCrypto');
  agent = agent.replace('  "agent_message_chunk",', '  "agent_message",\n  "agent_message_chunk",');
  agent = agent.replace('      const content = params.update.content as', `      if ((params.update as any).rawOutput?.scenarioPublish) {
        const args = { html: "<!doctype html><html><body style='font-family:system-ui;padding:20px'><h2>Rendered report</h2><p>The result is shown here.</p></body></html>", title: "Rendered report", height: 160 };
        const result = JSON.parse(execFileSync(process.env.T3_ACP_MCP_NODE!, [
          ...(process.env.T3_ACP_MCP_ENTRYPOINT ? [process.env.T3_ACP_MCP_ENTRYPOINT] : []),
          "acp-mcp-call", "html_render", JSON.stringify(args)
        ], { env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }, encoding: "utf8" }));
        if (result.isError) throw new Error(JSON.stringify(result));
        (params.update as any).rawOutput = result.structuredContent ?? JSON.parse(result.content.find((part: any) => part.type === "text").text);
      }
      const content = params.update.content as`);
  await writeFile(agentPath, agent);
  const prompt = { type: "expect_outbound", frame: { kind: "request", method: "session/prompt", params: "<any>" } };
  const update = (value: object) => ({ type: "emit_inbound", frame: { kind: "notification", method: "session/update", params: { sessionId: "replay", update: value } } });
  const end = { type: "emit_inbound", frame: { kind: "response", method: "session/prompt", result: { stopReason: "end_turn" } } };
  const reply = update({ sessionUpdate: "agent_message", messageId: "reply", content: [] });
  const frames = [prompt,
    update({ sessionUpdate: "tool_call", toolCallId: "render", kind: "other", title: "mcp.t3-code.html_render", status: "in_progress", rawInput: { server: "t3-code", tool: "html_render", arguments: {} }, _meta: { is_mcp_tool_call: true } }),
    update({ sessionUpdate: "tool_call_update", toolCallId: "render", status: "completed", rawOutput: { scenarioPublish: true } }),
    reply, end, prompt, reply, end];
  await writeFile(join(APP_DIR, "apps/server/scripts/dev-replay/showcase_transcript.ndjson"), frames.map(f => JSON.stringify(f)).join("\n") + "\n");
}

test("Thread find counts only the visible empty reply", WEB, async ({ t3, app, screen, browser }) => {
  await installRecording();
  const project = await t3.project("find-demo");
  await t3.thread({ project, title: "Rendered result and blank reply", messages: ["Show the report", "Continue without a report"], model: "showcase" });
  await t3.open();
  await screen.getByTestId("sidebar-row-card").tap();
  await screen.getByText("(empty response)", { exact: true }).waitFor({ state: "visible" });
  await browser.keyboard.press("Control+f");
  await screen.getByRole("searchbox", "Find in thread").fill("empty");
  await new Promise(resolve => setTimeout(resolve, 1500));
  await app.screenshot("find-empty");
  await expect(screen.getByText("1/1", { exact: true })).toBeVisible();
});
