import { createServer, request as httpRequest } from 'node:http';
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, watch } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import assert from 'node:assert/strict';
import * as NodeServices from '@effect/platform-node/NodeServices';
import * as NodeCrypto from '@effect/platform-node/NodeCrypto';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as DateTime from 'effect/DateTime';
import * as Fiber from 'effect/Fiber';
import * as Stream from 'effect/Stream';
import * as FetchHttpClient from 'effect/http/FetchHttpClient';
const Adapter=await import(process.env.QA_BASELINE==='1'?'./baseline/adapter.ts':'@t3tools/provider-opencode/server/v2/adapter');
const Client=await import(process.env.QA_BASELINE==='1'?'./baseline/OpenCode2Client.ts':'@t3tools/provider-opencode/server/v2/OpenCode2Client');
const Server=await import(process.env.QA_BASELINE==='1'?'./baseline/OpenCode2Server.ts':'@t3tools/provider-opencode/server/v2/OpenCode2Server');
import * as Runtime from '@t3tools/provider-opencode/server/OpenCodeRuntime';
import * as Ledger from '@t3tools/provider-opencode/server/OpenCodeServerLedger';
import * as Mcp from '@t3tools/provider-core/server/McpProviderSessions';
import * as IdAllocator from '@t3tools/provider-core/server/IdAllocator';
import * as Host from '@t3tools/provider-testing/TestProviderHost';

