import {mkdir,writeFile,readFile,rm} from 'node:fs/promises';
import {resolve,basename} from 'node:path';
import {createHash,randomBytes} from 'node:crypto';
import {platform,arch,availableParallelism} from 'node:os';
import {startPair,readRawSpans} from './processes.mjs';
import {SCENARIO_DETAILS} from '../src/recording.js';
const root=resolve('records'),runId=new Date().toISOString().replace(/[:.]/g,'-'),directory=resolve(root,runId);
await mkdir(directory,{recursive:true});
const endpoint=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT||'';
const pair=await startPair({directory,endpoint});const scenarios=[];let stopped=false;
async function request(scenario,headers={}){const started=performance.now();const response=await fetch(pair.checkoutURL+'/demo?scenario='+scenario,{headers,signal:AbortSignal.timeout(25000)});const body=await response.json();return{status:response.status,elapsedMs:performance.now()-started,traceId:body.traceId,sampled:body.sampled,body,inputTraceparent:headers.traceparent||null};}
try{
 for(const [id,title,description] of SCENARIO_DETAILS){
  let requests;
  if(id==='async-context')requests=await Promise.all([request(id),request(id)]);
  else if(id==='sampling')requests=await Promise.all([1,0].map(sample=>request(id,{traceparent:`00-${randomBytes(16).toString('hex')}-${randomBytes(8).toString('hex')}-0${sample}`})));
  else requests=[await request(id)];
  const record={id,title,description,requests,traceIds:requests.map(r=>r.traceId)};
  if(id==='privacy'){const health=await fetch(pair.checkoutURL+'/health');record.health={status:health.status,body:await health.json()};}
  scenarios.push(record);
 }
 await pair.stop();stopped=true;
 const spans=await readRawSpans(directory),byTrace=new Map();for(const s of spans){if(!byTrace.has(s.traceId))byTrace.set(s.traceId,[]);byTrace.get(s.traceId).push(s);}
 for(const record of scenarios){record.traces=record.traceIds.map(traceId=>({traceId,spans:byTrace.get(traceId)||[]}));record.logs=pair.logs.filter(l=>record.traceIds.includes(l.traceId));}
 const sampled=scenarios.find(r=>r.id==='sampling');
 if(sampled.traces.find(t=>t.traceId===sampled.requests.find(r=>!r.sampled).traceId).spans.length)throw Error('Unsampled request exported a span');
 const sourceFiles=['src/telemetry.mjs','src/service.mjs','src/worker.mjs','src/run-worker.mjs','src/recording.js','tools/processes.mjs','tools/stop-children.mjs','tools/capture.mjs','package-lock.json'];
 const sourceHashes={};for(const name of sourceFiles)sourceHashes[name]=createHash('sha256').update(await readFile(name)).digest('hex');
 const recording={schema:1,kind:'actual-node-opentelemetry-recording',recordedAt:new Date().toISOString(),environment:{node:process.version,platform:platform(),arch:arch(),availableParallelism:availableParallelism(),sdkNode:'0.301.1',sdkTrace:'3.1.1',api:'1.10.0',otlpEnabled:Boolean(endpoint)},sourceHashes,commands:['npm ci --ignore-scripts','npm run capture'],shutdown:pair.stops.map(s=>({...s,file:basename(s.file)})),stderr:pair.errors(),scenarios,checks:{spans:spans.length,healthSpans:spans.filter(s=>s.attributes['http.route']==='/health').length,secretLeaked:JSON.stringify(spans).includes('DEMO_TOKEN_DO_NOT_RECORD'),lastRequestExported:scenarios.at(-1).traces.every(t=>t.spans.length>0)}};
 if(recording.checks.healthSpans||recording.checks.secretLeaked||!recording.checks.lastRequestExported)throw Error('Recording verification failed');
 await writeFile(resolve(directory,'recording.json'),JSON.stringify(recording,null,2)+'\n');
 await writeFile(resolve(root,'recording.json'),JSON.stringify(recording,null,2)+'\n');
 await writeFile(resolve(root,'latest-run.json'),JSON.stringify({directory:runId},null,2)+'\n');
 console.log(JSON.stringify({directory,scenarios:scenarios.length,spans:spans.length,checks:recording.checks,shutdown:pair.stops.map(x=>({service:x.service,exported:x.exported}))}));
}finally{if(!stopped)await pair.stop();}
