import * as DateTime from 'effect/DateTime';
import * as Option from 'effect/Option';
import { AtomRegistry } from 'effect/reactivity';
import { startThreadNotifications } from 'f4-coordinator';
import { shellAtom, idsAtom, enableOutlets } from 'f4-state';
declare const __SIDE__: 'before'|'after';
declare const __SHA__: string;
const el = (id:string) => document.getElementById(id)!;
const notes:any[]=[]; const events:any[]=[]; let epoch=0;
function log(message:string, kind='') {
 const elapsed=(performance.now()-epoch)/1000, at=new Date().toISOString();
 events.push({elapsed,at,message}); const line=document.createElement('div');line.className=kind;
 line.textContent=`t=${elapsed.toFixed(3).padStart(6)}s  ${message}`;el('log').append(line);while(el('log').children.length>11)el('log').firstElementChild!.remove();
}
class RecordingNotification {
 static permission='granted';static requestPermission=async()=>'granted';tag:string;
 constructor(title:string,options?:NotificationOptions){
  notes.push({title,body:options?.body,at:new Date().toISOString(),elapsed:(performance.now()-epoch)/1000});
  this.tag=options?.tag??'';el('count').textContent=String(notes.length);log(`Notification(${JSON.stringify(options?.body)}) #${notes.length}`,'alert');
 }
 close(){} addEventListener(){}
}
Object.assign(window,{Notification:RecordingNotification});Document.prototype.hasFocus=()=>false;
const now=DateTime.makeUnsafe('2026-11-10T12:00:00.000Z');
function thread(id:string,status='running',run='wake',extra={}) {
 return {id,projectId:'synthetic-project',title:id,providerInstanceId:'synthetic',modelSelection:{instanceId:'synthetic',model:'synthetic'},runtimeMode:'full-access',interactionMode:'default',branch:null,worktreePath:null,activeProviderThreadId:null,lineage:{rootThreadId:id,parentThreadId:null,relationshipToParent:null},forkedFrom:null,createdBy:'user',creationSource:'web',latestRunId:`${id}-${run}`,activeRunId:status==='running'?`${id}-${run}`:null,status,latestRunBackgroundWake:run==='wake',pendingBackgroundTasks:[],pendingRuntimeRequest:null,latestVisibleMessage:null,latestUserMessageAt:null,hasActionableProposedPlan:false,itemCount:0,visibleItemCount:0,createdAt:now,updatedAt:now,latestRunRequestedAt:now,latestRunStartedAt:now,latestRunCompletedAt:status==='completed'?DateTime.makeUnsafe(run==='wake'?'2026-11-10T12:00:01.000Z':'2026-11-10T12:00:02.000Z'):null,archivedAt:null,settledOverride:null,settledAt:null,lastVisitedAt:null,deletedAt:null,...extra};
}
const cases = new Map<string,any>();
function feed(id:string,status='running',run='wake',extra={},fresh='live'){
 const c=cases.get(id); const value=thread(id,status,run,extra);
 c.registry.set(shellAtom,{status:fresh,snapshot:Option.some({threads:[value]})});
}
const router={state:{matches:[{params:{}}]},navigate:async()=>{}};
function create(id:string){
 const registry=AtomRegistry.make();registry.set(shellAtom,{status:'live',snapshot:Option.some({threads:[thread(id)]})});
 const c={registry,stop:startThreadNotifications(registry,router as never)};cases.set(id,c);
}
function schedule(t:number,fn:()=>void){setTimeout(fn,t*1000)}
const start=el('start') as HTMLButtonElement;start.disabled=false;start.textContent='Start F8–F10 sequence';
el('environment').textContent=`${navigator.userAgent} | DPR ${devicePixelRatio} | ${innerWidth}×${innerHeight} CSS px | notifications only | focus=false`;
start.onclick=()=>{
 epoch=performance.now();el('result').textContent='Observation running — timestamps record shell events and actual Notification() calls.';start.disabled=true;start.textContent='Sequence running';
 const clock=setInterval(()=>el('clock').textContent=`${((performance.now()-epoch)/1000).toFixed(1)} s`,100);
 el('status').textContent='F8 · same completion';create('F8 same completion');log('F8 live RUNNING; initial sighting','phase');
 schedule(1,()=>{log('F8 live COMPLETED progress wake; AFTER grace armed (deadline 6s)');feed('F8 same completion','completed')});
 schedule(2,()=>{log('F8 CACHED / disconnected; remain offline past original 6s deadline');feed('F8 same completion','completed','wake',{},'cached')});
 schedule(8,()=>{log('F8 LIVE same completion; AFTER restarts full 5s grace (deadline 13s)');feed('F8 same completion','completed')});
 schedule(10,()=>{log('F8 unchanged LIVE snapshot; 13s deadline must stay');feed('F8 same completion','completed')});
 schedule(14,()=>log('F8 checkpoint: observe one alert total; none while CACHED','phase'));
 schedule(15,()=>{el('status').textContent='F9 · successor';create('F9 successor');log('F9 live RUNNING; distinct thread and run','phase')});
 schedule(16,()=>{log('F9 old progress wake COMPLETED; AFTER grace armed (deadline 21s)');feed('F9 successor','completed')});
 schedule(17,()=>{log('F9 CACHED / disconnected; remain offline past 21s deadline');feed('F9 successor','completed','wake',{},'cached')});
 schedule(23,()=>{log('F9 LIVE successor RUNNING; old completion must be discarded');feed('F9 successor','running','terminal')});
 schedule(24,()=>{log('F9 terminal successor COMPLETED; immediate allowed-completion control');feed('F9 successor','completed','terminal')});
 schedule(30,()=>log('F9 checkpoint: successor only AFTER; no delayed old alert','phase'));
 const names=['archive','stop','thread removal','cleanup','environment removal'];
 schedule(31,()=>{el('status').textContent='F10 · cancel five graces';for(const name of names)create(`F10 ${name}`);log('F10 five independent RUNNING threads; own coordinator/registry each','phase')});
 schedule(32,()=>{log('F10 all five progress wakes COMPLETED; AFTER timers armed (deadline 37s)');for(const name of names)feed(`F10 ${name}`,'completed')});
 schedule(33,()=>{
  log('F10 archive + stop + thread removal + cleanup + environment removal');
  feed('F10 archive','completed','wake',{archivedAt:now});feed('F10 stop','completed','wake',{latestRunStopRequested:true});
  cases.get('F10 thread removal').registry.set(shellAtom,{status:'live',snapshot:Option.some({threads:[]})});
  cases.get('F10 cleanup').stop();cases.get('F10 environment removal').registry.set(idsAtom,[]);
 });
 schedule(39,()=>{
  log('F10 restore all completed snapshots after old deadline; re-enable/re-add must not replay');
  for(const name of names)feed(`F10 ${name}`,'completed');cases.get('F10 environment removal').registry.set(idsAtom,['synthetic-environment']);
  const c=cases.get('F10 cleanup');c.stop=startThreadNotifications(c.registry,router as never);
 });
 schedule(46,()=>log('F10 checkpoint: no delayed alert or restore replay through two grace windows','phase'));
 schedule(47,()=>{el('status').textContent='F10 · disable outlets';create('F10 disable outlets');log('F10 outlets control RUNNING; isolated from the five finished cancellation cases','phase')});
 schedule(48,()=>{log('F10 outlets progress wake COMPLETED; AFTER grace deadline 53s');feed('F10 disable outlets','completed')});
 schedule(49,()=>{log('F10 disable notifications AND in-app notifications; clears armed timer');enableOutlets(false)});
 schedule(55,()=>{log('F10 re-enable outlets with same completion; no replay');enableOutlets(true)});
 schedule(62,()=>{
  const expected=__SIDE__==='before'?[1,16,24,32,32,32,32,32,48]:[13,24];
  const pass=notes.length===expected.length&&notes.every((n,i)=>Math.abs(n.elapsed-expected[i])<0.8);
  log(`END: ${notes.length} calls at ${notes.map(n=>n.elapsed.toFixed(3)+'s').join(', ')}; ${pass?'PASS':'FAIL'}`,pass?'pass':'alert');
  el('result').textContent=pass?`PASS — ${__SIDE__} expected calls and timings; inspect row timestamps above`:'FAIL — inspect timestamps';el('result').className=pass?'pass':'alert';
  clearInterval(clock);el('clock').textContent='62.0 s';start.textContent='Observation complete';
  for(const c of cases.values()){c.stop();c.registry.dispose()}
  Object.assign(window,{__done:true,__pass:pass,__result:{side:__SIDE__,sha:__SHA__,notes,events,viewport:{width:innerWidth,height:innerHeight,dpr:devicePixelRatio},userAgent:navigator.userAgent}});
 });
};
