import { expect } from 'e2e';
import { createServer } from 'node:http';
import { readdir, readFile } from 'node:fs/promises';
import { APP_DIR } from '../e2e.config.ts';
import { test, WEB } from './t3.ts';

test('Committed preview navigation survives slow readiness', WEB, async ({t3, app, browser, screen}) => {
  const transcript: string[] = [];
  const sockets = new Set<any>();
  const server = createServer((req,res) => {
    if(req.url === '/image') { setTimeout(()=>{res.writeHead(200,{'Content-Type':'image/svg+xml'});res.end('<svg xmlns="http://www.w3.org/2000/svg"/>');},20000).unref(); return; }
    if(req.url === '/never') return;
    if(req.url === '/disconnect') {req.socket.destroy();return;}
    if(req.url === '/results') {res.setHeader('Content-Type','application/json');res.end(JSON.stringify(transcript));return;}
    const slow=req.url === '/slow';
    const busy=req.url === '/busy';
    res.setHeader('Content-Type','text/html; charset=utf-8');
    res.end(`<html><head><title>${slow?'Committed slow page':busy?'Busy renderer':'Healthy page'}</title></head><body style="font:18px system-ui;background:#122033;color:#fff;padding:28px"><h1>${slow?'Committed slow page':busy?'Busy renderer':'Healthy page'}</h1><p>${slow?'HTML committed. Image response stalls for 20 seconds.':'Preview navigation evidence — real MCP responses below.'}</p>${slow?'<img src="/image">':''}<pre id="log" style="white-space:pre-wrap;overflow-wrap:anywhere;font-size:15px"></pre><script>setInterval(async()=>{document.querySelector('#log').textContent=(await(await fetch('/results')).json()).slice(-5).join('\\n\\n')},150);${busy?'setTimeout(()=>{const end=Date.now()+6000;while(Date.now()<end){}},600);':''}</script></body></html>`);
  });
  server.on('connection',s=>{sockets.add(s);s.on('close',()=>sockets.delete(s));});
  await new Promise<void>(r=>server.listen(0,'127.0.0.1',r));
  const origin=`http://127.0.0.1:${(server.address() as any).port}`;
  try {
    const project=await t3.project('Preview evidence');
    await t3.thread({project,title:'Preview navigation proof',messages:['Start preview evidence'],model:'question'});
    await t3.open();
    await screen.getByRole('button','Preview navigation proof', {exact:false}).tap();
    let env: Record<string,string>|undefined;
    for(const pid of await readdir('/proc')) {
      if(!/^\d+$/.test(pid)) continue;
      try {
        const cmd=await readFile(`/proc/${pid}/cmdline`,'utf8');
        if(!cmd.includes(`${APP_DIR}/apps/server/scripts/dev-replay-agent.ts`)) continue;
        const entries=(await readFile(`/proc/${pid}/environ`,'utf8')).split('\0');
        env=Object.fromEntries(entries.map(e=>{const i=e.indexOf('=');return [e.slice(0,i),e.slice(i+1)]}));
        if(env.T3_ACP_MCP_AUTHORIZATION) break;
      } catch{}
    }
    if(!env?.T3_ACP_MCP_AUTHORIZATION) throw new Error('Replay MCP session environment unavailable');
    let session='';let id=0;
    const rpc=async(method:string,params:any)=>{
      const response=await fetch(env!.T3_ACP_MCP_ENDPOINT,{method:'POST',headers:{Authorization:env!.T3_ACP_MCP_AUTHORIZATION,'Content-Type':'application/json',Accept:'application/json, text/event-stream',...(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();
      const parsed=body.startsWith('event:')||body.startsWith('data:')?JSON.parse(body.split('\n').find(l=>l.startsWith('data:'))!.slice(5)):JSON.parse(body);
      if(parsed.error) throw new Error(JSON.stringify(parsed.error));return parsed.result;
    };
    await rpc('initialize',{protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'preview-proof',version:'1'}});
    const call=async(name:string,args:any)=>{
      const start=Date.now();const result=await rpc('tools/call',{name,arguments:args});
      const full=result.structuredContent; const value=full ? {url:full.url,title:full.title,loading:full.loading,viewport:full.viewport} : result.content;
      transcript.push(`${name} (${Date.now()-start} ms)\n${JSON.stringify(value)}${result.isError?'\nERROR':''}`);
      console.log(name,JSON.stringify(result)); await new Promise(r=>setTimeout(r,350)); return result;
    };
    const opened=await call('preview_open',{url:origin,open:true});
    const tabId=opened.structuredContent?.tabId;
    await browser.evaluate(() => { (document.querySelector('[aria-label="Open preview in right panel"]') as HTMLElement)?.click(); });
    await screen.getByRole('button','Maximize panel').tap();
    await app.screenshot('healthy-tab');
    const navigated=await call('preview_navigate',{tabId,url:origin+'/slow',timeoutMs:4000});
    await call('preview_status',{tabId});
    await app.screenshot('committed-slow-load');
    await call('preview_navigate',{tabId,url:origin,timeoutMs:4000});
    await app.screenshot('recovered');
    await call('preview_navigate',{tabId,url:origin+'/never',timeoutMs:1000});
    await call('preview_navigate',{tabId,url:origin+'/disconnect',timeoutMs:1000});
    await call('preview_navigate',{tabId,url:origin,timeoutMs:4000});
    await app.screenshot('navigation-errors-and-recovery');
    await call('preview_navigate',{tabId,url:origin+'/busy',readiness:'none',timeoutMs:1000});
    await new Promise(r=>setTimeout(r,1000));
    await call('preview_status',{tabId});
    await new Promise(r=>setTimeout(r,6500));
    await call('preview_navigate',{tabId,url:origin,timeoutMs:4000});
    await app.screenshot('busy-renderer-status');
    expect(navigated.isError??false).toBe(false);
    expect(navigated.structuredContent?.loading).toBe(true);
  } finally {for(const socket of sockets)socket.destroy();server.close();}
});
