import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as NodeServices from '@effect/platform-node/NodeServices';
import { EnvironmentId, ThreadId, ProviderInstanceId } from '@t3tools/contracts';
import * as Config from '/home/ubuntu/wt/pr-23537/apps/server/src/config.ts';
import * as Env from '/home/ubuntu/wt/pr-23537/apps/server/src/environment/ServerEnvironment.ts';
import * as Broker from '/home/ubuntu/wt/pr-23537/apps/server/src/mcp/PreviewAutomationBroker.ts';
import * as Manager from '/home/ubuntu/wt/pr-23537/apps/server/src/preview/Manager.ts';
import * as Browser from '/home/ubuntu/wt/pr-23537/apps/server/src/preview/ServerBrowser.ts';
import * as Contexts from '/home/ubuntu/wt/pr-23537/apps/server/src/preview/ServerBrowserContexts.ts';
import * as Preview from '/home/ubuntu/wt/pr-23537/apps/server/src/preview/PreviewBrowser.ts';
import * as Desktop from '/home/ubuntu/wt/pr-23537/apps/server/src/preview/DesktopBrowserChannel.ts';
import { execFileSync } from 'node:child_process';
import { performance } from 'node:perf_hooks';
import { createServer } from 'node:http';
const cwd='/home/ubuntu/wt/pr-23537';
const t0=performance.now();
const log=(event, data={})=>console.log(JSON.stringify({utc:new Date().toISOString(),ms:Math.round((performance.now()-t0)*1000)/1000,event,...data}));
const check=(condition,message)=>{log('assertion',{message,pass:!!condition});if(!condition)throw Error(message)};
const bounded=async(p,label,ms=14000)=>{let timer;try{return await Promise.race([p,new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error('TIMEOUT '+label)),ms)})])}finally{clearTimeout(timer)}};
const pages=[]; const seen=new WeakSet();let nextContext=0;
const original=Contexts.ServerBrowserContexts.prototype.contextFor;
Contexts.ServerBrowserContexts.prototype.contextFor=async function(...args){
 const context=await original.apply(this,args);
 if(!seen.has(context)){
  seen.add(context);const id=++nextContext;let contextClosed=false;
  const contextEnd=Promise.withResolvers();context.on('close',()=>{contextClosed=true;log('context.close.event',{context:id});contextEnd.resolve()});
  const close=context.close.bind(context);context.close=(...a)=>{log('context.close.call',{context:id,pages:context.pages().map(p=>({url:p.url(),closed:p.isClosed()}))});return close(...a)};
  const newPage=context.newPage.bind(context);context.newPage=async(...a)=>{
   const page=await newPage(...a); const item={page,context,id,contextEnd,contextClosed:()=>contextClosed,label:'unassigned',end:Promise.withResolvers(),dialog:Promise.withResolvers(),consoleWaiters:new Map()};pages.push(item);
   page.on('close',()=>{log('page.close.event',{label:item.label,context:id,contextClosed});item.end.resolve()});
   page.on('dialog',d=>{log('page.dialog.event',{label:item.label,type:d.type(),message:d.message()});item.dialog.resolve(d)});
   page.on('console',m=>{log('page.console',{label:item.label,text:m.text()});item.consoleWaiters.get(m.text())?.resolve()});
   const pageClose=page.close.bind(page);page.close=(...a)=>{log('page.close.call',{label:item.label,options:a[0]??null,isClosed:page.isClosed()});return pageClose(...a).then(v=>{log('page.close.resolved',{label:item.label,isClosed:page.isClosed()});return v})};
   return page;
  };
 }
 log('context.selected',{profile:args[0],isolationKey:args[1]??null});return context;
};
const environmentId=EnvironmentId.make('runtime-evidence-23537');const threadId=ThreadId.make('runtime-evidence-23537');
const scope={environmentId,thread:{threadId,providerSessionId:'evidence-agent',providerInstanceId:ProviderInstanceId.make('codex')},client:undefined,requestNamespace:'runtime-evidence',capabilities:new Set(['preview']),issuedAt:1};
const deps=Layer.mergeAll(Broker.layer,Manager.layer,Desktop.layer,
 Layer.succeed(Env.ServerEnvironment,{getEnvironmentId:Effect.succeed(environmentId),getDescriptor:Effect.die('unused service-harness descriptor')}),
 Layer.succeed(Preview.PreviewBrowser,{executable:Effect.succeed('/home/ubuntu/.t3/tools/chrome-headless-shell/linux64/154.0.8037.92/chrome-headless-shell'),installed:Effect.die('unused installation status')})
).pipe(Layer.provideMerge(Config.layerTest(cwd,'/tmp/pr23537-evidence/runtime/state')),Layer.provideMerge(NodeServices.layer));
const layer=Browser.layer.pipe(Layer.provideMerge(deps));
log('identity',{sha:execFileSync('git',['rev-parse','HEAD'],{cwd,encoding:'utf8'}).trim(),blob:execFileSync('git',['rev-parse','HEAD:apps/server/src/preview/ServerBrowser.ts'],{cwd,encoding:'utf8'}).trim(),node:process.version,pid:process.pid,platform:process.platform,arch:process.arch,sandbox:process.env.T3CODE_SERVER_BROWSER_SANDBOX,scope:'Real production ServerBrowser + Contexts + Broker + Manager layers; static harness environment identity/executable injection; no HTTP/WebSocket app, no mocks of pages/CDP/close/timers; observation wrappers forward unchanged.'});
const fixture=createServer((req,res)=>{res.setHeader('Content-Type','text/html');res.end('<title>runtime fixture</title><button>activate</button>')});
await new Promise(resolve=>fixture.listen(0,'127.0.0.1',resolve));
const origin='http://127.0.0.1:'+fixture.address().port;log('fixture.listen',{origin});
try{await Effect.runPromise(Effect.scoped(Effect.gen(function*(){
 yield* Browser.ServerBrowser; const broker=yield* Broker.PreviewAutomationBroker;const manager=yield* Manager.PreviewManager;yield* Effect.yieldNow;
 const run=Effect.runPromise;
 const open=async(label,profileId)=>{
  const count=pages.length;
  const result=await run(broker.invoke({scope,operation:'open',input:{url:origin+'/'+label,reuseExistingTab:false,show:false,...(profileId?{profileId}:{})}}));
  const item=pages[count];item.label=label;item.tabId=result.tabId;log('opened',{label,tabId:item.tabId,context:item.id,url:item.page.url()});return item;
 };
 const close=async(item)=>{log('manager.close.request',{label:item.label});const start=performance.now();await run(manager.close({threadId,tabId:item.tabId}));log('manager.close.return',{label:item.label,latencyMs:performance.now()-start,pageClosed:item.page.isClosed()});};
 const marker=(item,name)=>{const d=Promise.withResolvers();item.consoleWaiters.set(name,d);return bounded(d.promise,name)};
 yield* Effect.promise(async()=>{
 const sibling=await open('shared-sibling','default');await sibling.page.evaluate(()=>globalThis.siblingProof='alive');
 const pending=await open('pending-confirm','default');check(pending.context===sibling.context,'pending target shares sibling context');
 await pending.page.evaluate(()=>setTimeout(()=>confirm('leave this pending; do not answer'),0));await bounded(pending.dialog.promise,'confirm pending');
 const status=await run(broker.invoke({scope,tabId:pending.tabId,operation:'status',input:{}}));log('pending.status',{status});
 check(!pending.page.isClosed(),'pending confirm target still open before close');log('narration',{text:'No dialog accept/dismiss is invoked. Close through production manager now.'});
 await close(pending);await bounded(pending.end.promise,'pending page disappearance');check(!sibling.contextClosed()&&await sibling.page.evaluate(()=>globalThis.siblingProof)==='alive','sibling responsive after pending-dialog target closes');
 const delayed=await open('finite-delayed-unload');
 await delayed.page.evaluate(()=>{addEventListener('beforeunload',()=>{console.log('finite-unload-start');const end=performance.now()+1800;while(performance.now()<end){}console.log('finite-unload-end')})});
 const started=marker(delayed,'finite-unload-start');const ended=marker(delayed,'finite-unload-end');
 await close(delayed);await started;
 log('narration',{text:'Real synchronous finite 1800ms beforeunload handler started; not relying on async beforeunload promises.'});
 check(!delayed.page.isClosed()&&!delayed.contextClosed(),'isolated context and page remain open during finite unload');
 await ended;await bounded(delayed.end.promise,'finite page close');await bounded(delayed.contextEnd.promise,'isolated context disposal');
 check(delayed.page.isClosed()&&delayed.contextClosed(),'finite unload page closed and isolated context then disposed');
 const frozen=await open('shared-frozen','default');check(frozen.context===sibling.context,'frozen target shares sibling context');
 await frozen.page.evaluate(()=>{addEventListener('beforeunload',()=>{});setTimeout(()=>{console.log('freeze-start');for(;;){}},100)});
 await marker(frozen,'freeze-start');log('narration',{text:'Frozen renderer observed. Five seconds is configured grace before force-close attempt, not a completion bound.'});
 await close(frozen);check(!sibling.contextClosed()&&await sibling.page.evaluate(()=>globalThis.siblingProof)==='alive','sibling responsive while target frozen after close command');
 await bounded(frozen.end.promise,'shared frozen page disappearance',18000);
 check(!sibling.contextClosed()&&await sibling.page.evaluate(()=>globalThis.siblingProof)==='alive','sibling responsive after shared frozen fallback; shared context preserved');
 const external=await open('externally-closed','default');log('narration',{text:'Lower-priority external close: real CDP Target.closeTarget outside manager, then manager.close again.'});const session=await external.context.newCDPSession(external.page);const info=await session.send('Target.getTargetInfo');await session.send('Target.closeTarget',{targetId:info.targetInfo.targetId}).catch(e=>log('external.close.transport',{message:String(e)}));await bounded(external.end.promise,'external close');await close(external);check(await sibling.page.evaluate(()=>globalThis.siblingProof)==='alive','sibling remains responsive after externally closed cleanup');
 await close(sibling);await bounded(sibling.end.promise,'sibling cleanup');
 log('rows.complete',{faultInjection:'not attempted; no real close rejection proof',timing:'forwarded page.close calls and lifecycle events recorded; no hard completion bound claimed'});
});
})).pipe(Effect.provide(layer)))}catch(e){log('failure',{error:String(e),stack:e?.stack});process.exitCode=1}
finally{await new Promise(resolve=>fixture.close(resolve));log('scope.closed',{text:'Production scope finalizer awaited contexts.close; restores wrappers on process exit.'})}
