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

test("An intentional CLI follow-up stays reopened after PR merge", WEB, async ({ t3, app, screen, browser }) => {
  const capture = async (label: string) => {
    let previous = await readFile(await app.screenshot(`${label}-stability-start`));
    for (let i = 0; i < 12; i++) {
      await new Promise(resolve => setTimeout(resolve, 500));
      const current = await readFile(await app.screenshot(`${label}-stability-${i}`));
      if (previous.equals(current)) return app.screenshot(label);
      previous = current;
    }
    throw new Error(`The ${label} view did not stabilize`);
  };
  const project = await t3.project("settlement-demo");
  await t3.thread({ project, title: "Review merged change", messages: ["Prepare the change"] });
  const cli = makeState(T3_HOME).cli;
  const listed = await runPromise(cli(["thread", "list", "--project", project, "--json"]));
  const [{ threadId }] = JSON.parse(listed.stdout);
  await t3.open();
  await screen.getByRole("button", "Review merged change, Done, settlement-demo", {exact:true}).tap();
  const rpc = async (tag: string, payload: object) => browser.evaluate(async ({tag,payload}) => {
    const ws = new WebSocket(`${location.origin.replace(/^http/, "ws")}/ws?orchestrationProtocol=2`);
    return await new Promise<any>((resolve, reject) => {
      ws.onopen = () => ws.send(JSON.stringify({_tag:"Request", id:"1", tag, payload, headers:[]}));
      ws.onerror = () => reject(new Error("RPC socket failed"));
      ws.onmessage = event => {
        for (const msg of String(event.data).trim().split("\n")) {
          const decoded = JSON.parse(msg);
          for (const response of Array.isArray(decoded) ? decoded : [decoded]) {
          if(response._tag === "Ping") { ws.send(JSON.stringify({_tag:"Pong"})); continue; }
          if(response._tag === "Exit" && response.requestId === "1") {
            ws.close();
            if(response.exit._tag === "Success") resolve(response.exit.value);
            else reject(new Error(JSON.stringify(response.exit)));
          }
          }
        }
      };
    });
  }, {tag,payload});
  const dispatch = (command: object) => rpc("orchestration.dispatchCommand", {commandId: crypto.randomUUID(), ...command});
  await rpc("server.updateSettings", {patch:{sidebarAutoSettleOnMerge:true, sidebarAutoSettleAfterDays:null}});
  const key = {threadId,host:"example.invalid", repository:"demo/change",number:1};
  await dispatch({type:"thread.pull-request.link",...key,url:"https://example.invalid/demo/change/pull/1",source:"manual"});
  const snapshot = {state:"open",title:"Synthetic change",headBranch:"feature",baseBranch:"main",isDraft:false,updatedAt:new Date().toISOString(),syncedAt:new Date().toISOString()};
  await dispatch({type:"thread.pull-request-link.sync",...key,snapshot,stack:null});

  await capture("open-pr");
  const mergedAt = new Date().toISOString();
  await dispatch({type:"thread.pull-request-link.sync",...key,snapshot:{...snapshot,state:"merged",mergedAt,closedAt:mergedAt,updatedAt:mergedAt,syncedAt:mergedAt},stack:null});
  const settled = async () => {
    const projection = await rpc("orchestration.getThreadProjection", {threadId});
    return projection.thread?.settledOverride ?? projection.projection?.thread?.settledOverride ?? null;
  };
  // Wait for the automatic merge settlement before resuming through the real CLI/MCP path.
  for(let i=0;i<100;i++) { if(await settled() === "settled") break; await new Promise(r=>setTimeout(r,100)); }
  if(await settled() !== "settled") throw new Error("Setup did not automatically settle on merge");
  await capture("merged-and-settled");
  await runPromise(cli(["thread","send",threadId,"please check the merged change","--wait","--json"]));
  // Request another normal settlement sweep using the unchanged merged PR snapshot.
  await dispatch({type:"thread.pull-request-link.sync",...key,snapshot:{...snapshot,state:"merged",mergedAt,closedAt:mergedAt,updatedAt:mergedAt,syncedAt:new Date().toISOString()},stack:null});
  await new Promise(r=>setTimeout(r,2500));
  await capture("after-cli-follow-up");
  await expect(await settled()).not.toBe("settled");
});