const baseline=process.env.QA_BASELINE==='1';
const scenario = process.env.QA_SCENARIO ?? 'cpu';
const WORK = mkdtempSync(join(tmpdir(), `t3-opencode-${scenario}-`));
const gates = join(WORK, 'gates'); mkdirSync(gates);
process.chdir(WORK);
const startedAt = Date.now();
const log = (event, data={}) => console.log(JSON.stringify({elapsedMs:Date.now()-startedAt,event,...data}));
const cpuStat = () => 'unrestricted task VM';
log('environment',{scenario,baseline,node:process.version,platform:process.platform,arch:process.arch,cpuMax:'unrestricted',memoryMax:'unrestricted',work:WORK});
const initialCpu=cpuStat();
let paused=false;
const abort=new AbortController();
let modelCalls=0; let firstCalls=0; let delayRecovery=false; let target;
const apiRequests=[]; let eventSubscriptions=0; let historyReads=0; let opencodePid;
const toolScript=join(WORK,'cpu-tool.cjs');
writeFileSync(toolScript, `const {Worker}=require('node:worker_threads'); const fs=require('node:fs'); const path=require('node:path');
const [id,gates]=process.argv.slice(2); fs.writeFileSync(path.join(gates,id+'.started'),'started');
Promise.all(Array.from({length:4},()=>new Promise((resolve,reject)=>{ const w=new Worker(\`const {parentPort}=require('node:worker_threads'); const end=Date.now()+10000; let x=0; while(Date.now()<end){for(let i=0;i<100000;i++)x=Math.sqrt(x+i);} parentPort.postMessage(x);\`,{eval:true}); w.once('message',resolve); w.once('error',reject); }))).then(()=>{
fs.writeFileSync(path.join(gates,id+'.cpu-done'),'done'); const release=path.join(gates,'release');
const done=()=>{if(fs.existsSync(release)){watcher.close();console.log('QA_TOOL_DONE '+id);}}; const watcher=fs.watch(gates,done); done();
});`);
const waitFiles=(suffix) => new Promise((resolve)=>{
 const check=()=>{if(['tool1','tool2'].every(id=>existsSync(join(gates,id+suffix)))) { watcher.close();resolve(); }};
 const watcher=watch(gates,check); check();
});
const model=createServer(async(req,res)=>{
 try {
 const chunks=[]; for await(const part of req)chunks.push(part); const body=JSON.parse(Buffer.concat(chunks).toString()||'{}');
 modelCalls++;
 const tools=body.tools??[]; const hasResult=(body.messages??[]).some(m=>m.role==='tool');
 const name=tools.find(t=>/^(shell|bash)$/.test(t.function?.name))?.function?.name;
 log('model.request',{path:req.url,call:modelCalls,hasResult,tools:tools.map(t=>t.function?.name)});
 res.writeHead(200,{'content-type':'text/event-stream'});
 const frame=(delta,finish_reason=null)=>res.write(`data: ${JSON.stringify({id:'chatcmpl-qa',object:'chat.completion.chunk',created:1,model:'qa',choices:[{index:0,delta,finish_reason}]})}\n\n`);
 if(!hasResult){assert.ok(name,'OpenCode must expose its shell tool');const id='tool'+(++firstCalls);
 frame({role:'assistant',tool_calls:[{index:0,id:'call_'+id,type:'function',function:{name,arguments:JSON.stringify({command:`/home/user/.nvm/versions/node/v24.22.1/bin/node ${toolScript} ${id} ${gates}`,description:'Bounded CPU QA workload',timeout:240000})}}]});
 frame({},'tool_calls');
 } else {frame({role:'assistant',content:'QA_RECOVERED'}); frame({},'stop');}
 res.end('data: [DONE]\n\n');
 } catch(e){log('model.error',{error:String(e)});res.end();}
});
await new Promise(r=>model.listen(0,'127.0.0.1',r)); const modelUrl=`http://127.0.0.1:${model.address().port}/v1`;
const proxy=createServer((req,res)=>{
 const path=req.url; apiRequests.push(path);
 if(path==='/api/event') {eventSubscriptions++;log('event.subscribe',{count:eventSubscriptions});}
 const delayed=delayRecovery && (path==='/api/session/active' || /\/message(?:\?|$)|\/(permission|form)(?:\?|$)/.test(path));
 if(/\/message(?:\?|$)/.test(path))historyReads++;
 const upstream=httpRequest(new URL(path,target),{method:req.method,headers:req.headers},response=>{
  if(delayed){const chunks=[];response.on('data',d=>chunks.push(d));response.on('end',()=>{log('reconcile.delay',{path,status:response.statusCode,delayMs:8000});const timer=setTimeout(()=>{res.writeHead(response.statusCode,response.headers);res.end(Buffer.concat(chunks));},8000);res.once('close',()=>clearTimeout(timer));});}
  else {res.writeHead(response.statusCode,response.headers);response.pipe(res);}
 });
 upstream.on('error',err=>{if(!res.destroyed){res.writeHead(502);res.end(String(err));}});
 res.once('close',()=>upstream.destroy()); req.pipe(upstream);
});
await new Promise(r=>proxy.listen(0,'127.0.0.1',r)); const proxyUrl=`http://127.0.0.1:${proxy.address().port}`;
const binary=join(WORK,'opencode-wrapper');
writeFileSync(binary,`#!/bin/sh\nprintf '%s' "$$" > '${join(WORK,'opencode.pid')}'\nexec /tmp/t3-24158-opencode/node_modules/.bin/opencode "$@"\n`,{mode:0o755});
const config={"model": "fixture/qa", "providers": {"fixture": {"name": "QA local model", "package": "@opencode/ai/providers/openai-compatible", "settings": {"baseURL": modelUrl, "apiKey": "qa"}, "models": {"qa": {"name": "QA", "modelID": "qa", "capabilities": {"tools": true, "input": ["text"], "output": ["text"]}, "limit": {"context": 32768, "output": 8192}}}}}};
const environment={...process.env,XDG_CONFIG_HOME:join(WORK,'config'),XDG_DATA_HOME:join(WORK,'data'),XDG_STATE_HOME:join(WORK,'state'),XDG_CACHE_HOME:join(WORK,'cache'),OPENCODE_CONFIG_CONTENT:JSON.stringify(config),OPENCODE_DISABLE_PROJECT_CONFIG:'1'};
let ownerConnection;
const watchdog=setTimeout(()=>{log('watchdog'); if(paused){try{process.kill(opencodePid,'SIGCONT');}catch{}} abort.abort();},240000);
try {
 const clients=Layer.effect(Client.OpenCode2Client,Effect.gen(function*(){
  const actual=yield* Client.make;
  return Client.OpenCode2Client.of({connect:(input)=>{target=input.baseUrl;return actual.connect({...input,baseUrl:proxyUrl});}});
 }));
 const runtimeLayer=Runtime.layer.pipe(Layer.provide(Layer.mergeAll(Ledger.layerTest,NodeServices.layer)));
 const program=Effect.gen(function*(){
  const server=yield* Server.make({binaryPath:binary,serverUrl:'',serverPassword:'',directory:WORK,environment});
  const connection=yield* server.withConnection(c=>Effect.succeed(c)); ownerConnection=connection;
  const info=yield* connection.client.server.info();opencodePid=Number(readFileSync(join(WORK,'opencode.pid'),'utf8'));assert.equal(info.pid,opencodePid);log('server.ready',{pid:opencodePid,version:info.version});
  const adapter=yield* Adapter.make('opencode').pipe(Effect.provideService(Server.OpenCode2Server,server));
  const policy={runtimeMode:'full-access',interactionMode:'default',cwd:WORK};const selection={instanceId:'opencode',model:'fixture/qa'};
  const runtime=yield* adapter.openSession({threadId:'thread:qa-one',providerSessionId:'provider-session:qa',modelSelection:selection,runtimePolicy:policy});
  const now=yield* DateTime.now;const threads=[];
  for(let i=1;i<=2;i++){
   const native=yield* connection.client.session.create({title:'QA '+i,location:{directory:WORK},model:{providerID:'fixture',id:'qa'},permissions:[{action:'*',resource:'*',effect:'allow'}]});
   const threadId='thread:qa-'+i;
   const thread=yield* runtime.resumeThread({threadId,modelSelection:selection,runtimePolicy:policy,providerThread:{id:'provider-thread:qa-'+i,driver:'opencode',providerInstanceId:'opencode',providerSessionId:runtime.providerSessionId,appThreadId:threadId,ownerNodeId:null,nativeThreadRef:{driver:'opencode',nativeId:native.id,strength:'strong'},nativeConversationHeadRef:null,status:'idle',firstRunOrdinal:null,lastRunOrdinal:null,handoffIds:[],forkedFrom:null,createdAt:now,updatedAt:now}}); threads.push(thread);
  }
  const terminals=yield* runtime.events.pipe(Stream.tap(e=>{if(e.type==='turn.terminal')log('turn.terminal',{thread:e.providerThreadId,status:e.status});return Effect.void;}),Stream.filter(e=>e.type==='turn.terminal'),Stream.take(2),Stream.runCollect,Effect.forkScoped);
  for(let i=1;i<=2;i++)yield* runtime.startTurn({appThread:{id:threads[i-1].appThreadId,title:'QA '+i,lineage:{parentThreadId:null,relationshipToParent:null,rootThreadId:threads[i-1].appThreadId}},threadId:threads[i-1].appThreadId,runId:'run:qa-'+i,runOrdinal:1,providerTurnOrdinal:1,attemptId:'attempt:qa-'+i,rootNodeId:'node:qa-'+i,providerThread:threads[i-1],message:{messageId:'message:qa-'+i,text:'Run the bounded CPU test in the shell, then report done.',attachments:[],createdBy:'user',creationSource:'web',scheduledTaskId:undefined,senderThreadId:undefined},modelSelection:selection,runtimePolicy:policy});
  yield* Effect.promise(()=>waitFiles('.started'));log('tools.started',{count:2});
  if(scenario==='stall'){
   delayRecovery=true;process.kill(opencodePid,'SIGSTOP');paused=true;log('server.paused',{pid:opencodePid,durationMs:65000});
   yield* Effect.promise(()=>new Promise(resolve=>setTimeout(resolve,65000)));
   process.kill(opencodePid,'SIGCONT');paused=false;log('server.resumed',{pid:opencodePid});
  }
  if(scenario==='death'){process.kill(opencodePid,'SIGKILL');log('server.killed',{pid:opencodePid});}
  else {yield* Effect.promise(()=>waitFiles('.cpu-done'));log('cpu.work.complete');writeFileSync(join(gates,'release'),'release');}
  const ended=yield* Fiber.join(terminals);assert.deepEqual(ended.map(e=>e.status),scenario==='death'||baseline?['failed','failed']:['completed','completed']);assert.equal(firstCalls,2);assert.equal(modelCalls,scenario==='death'?2:4);if(scenario==='stall'){assert.ok(eventSubscriptions>=2);assert.ok(historyReads>=2);}
  delayRecovery=false;
  if(baseline){const nativeOutcomes=[];for(const thread of threads){const page=yield* connection.client.message.list({sessionID:thread.nativeThreadRef.nativeId,order:'desc',limit:50});nativeOutcomes.push(page.data.find(m=>m.type==='idle')?.outcome);}assert.deepEqual(nativeOutcomes,['succeeded','succeeded']);const still=yield* connection.client.server.info();assert.equal(still.pid,opencodePid);log('baseline.native.completed',{nativeOutcomes,stillAlivePid:still.pid});}
  log('passed',{scenario,baseline,turns:2,modelCalls,initialPrompts:firstCalls,eventSubscriptions,historyReads,pid:opencodePid});
 }).pipe(Effect.scoped,Effect.provide(Layer.mergeAll(clients,runtimeLayer,Host.layer({cwd:WORK}).pipe(Layer.provide(NodeServices.layer)),Mcp.layer,IdAllocator.layer,NodeServices.layer,NodeCrypto.layer).pipe(Layer.provideMerge(FetchHttpClient.layer))));
 await Effect.runPromise(program,{signal:abort.signal});
 log('shutdown.complete',{cpuBefore:initialCpu,cpuAfter:cpuStat()});
} catch(error){log('failed',{error:String(error),stack:error?.stack});throw error;}
finally {clearTimeout(watchdog);if(paused){try{process.kill(opencodePid,'SIGCONT');}catch{}}model.closeAllConnections();proxy.closeAllConnections();await Promise.all([new Promise(r=>model.close(r)),new Promise(r=>proxy.close(r))]);}
