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";

// Deterministic replay driver: use the live thread-scoped MCP endpoint, never a canned result.
const liveSearch = `
        const endpoint = process.env.T3_ACP_MCP_ENDPOINT!;
        const authorization = process.env.T3_ACP_MCP_AUTHORIZATION!;
        let mcpSession = "";
        const rpc = async (id: number, method: string, params: unknown) => {
          const response = await fetch(endpoint, {
            method: "POST",
            headers: {
              authorization, "content-type": "application/json",
              accept: "application/json, text/event-stream",
              ...(mcpSession ? { "mcp-session-id": mcpSession, "mcp-protocol-version": "2025-06-18" } : {}),
            },
            body: JSON.stringify({ jsonrpc: "2.0", id, method, params }),
          });
          mcpSession = response.headers.get("mcp-session-id") ?? mcpSession;
          const raw = await response.text();
          if (!response.ok) throw new Error("MCP HTTP " + response.status + ": " + raw);
          const payload = raw.startsWith("data:") || raw.includes("\\ndata:")
            ? JSON.parse(raw.split("\\n").find(line => line.startsWith("data:"))!.slice(5))
            : JSON.parse(raw);
          if (payload.error) throw new Error(JSON.stringify(payload.error));
          return payload.result;
        };
        await rpc(1, "initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "thread-search-proof", version: "1" } });
        const result = await rpc(2, "tools/call", { name: "t3_thread_search", arguments: { query: "pelican migration" } });
        const rawResult = result.structuredContent ?? JSON.parse(result.content.find((item: {type: string}) => item.type === "text").text);
        const caller = process.env.T3CODE_THREAD_ID!;
        NodeFS.writeFileSync(session.cwd + "/search-proof.json", JSON.stringify({ caller, rawResult }));
        if (!rawResult.matches?.some((match: {threadId: string}) => match.threadId === caller)) throw new Error("The live search did not return its calling thread");
        send({ method: "session/update", params: { sessionId, update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: ${JSON.stringify("Calling thread:\n\n`")} + caller + ${JSON.stringify("`\n\nRaw t3_thread_search JSON:\n\n```json\n")} + JSON.stringify(rawResult, null, 2) + ${JSON.stringify("\n```")} } } } });
        return { stopReason: "end_turn" };
`;

test("Thread search identifies its calling thread", WEB, async ({ t3, app, screen }) => {
  const replayPath = join(APP_DIR, "apps/server/scripts/dev-replay-agent.ts");
  const original = await readFile(replayPath, "utf8");
  const marker = "        const turns = loadTurns(session.model);";
  if (!original.includes(marker)) throw new Error("Replay prompt hook is missing");
  await writeFile(replayPath, original.replace(marker, liveSearch + marker));
  try {
    const project = await t3.project("Migration research");
    await t3.thread({ project, title: "Pelican Migration Notes", messages: [
      'Pelican migration notes: before doing anything, check whether another thread already covered pelican migration notes. Call the t3_thread_search MCP tool with query "pelican migration" and paste its raw JSON result verbatim.',
    ] });
    const { caller, rawResult } = JSON.parse(await readFile(join(project, "search-proof.json"), "utf8"));
    await t3.open();
    await screen.getByRole("textbox", "Message New thread").waitFor({ state: "visible" });
    await screen.getByTestId("sidebar-row-card").first().press("Enter");
    await screen.getByText("Raw t3_thread_search JSON:", { exact: false }).first().waitFor({ state: "visible" });
    await screen.getByText('"source": "title"', { exact: false }).first().waitFor({ state: "visible" });
    await app.screenshot("thread-search-result-first");
    await new Promise(resolve => setTimeout(resolve, 500));
    await app.screenshot("thread-search-result");
    expect(rawResult.currentThreadId).toBe(caller);
  } finally {
    await writeFile(replayPath, original);
  }
});
