/** Live-provider UI proof. AI-assisted; verified with the tests and tools listed above.
 * Usage: PROOF_MODEL=<available Claude model slug> node reproduce.mjs <checkout> <output-directory>
 * Install checkout dependencies with `vp i` first. Requires Claude CLI authentication,
 * ffmpeg, and the repository's Playwright Chromium (install via scripts/node_modules/playwright-core/cli.js).
 * Starts dev with seeding disabled against a new, isolated T3 home on every run. Output is timestamped;
 * it never deletes earlier runs. The browser and any dev process it starts are closed on exit.
 */
import { spawn, execFileSync } from 'node:child_process';
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
import { resolve, join } from 'node:path';
import { pathToFileURL } from 'node:url';
import { setTimeout as delay } from 'node:timers/promises';
const [checkoutArg, outputArg] = process.argv.slice(2);
if (!checkoutArg || !outputArg || !process.env.PROOF_MODEL) throw Error('Supply checkout, output directory, and PROOF_MODEL');
const checkout=resolve(checkoutArg), out=join(resolve(outputArg),new Date().toISOString().replaceAll(':','-'));
mkdirSync(out,{recursive:true});
const home=join(out,'t3-home');
const env={...process.env,T3CODE_DEV_SEED:'0'};
const run=(cmd,args,cwd=checkout)=>execFileSync(cmd,args,{cwd,env,encoding:'utf8',stdio:['ignore','pipe','pipe'],maxBuffer:16*1024*1024});
const cli=(...args)=>run(process.execPath,['apps/server/src/bin.ts',...args,'--base-dir',home]);
const runtimePath=join(home,'userdata/server-runtime.json');
let dev, browser;
try {
 let runtime;
 if(existsSync(runtimePath)){try{runtime=JSON.parse(readFileSync(runtimePath));process.kill(runtime.pid,0);}catch{runtime=undefined;}}
 if(!runtime){
  dev=spawn('vp',['run','dev','--home-dir',home],{cwd:checkout,env,detached:true,stdio:['ignore','pipe','pipe']});
  let log='';dev.stdout.on('data',d=>log+=d);dev.stderr.on('data',d=>log+=d);
  for(let i=0;i<180;i++){
   await delay(1000);
   if(log.includes('pairingUrl:')&&existsSync(runtimePath)){runtime=JSON.parse(readFileSync(runtimePath));break;}
   if(dev.exitCode!==null)throw Error('Dev server exited');
  }
  if(!runtime)throw Error('Dev server did not start');
 }
 const workspace=join(out,'sample');mkdirSync(workspace);
 writeFileSync(join(workspace,'math.ts'),'export function add(a: number, b: number) {\n  return a + b;\n}\n\n\nexport function mean(values: number[]) {\n  return values.reduce(add, 0) / values.length;\n}\n');
 run('git',['init','-q','--initial-branch=main'],workspace);
 run('git',['add','.'],workspace);
 run('git',['-c','user.name=Evidence','-c','user.email=evidence@example.invalid','commit','-qm','Sample'],workspace);
 cli('project','add',workspace,'--title','Subagent summary');
 const prompt='Use a subagent (Agent tool, foreground, not background). Give it exactly this prompt, without adding an absolute path: "Read math.ts in the current working directory and list the functions with their line numbers. Return only add (line 1) and mean (line 6), one per line, after verifying the file. Do not include paths or extra commentary." Then tell me what it said, without paths.';
 const turn=JSON.parse(cli('thread','start',workspace,prompt,'--title','Function report','--provider','claudeAgent','--model',process.env.PROOF_MODEL,'--wait','--json'));
 if(turn.status!=='completed')throw Error('Turn did not complete');
 // Keep model identifiers and pairing credentials out of the public transcript.
 writeFileSync(join(out,'turn.json'),JSON.stringify({status:turn.status,prompt,reply:turn.reply},null,2));
 const pair=cli('pair');
 const pairing=pair.match(/https?:\/\/[^\s]+\/pair#[^\s]+/)?.[0];
 if(!pairing)throw Error('Pairing URL missing');
 const {chromium}=await import(pathToFileURL(join(checkout,'scripts/node_modules/playwright-core/index.mjs')));
 browser=await chromium.launch({headless:true,args:['--no-sandbox','--disable-gpu']});
 const context=await browser.newContext({viewport:{width:1280,height:1000},deviceScaleFactor:1,colorScheme:'light',recordVideo:{dir:join(out,'raw-video'),size:{width:1280,height:1000}}});
 const page=await context.newPage();page.setDefaultTimeout(120000);
 await page.goto(pairing);
 const threadRow=page.locator('[data-sidebar-row$='+JSON.stringify(':'+turn.threadId)+']');
 await threadRow.waitFor();
 await threadRow.click();
 const work=page.getByRole('button',{name:/^Worked for/});await work.waitFor();
 const details=page.getByRole('button',{name:'Toggle thread details panel',exact:true});
 if(await details.getAttribute('aria-pressed')==='true')await details.click();
 const sidebar=page.getByRole('button',{name:'Toggle main sidebar',exact:true});
 if(await sidebar.getAttribute('aria-pressed')==='true')await sidebar.click();
 if(await work.getAttribute('aria-expanded')==='true')await work.click();
 await delay(1000);
 const clip={x:0,y:0,width:1280,height:800};
 const stable=async(label)=>{
  let a=await page.screenshot({clip});
  for(let i=0;i<20;i++){
   await delay(500);const b=await page.screenshot({clip});
   if(a.equals(b)){writeFileSync(join(out,label+'.png'),b);return;}
   a=b;
  }
  throw Error('Capture did not settle: '+label);
 };
 // Video time is relative to the actual recorded frame stream; use the first browser navigation timestamp.
 const start=Date.now();
 await stable('collapsed');
 await work.click();
 await page.getByRole('button',{name:/^Open .*math\.ts/}).waitFor();
 await stable('row');
 const rowText=await page.getByRole('button',{name:/^Open .*math\.ts/}).innerText();
 await delay(1000);
 await page.getByRole('button',{name:/^Open .*math\.ts/}).click();
 await page.getByRole('button',{name:'Open parent',exact:true}).waitFor();
 if(await details.getAttribute('aria-pressed')==='true')await details.click();
 await stable('child');
 const childText=await page.getByRole('region',{name:'Conversation',exact:true}).innerText();
 await delay(1500);
 const end=Date.now();
 const video=page.video();await context.close();
 const videoPath=await video.path();
 const duration=Number(run('ffprobe',['-v','error','-show_entries','format=duration','-of','default=nw=1:nk=1',videoPath]));
 const seconds=(end-start)/1000;
 run('ffmpeg',['-y','-i',videoPath,'-ss',String(Math.max(0,duration-seconds-0.3)),'-t',String(seconds),'-vf','crop=1280:800:0:0','-an','-c:v','libx264','-pix_fmt','yuv420p',join(out,'flow.mp4')]);
 const sha=run('git',['rev-parse','HEAD']).trim();
 writeFileSync(join(out,'observation.json'),JSON.stringify({sha,viewport:'1280x1000',theme:'light',crop:clip,rowText,childText,hasRawTag:rowText.includes('<tools-used-before-summary>'),childHasToolNote:/\n(?:Bash|Read|Bash, Read)\n/.test(childText),videoSeconds:seconds},null,2));
 console.log(out);
}finally{
 if(browser)await browser.close();
 if(dev)process.kill(-dev.pid,'SIGTERM');
}
