import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import * as net from 'node:net';
import * as crypto from 'node:crypto';
import {execFileSync} from 'node:child_process';
import {it,expect,vi} from 'vitest';
import * as Semaphore from 'effect/Semaphore';
import * as Tracer from 'effect/Tracer';
import * as NodeServices from '@effect/platform-node/NodeServices';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as Fiber from 'effect/Fiber';
import * as Exit from 'effect/Exit';
import * as Scope from 'effect/Scope';
import * as Context from 'effect/Context';
import {ChildProcessSpawner, ChildProcess} from 'effect/process';
import * as ServerConfig from '../config.ts';
import * as GitVcsDriver from './GitVcsDriver.ts';

const scratch='/tmp/lead-26601-r1/capture-r12';
const helper=path.join(scratch,'fixture26601.py');
const sha=execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();
const group='R12-recapture';
const root=path.join(scratch,`${new Date().toISOString().replaceAll(':','-')}-${sha.slice(0,10)}-${group}`);
fs.mkdirSync(root,{recursive:true});
const mono=process.hrtime.bigint();
const events:any[]=[];
const waiters=new Set<{predicate:(e:any)=>boolean,resolve:(e:any)=>void}>();
const summaries:any[]=[];
const now=()=>Number(process.hrtime.bigint()-mono)/1e6;
function log(kind:string,data:any={}) {const e={...data,ms:+now().toFixed(3),kind};events.push(e);fs.appendFileSync(path.join(root,'events.jsonl'),JSON.stringify(e)+'\n');console.log(JSON.stringify(e));for(const w of waiters)if(w.predicate(e)){waiters.delete(w);w.resolve(e);}return e;}
function wait(predicate:(e:any)=>boolean){const e=events.find(predicate);if(e)return Promise.resolve(e);return new Promise<any>(resolve=>waiters.add({predicate,resolve}));}
const ready=(label:string)=>Effect.promise(()=>wait(e=>e.kind==='READY'&&e.label===label));
const qu=(s:string)=>"'"+s.replaceAll("'","'\\''")+"'";
const socketPath=path.join(root,'ipc.sock');
const server=net.createServer();
function git(cwd:string,args:string[]){return execFileSync('git',args,{cwd,env:{...process.env,GIT_CONFIG_GLOBAL:'/dev/null',GIT_AUTHOR_DATE:'2026-01-01T00:00:00Z',GIT_COMMITTER_DATE:'2026-01-01T00:00:00Z'},encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim();}
function repo(label:string){const cwd=path.join(root,label);fs.mkdirSync(cwd,{recursive:true});git(cwd,['init','-b','main']);git(cwd,['config','user.name','Capture']);git(cwd,['config','user.email','capture@example.invalid']);fs.writeFileSync(path.join(cwd,'tracked'),'seed\n');git(cwd,['add','.']);git(cwd,['commit','-m','synthetic seed']);return cwd;}
function alive(pid:number){try{const raw=fs.readFileSync(`/proc/${pid}/stat`,'utf8');return raw.slice(raw.lastIndexOf(')')+2).split(' ')[0]!=='Z';}catch{return false;}}
function snapshot(label:string,prefix=label){const pids=[...new Set(events.filter(e=>e.source==='fixture'&&(e.label===prefix||e.label?.startsWith(prefix+'.'))).map(e=>e.pid))];const live=pids.filter(alive);const markers=fs.readdirSync(root).filter(x=>x.endsWith('.marker')&&(x===prefix+'.marker'||x.startsWith(prefix+'.')));return log('SNAPSHOT',{label,pids,live,liveCount:live.length,markers,markerCount:markers.length});}
function summary(row:string,variant:string,data:any){const e={row,variant,...data};summaries.push(e);log('RESULT',e);fs.writeFileSync(path.join(root,'results.json'),JSON.stringify(summaries,null,2));}
const safe=(e:any)=>e&&({_tag:e._tag,operation:e.operation,detail:e.detail,reason:e.reason,exitCode:e.exitCode,stdoutLength:e.stdoutLength,stderrLength:e.stderrLength});
function measure(label:string,effect:any){return Effect.gen(function*(){const start=now();log('REQUEST',{label});const result=yield* Effect.result(effect);const elapsed=now()-start;log('RETURN',{label,elapsedMs:+elapsed.toFixed(3),result:result._tag==='Success'?result.success:safe(result.failure)});return {elapsedMs:+elapsed.toFixed(3),ok:result._tag==='Success',result:result._tag==='Success'?result.success:safe(result.failure)};});}
const rawKill=process.kill.bind(process);
const signals:any[]=[];
function instrumentKill(){process.kill=((pid:number,signal:any)=>{if(signal!==0){const e=log('SIGNAL',{pid,signal});signals.push(e);}return rawKill(pid,signal);}) as typeof process.kill;}
function layer(label:string){const observe=Layer.effect(ChildProcessSpawner.ChildProcessSpawner,Effect.gen(function*(){const native=yield* ChildProcessSpawner.ChildProcessSpawner;return ChildProcessSpawner.make(command=>Effect.gen(function*(){if(!ChildProcess.isStandardCommand(command))return yield* native.spawn(command);const id=events.length;const cwd=command.options.cwd;const args=command.args;const start=now();log('SPAWN_REQUEST',{service:label,id,cwd,args,options:{forceKillAfter:command.options.forceKillAfter,detached:command.options.detached},nativeDelegation:true});yield* Effect.addFinalizer(()=>Effect.sync(()=>log('FINALIZED',{service:label,id,cwd,args,elapsedMs:+(now()-start).toFixed(3)})));const handle=yield* native.spawn(command);log('SPAWNED',{service:label,id,pid:handle.pid,cwd,args});return handle;}));})).pipe(Layer.provide(NodeServices.layer));return GitVcsDriver.layer.pipe(Layer.provide(ServerConfig.layerTest(root,path.join(root,'config-'+label))),Layer.provide(observe),Layer.provideMerge(NodeServices.layer));}
function withDriver(label:string,run:(d:any)=>any){return Effect.scoped(Effect.gen(function*(){const d=yield* GitVcsDriver.GitVcsDriver;return yield* run(d);}).pipe(Effect.provide(layer(label))));}
function command(d:any,cwd:string,label:string,options:any={}){const {mode='hostile',duration=8000,code=0,...input}=options;return d.execute({operation:'capture.'+label,cwd,args:['-c',`alias.capture=!exec python3 ${qu(helper)} ${qu(mode)} ${duration} ${code} ${qu(label)} ${qu(root)} ${qu(socketPath)}`,'capture'],...input});}
function probes(d:any,cwd:string,label:string,count=8){return measure(label,Effect.all(Array.from({length:count},()=>d.execute({operation:'capture.probe',cwd,args:['rev-parse','HEAD']})),{concurrency:'unbounded'})).pipe(Effect.tap((r:any)=>Effect.sync(()=>log('PROBES',{label,count:r.ok?r.result.length:0}))));}
function holders(d:any,cwd:string,label:string,options:any={}){return Effect.gen(function*(){const fibers=[];for(let i=0;i<8;i++)fibers.push(yield* measure(label+'.h'+i,command(d,cwd,label+'.h'+i,options)).pipe(Effect.forkChild));const readies=yield* Effect.all(Array.from({length:8},(_,i)=>ready(label+'.h'+i)),{concurrency:'unbounded'});log('EIGHT_READY',{label,readyCount:readies.length,liveCount:readies.filter(e=>alive(e.pid)).length});return fibers;});}
const joins=(fibers:any[])=>Effect.all(fibers.map(f=>Fiber.join(f)),{concurrency:'unbounded'});
const interrupts=(fibers:any[])=>Effect.all(fibers.map(f=>Fiber.interrupt(f)),{concurrency:'unbounded'});
function transportRepo(label:string,duration:number){const cwd=repo(label),origin=path.join(root,label+'-origin.git');git(cwd,['clone','--bare',cwd,origin]);git(cwd,['remote','add','origin',origin]);git(cwd,['fetch','origin']);git(cwd,['branch','--set-upstream-to=origin/main','main']);const upload=`python3 ${qu(helper)} transport ${duration} 0 ${qu(label)} ${qu(root)} ${qu(socketPath)}`;git(cwd,['config','remote.origin.uploadpack',upload]);git(cwd,['config','capture.origin',origin]);return {cwd,origin};}

const queueStates:any[]=[];
function spans(){
  const fiber=Fiber.getCurrent();
  let span:any=fiber?Context.getOption(fiber.context,Tracer.ParentSpan):undefined;
  span=span?._tag==='Some'?span.value:undefined;
  const chain=[];
  while(span){chain.push(span.name??span._tag);span=span.parent?._tag==='Some'?span.parent.value:undefined;}
  return {fiberId:fiber?.id,spans:chain};
}
function observeFetchQueue(){
  const prototype=Object.getPrototypeOf(Semaphore.makeUnsafe(4));
  const nativeWithPermits=prototype.withPermits;
  const observed=new WeakSet();
  return vi.spyOn(prototype,'withPermits').mockImplementation(function(this:any,n:number){
    if(this.permits!==4||observed.has(this))return nativeWithPermits.call(this,n);
    observed.add(this);
    const real=this;
    const set=real.waiters as Set<()=>void>;
    const nativeAdd=set.add.bind(set),nativeDelete=set.delete.bind(set);
    const identities=new Map<()=>void,any>();
    const state={real,set};queueStates.push(state);
    log('FETCH_SEMAPHORE_OBSERVED',{permits:real.permits,taken:real.taken,free:real.free,observer:'real Semaphore.make(4); original semaphore.withPermits effect returned unchanged'});
    set.add=(entry)=>{
      const result=nativeAdd(entry);
      const identity=spans();identities.set(entry,identity);
      log('FETCH_SLOT_QUEUE_INSERT',{...identity,permits:real.permits,taken:real.taken,free:real.free,waiters:set.size,observer:'native Set.add already completed inside effect/Semaphore.waitForPermits'});
      return result;
    };
    set.delete=(entry)=>{
      const identity=identities.get(entry);
      const deleted=nativeDelete(entry);
      if(deleted)log('FETCH_SLOT_QUEUE_REMOVE',{...identity,permits:real.permits,taken:real.taken,free:real.free,waiters:set.size,observer:'native Set.delete already completed'});
      return deleted;
    };
    return nativeWithPermits.call(this,n);
  });
}
function r12(){return Effect.gen(function*(){
  const scope=yield* Scope.make();
  const context=yield* Layer.buildWithScope(layer('R12'),scope);
  const d=Context.get(context,GitVcsDriver.GitVcsDriver);
  const tuples=Array.from({length:5},(_,i)=>transportRepo('R12.'+i,8000));
  const calls=[];
  for(let i=0;i<5;i++){
    process.env.FIXTURE_ORIGIN=tuples[i].origin;
    calls.push(yield* measure('R12.caller'+i,d.statusDetailsRemote(tuples[i].cwd,{refreshUpstream:true})).pipe(Effect.withSpan('capture.R12.caller'+i),Effect.forkChild));
    if(i<4)yield* ready('R12.'+i);
  }
  log('FOUR_FETCHES_READY',{label:'R12',count:4,fifthReady:events.filter(e=>e.kind==='READY'&&e.label==='R12.4').length});
  const queued=yield* Effect.promise(()=>wait(e=>e.kind==='FETCH_SLOT_QUEUE_INSERT'&&e.spans?.includes('capture.R12.caller4')));
  const fifthPreflight=events.filter(e=>e.kind==='FINALIZED'&&e.cwd===tuples[4].cwd).map(e=>({ms:e.ms,args:e.args}));
  log('FIFTH_FETCH_OBSERVED_QUEUED',{label:'R12.4',queueEventMs:queued.ms,fiberId:queued.fiberId,spans:queued.spans,permits:queued.permits,taken:queued.taken,free:queued.free,waiters:queued.waiters,fifthPreflight,fifthFetchSpawns:events.filter(e=>e.kind==='SPAWNED'&&e.cwd===tuples[4].cwd&&e.args?.includes('fetch')).length});
  expect(queued.taken).toBe(4);expect(queued.free).toBe(0);expect(queued.waiters).toBe(1);
  expect(queued.spans).toContain('GitVcsDriver.fetchRemoteForStatus.wait');
  const independent=yield* probes(d,tuples[0].cwd,'R12.independent',1);
  log('CALLER_INTERRUPT_REQUEST',{label:'R12.caller0'});
  const callerInterruptStart=now();
  yield* Fiber.interrupt(calls[0]);
  const callerExit=yield* Fiber.await(calls[0]);
  log('CALLER_INTERRUPT_COMPLETE',{label:'R12.caller0',elapsedMs:+(now()-callerInterruptStart).toFixed(3),exit:String(callerExit)});
  const afterCaller=snapshot('R12');
  expect(afterCaller.liveCount).toBe(4);
  expect(events.filter(e=>e.kind==='TERM'&&e.source==='fixture')).toHaveLength(0);
  expect(queueStates[0].set.size).toBe(1);
  const t=now();
  log('SERVICE_CLOSE_REQUEST',{label:'R12',queuedFetchWaiters:queueStates[0].set.size,liveHelpers:afterCaller.liveCount});
  yield* Scope.close(scope,Exit.void);
  const closeMs=now()-t;
  log('SERVICE_CLOSE_COMPLETE',{label:'R12',closeMs:+closeMs.toFixed(3),remainingFetchWaiters:queueStates[0].set.size});
  yield* interrupts(calls.slice(1));
  const final=snapshot('R12');
  const fetchSpawns=events.filter(e=>e.kind==='SPAWNED'&&e.args?.includes('fetch'));
  const fifthFetchSpawns=fetchSpawns.filter(e=>e.cwd===tuples[4].cwd).length;
  const fifthReady=events.filter(e=>e.kind==='READY'&&e.label==='R12.4').length;
  const groups=events.filter(e=>e.kind==='READY'&&e.source==='fixture').map(e=>e.pgid);
  const fetchSignals=signals.filter(e=>groups.includes(-e.pid));
  const terms=fetchSignals.filter(e=>e.signal==='SIGTERM');
  const kills=fetchSignals.filter(e=>e.signal==='SIGKILL');
  expect(fetchSpawns).toHaveLength(4);expect(fifthFetchSpawns).toBe(0);expect(fifthReady).toBe(0);
  expect(terms).toHaveLength(4);expect(kills).toHaveLength(4);
  expect(final.liveCount).toBe(0);expect(final.markerCount).toBe(0);expect(queueStates[0].set.size).toBe(0);
  summary('R12','scope',{closeMs:+closeMs.toFixed(3),queued,independentProbeOk:independent.ok,callerExit:String(callerExit),afterCaller,fetchSpawnCount:fetchSpawns.length,fifthFetchSpawns,fifthReady,fetchSignals,remainingFetchWaiters:queueStates[0].set.size,...final});
});}
it('captures real GitVcsDriver runtime',async()=>{
await new Promise<void>(resolve=>server.listen(socketPath,resolve));
server.on('connection',sock=>{let buf='';sock.on('data',data=>{buf+=data.toString();let ix;while((ix=buf.indexOf('\n'))>=0){const e=JSON.parse(buf.slice(0,ix));buf=buf.slice(ix+1);const {kind,...rest}=e;log(kind,{...rest,source:'fixture'});}});});
const driverHash=crypto.createHash('sha256').update(fs.readFileSync(import.meta.filename)).digest('hex');
const helperHash=crypto.createHash('sha256').update(fs.readFileSync(helper)).digest('hex');
const env={sha,wallOrigin:new Date().toISOString(),os:os.type(),kernel:os.release(),node:process.version,git:git(process.cwd(),['--version']),cpus:os.availableParallelism(),platformNodeShared:JSON.parse(fs.readFileSync(path.join(fs.realpathSync('node_modules/@effect/platform-node'),'..','platform-node-shared','package.json'),'utf8')).version,driverHash,helperHash,group,root,inputProtocol:'native GitVcsDriver.layer + NodeServices.layer + ServerConfig.layerTest; passive native spawn/signal observer; passive native semaphore waiters Set.add/delete observer delegates unchanged; traced caller identity; real git aliases/hooks/local upload-pack; READY IPC; no fake spawner/clock/platform'};
log('ENVIRONMENT',env);fs.writeFileSync(path.join(root,'environment.json'),JSON.stringify(env,null,2));instrumentKill();const queueObserver=observeFetchQueue();
try{await Effect.runPromise(Effect.scoped(r12()));}finally{queueObserver.mockRestore();process.kill=rawKill as typeof process.kill;server.close();log('RUN_COMPLETE',{rows:summaries.length});}
});
