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

test("Stop stays inert through interruption and deliberate Resume starts a fresh run", WEB, async ({t3, app, screen, browser}) => {
  // Synthetic replay: an approval with relative paths only, so public evidence has no host paths.
  const fixture = `${APP_DIR}/apps/server/scripts/dev-replay/showcase_transcript.ndjson`;
  const original = await readFile(fixture, "utf8");
  const records = original.split("\n").filter(Boolean).map(line => JSON.parse(line));
  const chosen = records.filter(r => r.type === "transcript_start" || r.type === "expect_outbound" && r.frame.method === "session/prompt" || r.frame?.method === "session/request_permission" || r.frame?.params?.update?.toolCallId === "showcase-edit");
  await writeFile(fixture, JSON.stringify(chosen[0]) + "\n" + chosen.slice(1).map(r => JSON.stringify(r).replaceAll("<workspace>/", "").replaceAll('"name":"Yes"', '"name":"Approve"').replaceAll('"name":"No"', '"name":"Decline"')).join("\n") + "\n" + JSON.stringify({type:"emit_inbound", frame:{kind:"response", method:"session/prompt", result:{stopReason:"end_turn"}}}) + "\n");
  const screenshot = async (label: string) => {
    await browser.evaluate(() => document.getAnimations().forEach(animation => animation.pause()));
    for(let attempt=0;attempt<8;attempt++) {
      const first=await app.screenshot(`${label}-stability-check-${attempt}`);
      await new Promise(r=>setTimeout(r,500));
      const second=await app.screenshot(label);
      const crop = (file: string) => execFileSync("convert",[file,"-crop","740x320+270+480","+repage","rgba:-"]);
      if(crop(first).equals(crop(second))) {
        const buttons = await browser.evaluate(() => [...document.querySelectorAll("button")].filter(b=>/^(Stopping|Stopped|Stop generation|Resume thread)$/.test(b.getAttribute("aria-label")??"")).map(b=>({label:b.getAttribute("aria-label"),disabled:b.disabled})));
        console.log("CAPTURE_STATE",JSON.stringify({label,buttons}));
        return second;
      }
    }
    throw new Error(`Capture did not settle: ${label}`);
  };
  try {
    const project = await t3.project("Stop demonstration", {"src/math.ts":"export const sum = 2;\n"});
    await browser.emulateMedia({reducedMotion:"reduce",colorScheme:"light"});
    await t3.open();
    await screen.getByRole("button", "Permissions: Full access").tap();
    await screen.getByRole("menuitemradio", "Supervised").tap();
    await screen.getByRole("textbox").fill("Prepare a small math helper and ask before editing.");
    await screen.getByRole("textbox").press("Enter");
    await screen.getByRole("button", "Approve").waitFor({state:"visible"});
    await screen.getByRole("button", "Stop generation").waitFor({state:"visible"});
    await screenshot("approval-before-stop");
    await browser.evaluate(() => {
      const realSend = WebSocket.prototype.send;
      const state = {held: [] as Array<() => void>, messages: [] as string[], hold:true};
      (window as any).__stopProof = state;
      WebSocket.prototype.send = function(data) {
        const str = typeof data === "string" ? data : "binary";
        state.messages.push(str);
        if (state.hold && /"type":"thread.stop"/.test(str)) { state.held.push(() => realSend.call(this,data)); return; }
        return realSend.call(this,data);
      };
    });
    const point = await browser.evaluate(() => { const r = document.querySelector('[aria-label="Stop generation"]')!.getBoundingClientRect(); return {x:r.x+r.width/2,y:r.y+r.height/2}; });
    const click = async () => { await browser.mouse.move(point.x,point.y); await browser.mouse.down(); await browser.mouse.up(); };
    await click();
    await browser.mouse.move(600,350);
    await new Promise(r => setTimeout(r,600));
    await screenshot("stopping-pending");
    await new Promise(r => setTimeout(r,10_000));
    await screenshot("timeout-reenabled");
    await browser.evaluate(() => { const state=(window as any).__stopProof; state.hold=false; state.held.splice(0).forEach((send:()=>void)=>send()); });
    await new Promise(r => setTimeout(r,1800));
    await screenshot("stop-completed");
    await click(); await click(); await click();
    await new Promise(r => setTimeout(r,1800));
    await screenshot("repeat-position");
    const accidentalResume = await screen.getByText("Continue where you left off.", {exact:true}).first().isVisible();
    if (await screen.getByRole("button", "Stop generation").isVisible()) {
      await screen.getByRole("button", "Stop generation").tap();
      await new Promise(r => setTimeout(r,1800));
    }
    await screen.getByRole("button", "Resume thread").tap();
    await screen.getByRole("button", "Stop generation").waitFor({state:"visible"});
    await new Promise(r => setTimeout(r,800));
    await screenshot("deliberate-resume-new-stop");
    const freshStopEnabled = await screen.getByRole("button", "Stop generation").isEnabled();
    await browser.evaluate(() => { (window as any).__stopProof.hold=true; });
    const draftPoint = await browser.evaluate(() => { const r=document.querySelector('[aria-label="Stop generation"]')!.getBoundingClientRect(); return {x:r.x+r.width/2,y:r.y+r.height/2}; });
    await screen.getByRole("button", "Stop generation").tap();
    await screen.getByRole("textbox").fill("Keep this draft for later.");
    await browser.evaluate(() => {const s=(window as any).__stopProof;s.hold=false;s.held.splice(0).forEach((send:()=>void)=>send());});
    await new Promise(r => setTimeout(r,1800));
    await screenshot("typed-draft-before-repeat");
    await browser.mouse.move(draftPoint.x,draftPoint.y);
    for(let n=0;n<3;n++){await browser.mouse.down();await browser.mouse.up();}
    await new Promise(r => setTimeout(r,1800));
    await screenshot("typed-draft-after-repeat");
    const draftKept = await browser.evaluate(() => !!document.querySelector('[data-testid="composer-editor"]')?.textContent?.includes("Keep this draft for later."));
    console.log("OBSERVATIONS", JSON.stringify({accidentalResume,freshStopEnabled,draftKept}));
    await browser.evaluate(() => (window as any).t3dev.replay("question"));
    await screen.getByRole("button", "Submit", {exact:true}).waitFor({state:"visible"});
    await screenshot("question-answer-layout");
    await browser.evaluate(() => { (window as any).__stopProof.hold=true; });
    await screen.getByRole("button", "Stop generation").tap();
    await new Promise(r => setTimeout(r,600));
    await screenshot("question-stopping-layout");
    await browser.evaluate(() => {const s=(window as any).__stopProof;s.hold=false;s.held.splice(0).forEach((send:()=>void)=>send());});
    await new Promise(r => setTimeout(r,1800));
    await screenshot("question-stopped-layout");
    expect(accidentalResume).toBe(false);
    expect(freshStopEnabled).toBe(true);
    expect(draftKept).toBe(true);
  } finally { await writeFile(fixture,original); }
});
