import { expect } from "e2e";
import { createServer } from "node:http";
import { readdirSync, readFileSync, readlinkSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { test, WEB } from "./t3.ts";
import { APP_DIR, T3_HOME } from "../e2e.config.ts";

test("Committed slow-load navigation returns the page and the tab stays usable", WEB, async ({ t3, app, screen }) => {
  const timers = new Set<ReturnType<typeof setTimeout>>();
  const server = createServer((req, res) => {
    if (req.url === "/image.svg") {
      const timer = setTimeout(() => { timers.delete(timer); res.writeHead(200, {"Content-Type":"image/svg+xml"}); res.end('<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"/>'); }, 20000);
      timers.add(timer);
      return;
    }
    const slow = req.url === "/slow";
    res.writeHead(200, {"Content-Type":"text/html"});
    res.end(`<html><head><title>${slow ? "Committed slow page" : "Healthy page"}</title></head><body style="font:24px system-ui;padding:40px"><h1>${slow ? "Committed slow page" : "Healthy page"}</h1><p>${slow ? "HTML arrived immediately. The image takes 20 seconds." : "The browser tab is usable."}</p>${slow ? '<img src="/image.svg">' : ''}</body></html>`);
  });
  await new Promise<void>(resolve => server.listen(0, "127.0.0.1", resolve));
  const origin = `http://127.0.0.1:${(server.address() as { port:number }).port}`;
  try {
    const project = await t3.project("Navigation proof");
    await t3.thread({project, title:"Slow navigation proof", messages:["Prepare a browser navigation proof."], model:"question"});
    // Only inspect the replay process rooted in this scenario's synthetic workspace.
    let env: Record<string,string> | undefined;
    for (const pid of readdirSync("/proc").filter(name => /^\d+$/.test(name))) {
      try {
        if (readlinkSync(`/proc/${pid}/cwd`) !== project) continue;
        const entries = readFileSync(`/proc/${pid}/environ`, "utf8").split("\0");
        const found = Object.fromEntries(entries.filter(Boolean).map(entry => { const at=entry.indexOf("="); return [entry.slice(0,at),entry.slice(at+1)]; }));
        if (found.T3_ACP_MCP_AUTHORIZATION) { env=found; break; }
      } catch {}
    }
    if (!env) throw new Error("Replay process MCP credentials were unavailable");
    await t3.open();
    await screen.getByRole("button", "Slow navigation proof", {exact:false}).first().tap();
    let session: string | null=null;
    let id=0;
    async function rpc(method:string, params:unknown) {
      const response=await fetch(env!.T3_ACP_MCP_ENDPOINT, {method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json, text/event-stream",Authorization:env!.T3_ACP_MCP_AUTHORIZATION,...(session ? {"mcp-session-id":session} : {})},body:JSON.stringify({jsonrpc:"2.0",id:++id,method,params})});
      session=response.headers.get("mcp-session-id") ?? session;
      const body=await response.text();
      if (!response.ok) throw new Error(`MCP HTTP ${response.status}`);
      const result=body.startsWith("event:") || body.startsWith("data:") ? JSON.parse(body.split("\n").find(line=>line.startsWith("data: "))!.slice(6)) : JSON.parse(body);
      if(result.error) throw new Error(JSON.stringify(result.error));
      return result.result;
    }
    await rpc("initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"navigation-proof",version:"1"}});
    const call=(name:string,args:unknown)=>rpc("tools/call",{name,arguments:args});
    const decode=(result:any)=>{ if(result.isError) throw new Error(JSON.stringify(result)); return result.structuredContent ?? JSON.parse(result.content.filter((x:any)=>x.type==="text").map((x:any)=>x.text).join("\n")); };
    const opened=decode(await call("preview_open",{url:`${origin}/healthy`}));
    const tabId=opened.tabId;
    const navigation=await call("preview_navigate",{url:`${origin}/slow`,tabId,timeoutMs:4000});
    const status=decode(await call("preview_status",{tabId}));
    const fields=(value:any)=>`URL: ${value.url}\nTitle: ${value.title}\nloading: ${value.loading}`;
    const summary=`Navigation observation (real MCP responses)\npreview_navigate — timeoutMs: 4000, default readiness\n${navigation.isError ? navigation.content.map((x:any)=>x.text).join("\n") : fields(decode(navigation))}\n\nImmediate preview_status\n${fields(status)}`;
    console.log(summary);
    execFileSync(process.execPath,["apps/server/src/bin.ts","thread","send",env.T3CODE_THREAD_ID,summary,"--base-dir",T3_HOME,"--json"],{cwd:APP_DIR,stdio:"pipe"});
    await screen.getByText("Navigation observation (real MCP responses)",{exact:false}).first().waitFor({state:"visible"});
    await screen.getByRole("button", "Agent Navigation observation (real MCP responses)").tap();
    await app.screenshot("committed-page-result");
    await new Promise(resolve=>setTimeout(resolve,2000));
    const back=decode(await call("preview_navigate",{url:`${origin}/healthy`,tabId,timeoutMs:4000}));
    console.log("Navigation back:",JSON.stringify({url:back.url,title:back.title,loading:back.loading}));
    await app.screenshot("tab-usable-after-navigation");
    await new Promise(resolve=>setTimeout(resolve,2000));
    expect(navigation.isError ?? false).toBe(false);
    const navigated=decode(navigation);
    expect(navigated.title).toBe("Committed slow page");
    expect(navigated.url).toBe(`${origin}/slow`);
    expect(navigated.loading).toBe(true);
    expect(back.title).toBe("Healthy page");
  } finally {
    for(const timer of timers) clearTimeout(timer);
    server.closeAllConnections();
    await new Promise<void>(resolve=>server.close(()=>resolve()));
  }
});
