import { EnvironmentId, ThreadId, RunId, MessageId, RuntimeRequestId, OrchestrationV2ThreadShell } from '@t3tools/contracts';
import * as DateTime from 'effect/DateTime';
import * as Option from 'effect/Option';
import * as Schema from 'effect/Schema';
import { Atom, AtomRegistry } from 'effect/reactivity';
import { it, expect } from 'vite-plus/test';
import { PrimaryConnectionTarget } from '../connection/model.ts';
import { v2Now, v2ShellSnapshot, v2ThreadShell } from './orchestrationV2TestFixtures.ts';
import { createEnvironmentThreadShellAtoms } from './threadShell.ts';
import { writeFileSync } from 'node:fs';

it('records navigation selector evidence', () => {
 const e1=EnvironmentId.make('capture-e1'), e2=EnvironmentId.make('capture-e2');
 const queue: Array<()=>void>=[];
 const registry=AtomRegistry.make({scheduleTask: f=>{queue.push(f);return ()=>{const i=queue.indexOf(f);if(i>=0)queue.splice(i,1)}}});
 const flush=()=>{while(queue.length)queue.shift()!()};
 const snapshotAtom=Atom.family((_id: EnvironmentId)=>Atom.make(v2ShellSnapshot));
 const catalogValueAtom=Atom.make({isReady:true,entries:new Map([e1,e2].map(environmentId=>[environmentId,{target:new PrimaryConnectionTarget({environmentId,label:'Capture',httpBaseUrl:'https://example.test',wsBaseUrl:'wss://example.test'}),profile:Option.none(),enabled:true}]))});
 const atoms=createEnvironmentThreadShellAtoms({snapshotAtom,catalogValueAtom});
 const nav=atoms.navigationThreadShellsAtom;
 const live=atoms.threadShellAtom({environmentId:e1,threadId:v2ThreadShell.id});
 const run=RunId.make('capture-run');
 const running={...v2ThreadShell,latestRunId:run,activeRunId:run,status:'running' as const,itemCount:1,visibleItemCount:1};
 const idle={...v2ThreadShell,id:ThreadId.make('idle')};
 let seq=0, notifications=0, liveNotifications=0;
 const decode=Schema.decodeUnknownSync(Schema.toType(OrchestrationV2ThreadShell));
 const decoded=new WeakMap<object,OrchestrationV2ThreadShell>();
 const valid=(row:OrchestrationV2ThreadShell)=>{let result=decoded.get(row);if(!result){result=decode(row);decoded.set(row,result)}return result};
 const set=(rows: readonly OrchestrationV2ThreadShell[], env=e1)=>{registry.set(snapshotAtom(env),{...v2ShellSnapshot,snapshotSequence:++seq,threads:rows.map(valid)});registry.get(nav);registry.get(live);flush()};
 registry.mount(snapshotAtom(e1));registry.mount(snapshotAtom(e2));set([running,idle]);set([],e2);
 const unsub=registry.subscribe(nav,()=>notifications++);const unlive=registry.subscribe(live,()=>liveNotifications++);flush();
 const out:any[]=[];
 const record=(label:string, from:readonly OrchestrationV2ThreadShell[], to:readonly OrchestrationV2ThreadShell[], check:(rows:any)=>void, expected=1)=>{
   set(from);const before=registry.get(nav);notifications=0;set(to);const after=registry.get(nav);check(after);expect(notifications).toBe(expected);out.push({label,events:1,notifications,listReplacements:Number(before!==after),correct:true});
 };
 for(const mode of ['updatedAt','itemCount','visibleItemCount','message','all']){
  set([running,idle]);notifications=0;liveNotifications=0;let prior=registry.get(nav),list=0,a=0,b=0;
  for(let tick=1;tick<=100;tick++){
   const updatedAt=DateTime.add(v2Now,{seconds:tick});
   set([{...running,...(['updatedAt','all'].includes(mode)?{updatedAt}:{}),...(['itemCount','all'].includes(mode)?{itemCount:tick+1}:{}),...(['visibleItemCount','all'].includes(mode)?{visibleItemCount:tick+1}:{}),...(['message','all'].includes(mode)?{latestVisibleMessage:{id:MessageId.make('stream'),role:'assistant' as const,text:`Chunk ${tick}`,updatedAt}}:{})},idle]);
   const next=registry.get(nav);list+=Number(next!==prior);a+=Number(next[0]!==prior[0]);b+=Number(next[1]!==prior[1]);prior=next;
  }
  const current=registry.get(live)!;expect(liveNotifications).toBe(100);
  out.push({label:`A01 pure ${mode}`,events:100,notifications,listReplacements:list,activeRowReplacements:a,idleRowReplacements:b,liveNotifications,liveItemCount:current.source.itemCount,liveText:current.source.latestVisibleMessage?.text??null});
 }
 const field=(label:string,patch:Partial<OrchestrationV2ThreadShell>,key:keyof OrchestrationV2ThreadShell)=>record(label,[running,idle],[{...running,...patch},idle],rows=>expect(rows[0].source[key]).toEqual(patch[key]));
 field('A05 rename',{title:'Garden renamed'},'title');field('A05 branch',{branch:'garden'},'branch');
 for(const status of ['preparing','queued','starting','waiting'] as const)field(`A06 status ${status}`,{status},'status');
 field('A06 activity override',{activityRunStatus:'waiting'},'activityRunStatus');
 field('A06 blocked on thread wait',{blockedOnThreadWait:true},'blockedOnThreadWait');
 for(const kind of ['command','user_input','auth_refresh'] as const){const request={id:RuntimeRequestId.make(kind),kind,createdAt:v2Now};field(`A07/A08 ${kind} set`,{pendingRuntimeRequest:request},'pendingRuntimeRequest');record(`A07/A08 ${kind} clear`,[{...running,pendingRuntimeRequest:request},idle],[running,idle],rows=>expect(rows[0].source.pendingRuntimeRequest).toBeNull())}
 const child={...running,id:ThreadId.make('child'),creationSource:'mcp' as const,lineage:{...running.lineage,parentThreadId:running.id,relationshipToParent:'subagent' as const}};
 for(const kind of ['command','user_input'] as const){const asking={...child,pendingRuntimeRequest:{id:RuntimeRequestId.make(kind),kind,createdAt:v2Now}};record(`A09 child ${kind} set`,[running,idle,child],[running,idle,asking],rows=>{expect(rows).toHaveLength(2);expect(rows[0].pendingSubagentRequest).toBe(kind==='command'?'approval':'user_input')});record(`A09 child ${kind} clear`,[running,idle,asking],[running,idle,child],rows=>expect(rows[0].pendingSubagentRequest).toBeNull())}
 field('A10 pin',{pinnedAt:v2Now},'pinnedAt');field('A10 active order',{activeOrderKey:'a1'},'activeOrderKey');field('A10 snooze',{snoozedAt:v2Now,snoozedUntil:DateTime.add(v2Now,{hours:1})},'snoozedUntil');
 field('A10 settle',{settledOverride:"settled",settledAt:v2Now},'settledOverride');field('A10 visit',{lastVisitedAt:DateTime.add(v2Now,{seconds:2})},'lastVisitedAt');
 field('A11 title search source',{title:'Search garden'},'title');
 // A12 and A13 consume this same selector; no PR payload or imperative destination claim.
 const preceding={...idle,id:ThreadId.make('preceding')};
 record('A14 insert preceding',[running,idle],[preceding,running,idle],rows=>expect(rows.map((r:any)=>r.id)).toEqual([preceding.id,running.id,idle.id]));
 record('A14 remove preceding',[preceding,running,idle],[running,idle],rows=>expect(rows.map((r:any)=>r.id)).toEqual([running.id,idle.id]));
 record('A14 reorder preceding',[preceding,running,idle],[running,preceding,idle],rows=>expect(rows.map((r:any)=>r.id)).toEqual([running.id,preceding.id,idle.id]));
 record('A14 archive',[running,idle],[{...running,archivedAt:v2Now},idle],rows=>expect(rows.map((r:any)=>r.id)).toEqual([idle.id]));
 set([running,idle]);set([running],e2);const both=registry.get(nav);expect(both.filter(r=>r.id===running.id).map(r=>r.environmentId)).toEqual([e1,e2]);const e2Row=both[2];notifications=0;set([{...running,title:'Only E1'},idle]);expect(registry.get(nav)[2]).toBe(e2Row);expect(notifications).toBe(1);out.push({label:'A14 same ID, two environments',events:1,notifications,correct:true});set([],e2);
 for(const [from,to] of [[[0,0],[1,0]],[[1,0],[1,1]],[[1,1],[0,1]],[[1,1],[1,0]],[[0,1],[1,1]],[[1,0],[1,1]]] as const){record(`A15 counts ${from} -> ${to}`,[{...running,itemCount:from[0],visibleItemCount:from[1]}],[{...running,itemCount:to[0],visibleItemCount:to[1]}],rows=>expect([rows[0].source.itemCount,rows[0].source.visibleItemCount]).toEqual(to))}
 field('A06 background holding',{pendingBackgroundTasks:[{taskId:'background',kind:'command',awaited:true}]},'pendingBackgroundTasks');
 field('A06 active goal',{goal:{objective:'Garden goal',status:'active',tokenBudget:1000,tokensUsed:1}},'goal');
 for(const status of ['completed','interrupted','failed','rolled_back','idle'] as const){const stopped={...running,status,activeRunId:null,activityRunStatus:null};record(`A16 stop ${status}`,[running],[stopped],rows=>expect(rows[0].source.status).toBe(status));for(const [name,patch] of [['timestamp',{updatedAt:DateTime.add(v2Now,{seconds:3})}],['count',{itemCount:4}],['message',{latestVisibleMessage:{id:MessageId.make('terminal'),role:'assistant' as const,text:'fresh terminal',updatedAt:v2Now}}]] as const)record(`A16 ${status} post-stop ${name}`,[stopped],[{...stopped,...patch}],rows=>expect(rows[0].source).toEqual({...stopped,...patch}))}
 unsub();unlive();registry.dispose();
 writeFileSync(process.env.CAPTURE_OUTPUT!,JSON.stringify({sha:process.env.CAPTURE_SHA,metric:'actual subscription callbacks and per-event identities; synchronous custom scheduler drain',results:out},null,2));
 console.log(JSON.stringify(out));
});
