import {fork} from 'node:child_process';
import {createServer} from 'node:net';
import {mkdir,readdir,readFile} from 'node:fs/promises';
import {resolve} from 'node:path';
import {stopChildren} from './stop-children.mjs';
export async function assertPortFree(port){if(port===0)return;await new Promise((ok,no)=>{const s=createServer();s.once('error',()=>no(Error(`Port ${port} is already occupied; no other process was stopped`)));s.listen(port,'127.0.0.1',()=>s.close(ok));});}
export async function startPair({directory,checkoutPort=8413,pricingPort=8414,endpoint=''}={}){
 await assertPortFree(checkoutPort);await assertPortFree(pricingPort);await mkdir(directory,{recursive:true});
 const children=[],logs=[],stops=[];
 async function spawn(service,port,pricingURL){
  const child=fork(new URL('../src/service.mjs',import.meta.url),[],{execArgv:[],env:{...process.env,SERVICE:service,PORT:String(port),HOST:'127.0.0.1',RAW_DIR:resolve(directory),PRICING_URL:pricingURL||'http://127.0.0.1:8414',OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:endpoint,TRACE_RATIO:'1'},stdio:['ignore','pipe','pipe','ipc']});
  children.push(child);let out='',err='';
  child.stdout.on('data',chunk=>{out+=chunk;let i;while((i=out.indexOf('\n'))>=0){const line=out.slice(0,i);out=out.slice(i+1);try{logs.push(JSON.parse(line));}catch{logs.push({type:'stdout',service,line});}}});
  child.stderr.on('data',chunk=>{err+=chunk;});
  const ready=await new Promise((ok,no)=>{const timeout=setTimeout(()=>no(Error('Service ready timeout: '+err)),15000);child.once('error',e=>{clearTimeout(timeout);no(e);});child.once('exit',code=>{clearTimeout(timeout);no(Error(`Service exited ${code}: ${err}`));});child.on('message',message=>{if(message?.type==='ready'){clearTimeout(timeout);ok(message);}if(message?.type==='stopped')stops.push(message);});});
  child.reviewStderr=()=>err;return{child,...ready};
 }
 async function stop(){
  await stopChildren(children);
  return stops;
 }
 try{const pricing=await spawn('pricing',pricingPort);const checkout=await spawn('checkout',checkoutPort,`http://127.0.0.1:${pricing.port}`);return{checkoutURL:`http://127.0.0.1:${checkout.port}`,pricingURL:`http://127.0.0.1:${pricing.port}`,logs,stops,stop,errors:()=>children.map(c=>c.reviewStderr()).filter(Boolean)};}catch(e){await stop();throw e;}
}
export async function readRawSpans(directory){const rows=[];for(const file of await readdir(directory)){if(!file.endsWith('.jsonl'))continue;for(const line of (await readFile(resolve(directory,file),'utf8')).trim().split('\n').filter(Boolean))rows.push(JSON.parse(line));}return rows;}
