import { expect } from "e2e";
import { spawn, execFileSync } from "node:child_process";
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, openSync } from "node:fs";
import { join } from "node:path";
import { APP_DIR } from "../e2e.config.ts";
import { test, WEB, makeState } from "./t3.ts";

// Synthetic host, credentials, project and replay history are recreated for each commit.
test("Host images use the full-grant route beside an older LAN pairing", WEB, async ({ t3, app, screen, browser }) => {
  const stable = async (label: string) => {
    let previous = await app.screenshot(label+"-settling");
    for (let n=0;n<20;n++) {
      await new Promise(r=>setTimeout(r,500));
      const next = await app.screenshot(label);
      if(readFileSync(previous).equals(readFileSync(next))) return next;
      previous=next;
    }
    throw Error("View did not settle for capture");
  };
  const scratch = mkdtempSync("/tmp/factory-issue-31772-evidence-1/run-");
  const home = join(scratch, "host");
  mkdirSync(join(home, "userdata"), { recursive: true });
  const image = join(scratch, "shot.png");
  execFileSync("convert", ["-size", "520x220", "xc:#163849", "-fill", "#65e0b6", "-draw", "roundrectangle 35,35 485,185 18,18", "-fill", "#163849", "-gravity", "center", "-font", "DejaVu-Sans", "-pointsize", "25", "-annotate", "0", "HOST IMAGE LOADED", image]);
  const agent = join(scratch, "replay.mjs");
  writeFileSync(agent, `import {createInterface} from 'node:readline';
const send = x => process.stdout.write(JSON.stringify({jsonrpc:'2.0',...x})+'\\n');
createInterface({input:process.stdin}).on('line', l => {const m=JSON.parse(l); if(m.id==null)return; let result={};
if(m.method==='initialize')result={protocolVersion:1,agentCapabilities:{loadSession:true,promptCapabilities:{embeddedContext:true}},authMethods:[],agentInfo:{name:'Image replay',version:'1'}};
if(m.method==='session/new')result={sessionId:'image-session',models:{currentModelId:'image',availableModels:[{modelId:'image',name:'Image replay'}]}};
if(m.method==='session/prompt'){send({method:'session/update',params:{sessionId:m.params.sessionId,update:{sessionUpdate:'agent_message_chunk',content:{type:'text',text:${JSON.stringify("Here is the image saved on the host.\n\n![shot](" + image + ")")}}}}});result={stopReason:'end_turn'};}
send({id:m.id,result});});`);
  writeFileSync(join(home, "userdata/settings.json"), JSON.stringify({defaultModelSelection:{instanceId:"imageReplay",model:"image"},providerInstances:{...Object.fromEntries(["codex","claudeAgent","cursor","grok","muse","pi","opencode","antigravity"].map(driver=>[driver,{driver,enabled:false}])),imageReplay:{driver:"acpRegistry",displayName:"Image replay",config:{source:"local",customModels:["image"],commandPath:process.execPath,commandArgs:[agent]}}}}));
  const bin = join(scratch, "bin");
  mkdirSync(bin);
  writeFileSync(join(bin, "hostnamectl"), "#!/bin/sh\necho 'Image host'\n", { mode: 0o755 });
  const log = openSync(join(scratch, "host.log"), "w");
  const host = spawn(process.execPath, ["apps/server/src/bin.ts", "serve", "--base-dir", home, "--port", "39771", "--lan"], { cwd: APP_DIR, env:{...process.env,PATH:bin+":"+process.env.PATH,T3CODE_DEV_SEED:"0"}, stdio:["ignore",log,log] });
  const cli = (args: string[]) => execFileSync(process.execPath, ["apps/server/src/bin.ts", ...args, "--base-dir", home], {cwd:APP_DIR, env:{...process.env,T3CODE_DEV_SEED:"0"}, encoding:"utf8",timeout:120000});
  try {
    for(let n=0;;n++){try{const r=await fetch("http://127.0.0.1:39771/.well-known/t3/environment");if(r.ok)break;}catch{}if(n>120)throw Error("Host did not start");await new Promise(r=>setTimeout(r,500));}
    const state = makeState(home).state;
    const project = await state.project("Host image demo");
    cli(["thread","start",project,"Show the saved image","--title","Image from host","--provider","imageReplay","--model","image","--wait","--json"]);
    const restricted = cli(["pair","--ttl","1h","--scope","orchestration:read","--scope","orchestration:operate"]);
    const full = cli(["pair","--ttl","1h"]);
    const token = (s:string) => {const t=/\/pair#token=([^\s]+)/.exec(s)?.[1]; if(!t)throw Error("No pairing token");return t;};
    await t3.open();
    await screen.getByRole("button", "Start", {exact:true}).tap();
    await app.open("/settings/connections");
    await screen.getByRole("button", "Add environment").tap();
    await screen.getByLabel("Host",{exact:true}).fill("http://192.168.122.35:39771");
    await screen.getByLabel("Pairing code",{exact:true}).fill(token(restricted));
    await screen.getByRole("dialog").getByRole("button","Add environment",{exact:true}).tap();
    await screen.getByRole("button","Dismiss",{exact:true}).tap();
    await screen.getByRole("button",/^(Routes|[0-9]+ routes)$/).last().tap();
    await screen.getByRole("button","Add route",{exact:true}).tap();
    await screen.getByLabel("Host",{exact:true}).fill("http://100.72.88.18:39771");
    await screen.getByLabel("Pairing code",{exact:true}).fill(token(full));
    await screen.getByRole("dialog").getByRole("button","Add route",{exact:true}).tap();
    await screen.getByLabel("Pairing code",{exact:true}).waitFor({state:"hidden"});
    await screen.getByText("In use",{exact:true}).waitFor({state:"visible"});
    await new Promise(r=>setTimeout(r,5000));
    await stable("saved-routes");
    await app.open("/");
    await screen.getByRole("button", /^Image from host, Done,/).tap();
    // The same settled view is captured on both commits before testing the broken capability.
    await screen.getByText("Here is the image saved on the host.",{exact:false}).waitFor({state:"visible"});
    await new Promise(r=>setTimeout(r,3000));
    await stable("host-image");
    await expect(browser.locator('img[alt="shot"]')).toBeVisible();
    const loaded = await browser.evaluate(() => {const img=document.querySelector('img[alt="shot"]') as HTMLImageElement|null;return !!img?.complete && img.naturalWidth>0;});
    expect(loaded).toBe(true);
  } finally {host.kill("SIGTERM");await new Promise<void>(resolve=>host.once("exit",()=>resolve()));}
});
