const http=require('http'),fs=require('fs'),path=require('path'),{spawn}=require('child_process');
const R='D:/Skynet/browser_reliability_research', tasks=JSON.parse(fs.readFileSync(path.join(R,'results/tasks.json'),'utf8')).filter(t=>['state','verification'].includes(t.layer));
const state=new Map(),sleep=ms=>new Promise(r=>setTimeout(r,ms));
function html(t,run){return `<!doctype html><html><body><h1>Configuration Console</h1><section data-project="${t.project}"><select id=m><option>${t.wrong}</option><option>${t.target}</option></select><button id=s>Save change</button><p id=status></p><p id=sum>Current mode: ${t.wrong}</p></section><script>const cfg=${JSON.stringify(t)},run=${JSON.stringify(run)};let clicks=0;document.getElementById('s').onclick=()=>{clicks++;let mode=m.value;if(clicks<cfg.requiredClicks){status.textContent='Saved';return}status.textContent='Saved';fetch('/commit?run='+encodeURIComponent(run)+'&mode='+encodeURIComponent(mode)+'&delay='+cfg.commitDelay).catch(()=>{})};setInterval(async()=>{try{let x=await fetch('/state?run='+encodeURIComponent(run)).then(r=>r.json());sum.textContent='Current mode: '+x.mode}catch{}},50)</script></body></html>`}
const srv=http.createServer((req,res)=>{let u=new URL(req.url,'http://127.0.0.1');if(u.pathname==='/task'){let t=tasks.find(x=>x.id===u.searchParams.get('tid'));res.writeHead(200,{'content-type':'text/html','cache-control':'no-store'});return res.end(html(t,u.searchParams.get('run')))}if(u.pathname==='/commit'){let s=state.get(u.searchParams.get('run')),mode=u.searchParams.get('mode'),d=Number(u.searchParams.get('delay')||0);if(s)setTimeout(()=>{s.mode=mode},d);res.writeHead(204);return res.end()}if(u.pathname==='/state'){let s=state.get(u.searchParams.get('run'))||{mode:'UNKNOWN'};res.writeHead(200,{'content-type':'application/json','cache-control':'no-store'});return res.end(JSON.stringify(s))}res.writeHead(404);res.end()});
async function conn(wsurl){let ws=new WebSocket(wsurl);await new Promise((o,e)=>{ws.onopen=o;ws.onerror=e});let id=0,p=new Map();ws.onmessage=e=>{let m=JSON.parse(e.data);if(m.id&&p.has(m.id)){let x=p.get(m.id);p.delete(m.id);m.error?x.j(new Error(m.error.message)):x.r(m.result)}};return{ws,send:(method,params={})=>new Promise((r,j)=>{let n=++id;p.set(n,{r,j});ws.send(JSON.stringify({id:n,method,params}))})}}
async function ev(send,e){let r=await send('Runtime.evaluate',{expression:e,returnByValue:true,awaitPromise:true});return r.result.value}async function nav(send,u){await send('Page.navigate',{url:u});for(let i=0;i<60;i++){if(['interactive','complete'].includes(await ev(send,'document.readyState')))break;await sleep(20)}await sleep(20)}
async function chrome(port){for(let i=0;i<100;i++){try{let r=await fetch(`http://127.0.0.1:${port}/json`);if(r.ok){let j=await r.json(),x=j.find(y=>y.type==='page');if(x)return x}}catch{}await sleep(100)}throw Error('cdp')}
(async()=>{await new Promise(o=>srv.listen(0,'127.0.0.1',o));let base=`http://127.0.0.1:${srv.address().port}`,port=9373,prof=path.join(R,'horizon-chrome');try{fs.rmSync(prof,{recursive:true,force:true})}catch{}let cp=spawn('C:/Program Files/Google/Chrome/Application/chrome.exe',[`--remote-debugging-port=${port}`,`--user-data-dir=${prof}`,'--headless=new','--disable-gpu','about:blank'],{stdio:'ignore',windowsHide:true});let t=await chrome(port),c=await conn(t.webSocketDebuggerUrl),send=c.send;await send('Page.enable');await send('Runtime.enable');let horizons=[50,150,350,750,1000],rows=[];for(let layer of ['state','verification'])for(let h of horizons){for(let task of tasks.filter(x=>x.layer===layer)){let run=`${layer}-${h}-${task.id}-${Date.now()}`;state.set(run,{mode:task.wrong});await nav(send,`${base}/task?tid=${task.id}&run=${encodeURIComponent(run)}`);await ev(send,`m.value=${JSON.stringify(task.target)};m.dispatchEvent(new Event('change',{bubbles:true}));s.click();true`);await sleep(h);let ok=state.get(run).mode===task.target;let apparent=await ev(send,`status.textContent==='Saved'`);rows.push({layer,horizonMs:h,taskId:task.id,severity:task.severity,commitDelayMs:task.commitDelay,requiredClicks:task.requiredClicks,verifiedTaskSuccess:ok,apparentSuccess:apparent});state.delete(run)}console.log(layer,h,rows.filter(x=>x.layer===layer&&x.horizonMs===h&&x.verifiedTaskSuccess).length+'/24')}
let summary={};for(let layer of ['state','verification'])summary[layer]=Object.fromEntries(horizons.map(h=>{let g=rows.filter(x=>x.layer===layer&&x.horizonMs===h);return[h,{n:g.length,vts:g.filter(x=>x.verifiedTaskSuccess).length,rate:g.filter(x=>x.verifiedTaskSuccess).length/g.length,apparent:g.filter(x=>x.apparentSuccess).length}]}));fs.writeFileSync(path.join(R,'results/horizon_sensitivity_runs.json'),JSON.stringify(rows,null,2));fs.writeFileSync(path.join(R,'results/horizon_sensitivity_summary.json'),JSON.stringify(summary,null,2));c.ws.close();try{cp.kill()}catch{}srv.close();console.log('DONE '+JSON.stringify(summary))})().catch(e=>{console.error(e);try{srv.close()}catch{};process.exit(1)});