import { writeFile, readFile } from "node:fs/promises";
import { expect } from "e2e";
import { test, WEB, runPromise, makeState } from "./t3.ts";
import { T3_HOME } from "../e2e.config.ts";

const scratch = "/tmp/blocked-goal-proof";

test("Blocked goals return to attention", WEB, async ({ t3, app, screen, browser }) => {
  const capture = async (label: string) => {
    await browser.mouse.move(1100,600);
    await browser.evaluate(() => { (document.activeElement as HTMLElement | null)?.blur(); });
    for (let attempt = 0; attempt < 12; attempt++) {
      const first = await app.screenshot(`${label}-stability-${attempt}`);
      await new Promise(resolve => setTimeout(resolve,500));
      const second = await app.screenshot(label);
      if ((await readFile(first)).equals(await readFile(second))) return;
    }
    throw new Error(`Capture did not stabilize: ${label}`);
  };
  const project = await t3.project("Release demo");
  const { cli } = makeState(T3_HOME);
  const start = async (title: string, background = false, unopened = false) => {
    const launched = await runPromise(cli(["thread", "start", project,
      `/goal ${unopened ? "Unopened " : ""}${title.includes("Control") ? "Control " : ""}Publish release notes using RELEASE_TOKEN.${background ? " Keep background work pending." : ""} Block when the token is unset.`,
      "--title", title, "--provider", "codex", "--model", "replay-goal", "--json", ...(unopened ? ["--wait"] : [])], [0,2]));
    return JSON.parse(launched.stdout).threadId as string;
  };
  const block = () => writeFile(`${scratch}/block`, String(Date.now()));
  const rpc = (tag: string, payload: unknown) => browser.evaluate(async ({tag,payload}) => {
    return await new Promise((resolve,reject) => {
      const ws = new WebSocket(`${location.origin.replace("http", "ws")}/ws?orchestrationProtocol=2`);
      const timer = setTimeout(() => { ws.close(); reject(new Error("RPC timeout")); },15000);
      ws.onopen = () => ws.send(JSON.stringify({_tag:"Request",id:"proof",tag,payload,headers:[]}));
      ws.onmessage = event => {
        const frames = JSON.parse(event.data);
        for(const frame of Array.isArray(frames)?frames:[frames]) {
          if(frame._tag === "Exit" && frame.requestId === "proof") {
            clearTimeout(timer); ws.close();
            if(frame.exit._tag === "Success") resolve(frame.exit.value);
            else reject(new Error(JSON.stringify(frame.exit)));
          }
        }
      };
    });
  }, {tag,payload});
  const command = (payload: object) => rpc("orchestration.dispatchCommand",{commandId:crypto.randomUUID(),...payload});
  const row = (title: string) => screen.getByRole("button", {name:new RegExp(`^${title},`)});
  const open = async (title: string) => {
    await row(title).waitFor({state:"visible",timeout:60000}); await row(title).tap();
  };
  const blocked = async () => {
    await screen.getByText("Goal blocked", {exact:false}).first().waitFor({state:"visible",timeout:60000});
    await screen.getByRole("button", "Stop generation").waitFor({state:"hidden",timeout:60000});
  };

  await writeFile(`${scratch}/clock-offset`, "0");
  const background = await start("Release with background", true);
  await browser.addInitScript(() => {
    localStorage.setItem("t3code:sidebar:working-expanded", "true");
    localStorage.setItem("t3code:sidebar:snoozed-expanded", "true");
    localStorage.setItem("t3code:sidebar:settled-expanded", "true");
  });
  await t3.open();
  await open("Release with background");
  await browser.evaluate(async () => {
    const settings = await import("/src/hooks/useSettings.ts");
    await settings.updateClientSettings({inAppNotificationsEnabled:true});
  });
  await rpc("server.updateSettings", {patch:{sidebarAutoSettleAfterDays:1}});
  await screen.getByText("Pursuing goal", {exact:false}).first().waitFor({state:"visible",timeout:60000});
  await command({type:"thread.pull-request.watch",threadId:background,host:"github.com",repository:"example/release-demo",number:1,watching:true,link:{url:"https://github.com/example/release-demo/pull/1",source:"manual"}});

  await block();
  await blocked();
  await capture("background-blocked-open");
  await screen.getByRole("link", "T3 Code home").tap();
  await capture("background-blocked-inbox");


  const snoozed = await start("Snoozed release");
  await open("Snoozed release");
  await screen.getByText("Pursuing goal", {exact:false}).first().waitFor({state:"visible",timeout:60000});
  await command({type:"thread.snooze",threadId:snoozed,snoozedUntil:new Date(Date.now()+3600000).toISOString()});
  await screen.getByRole("link", "T3 Code home").tap();
  await capture("release-snoozed");
  await block();
  await open("Snoozed release");
  await blocked();
  await screen.getByRole("link", "T3 Code home").tap();
  await capture("blocked-after-snooze");
  const workingShelf = screen.getByRole("button", /^Working \d/);
  if (await workingShelf.isVisible()) await workingShelf.tap();

  await start("Release unopened", false, true);
  await row("Release unopened").waitFor({state:"visible"});
  await screen.getByText(/Input needed|Thread completed/, {exact:true}).first().waitFor({state:"visible",timeout:60000});
  await capture("unopened-blocked-notification");
  await open("Release unopened");
  await blocked();
  await capture("unopened-goal-banner");
  await screen.getByRole("button", "Thread actions for Release unopened").tap();
  await screen.getByRole("menuitem", "Snooze", {exact:true}).waitFor({state:"visible"});
  await capture("blocked-goal-snooze-disabled");
  await browser.keyboard.press("Escape");
  await screen.getByRole("link", "T3 Code home").tap();

  await browser.keyboard.press("Control+k");
  await screen.getByPlaceholder("Search commands, projects, and threads...").fill("Release unopened");
  await screen.getByText("Release unopened", {exact:true}).last().waitFor({state:"visible"});
  await capture("command-palette-attention-pill");
  await browser.keyboard.press("Escape");

  await start("Control release", false, true);
  await row("Control release").waitFor({state:"visible"});
  await open("Control release");
  await screen.getByRole("link", "T3 Code home").tap();
  await capture("before-auto-settlement");
  await writeFile(`${scratch}/clock-offset`, String(2*24*60*60*1000));
  // Observe the real server's periodic one-minute settlement sweep after advancing its wall clock.
  await new Promise(resolve => setTimeout(resolve,65000));
  await capture("after-auto-settlement");
  await writeFile(`${scratch}/clock-offset`, "0");
  await expect(row("Release unopened")).toHaveAttribute("aria-label", /, Input,/);
});
