import * as NodeServices from '@effect/platform-node/NodeServices';
import * as NetService from '@t3tools/shared/Net';
import * as NodeCrypto from '@effect/platform-node/NodeCrypto';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as Option from 'effect/Option';
import * as References from 'effect/References';
import * as EnvironmentAuth from './src/auth/EnvironmentAuth.ts';
import * as ServerConfig from './src/config.ts';
import { resolveCliAuthConfig } from './src/cli/config.ts';
import { readLiveServerRuntimeState } from './src/serverRuntimeState.ts';
import { mcpToolSession } from './src/mcp/AcpMcpStdioBridge.ts';
import { writeFileSync } from 'node:fs';

const [home, output, sha] = process.argv.slice(2);
const records: unknown[] = [];
const privateKeys = new Set(['model','modelSelection','providerInstanceId','executedModel','worktreePath','workspaceRoot','link']);
function clean(value: unknown): unknown {
  if (typeof value === 'string') return value.replaceAll(home, '<isolated-home>');
  if (Array.isArray(value)) return value.map(clean);
  if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).filter(([k])=>!privateKeys.has(k)).map(([k,v])=>[k,clean(v)]));
  return value;
}
const main = Effect.gen(function* () {
 const config = yield* resolveCliAuthConfig({baseDir:Option.some(home)},Option.none(),{quietLogs:true});
 yield* Effect.gen(function* () {
  const runtime = yield* readLiveServerRuntimeState(config.serverRuntimeStatePath);
  if (Option.isNone(runtime)) throw new Error('Server not running');
  const auth = yield* EnvironmentAuth.EnvironmentAuth;
  const issued = yield* Effect.acquireRelease(auth.issueMcpClientSession({label:'Synthetic evidence driver',access:'full-access',peerLink:false,client:{deviceType:'bot'}}),s=>auth.revokeSession(s.sessionId));
  const session = yield* mcpToolSession({endpoint:new URL('/mcp',runtime.value.origin).href,authorization:`Bearer ${issued.token}`,clientName:'evidence-driver'});
  const call = (name:string,args:Record<string,unknown>,label?:string) => session.call(name,args).pipe(Effect.map((raw:any)=>{
   if(raw.isError) throw new Error(JSON.stringify(raw));
   const result = raw.structuredContent ?? JSON.parse(raw.content.find((x:any)=>x.type==='text').text);
   if(label) records.push({label,tool:name,arguments:clean(args),response:clean(result)});
   return result;
  }));
  const project = yield* call('t3_project_create',{title:'Tool response evidence'});
  const threads=[];
  for(let n=1;n<=12;n++) threads.push(yield* call('t3_thread_launch',{projectId:project.id,title:`Evidence thread ${String(n).padStart(2,'0')}`,modelSelection:{instanceId:'devReplay',model:'showcase'},runtimeMode:'full-access'}));
  const threadId=threads[0].threadId;
  for(let n=1;n<=6;n++) {
   const sent=yield* call('t3_thread_send',{threadId,message:`Synthetic prompt ${n}: describe the evidence fixture.`});
   let waited;
   do { waited=yield* call('t3_thread_wait',{threadId,runId:sent.runId,timeoutMs:15000}); } while(waited.next);
   const check=yield* call('t3_thread_read',{threadId,limit:100,maxCharsPerItem:50000});
   if(check.thread.status==='failed') throw new Error('Replay failed');
  }
  const list=yield* call('t3_thread_list',{projectId:project.id},'Default thread list');
  if(list.nextCursor!==null) yield* call('t3_thread_list',{projectId:project.id,cursor:list.nextCursor},'Follow nextCursor');
  const largerList=yield* call('t3_thread_list',{projectId:project.id,limit:20},'Explicit list limit 20');
  const read=yield* call('t3_thread_read',{threadId},'Default thread read');
  const pages=[read];
  let page=read;
  while(page.hasMore && page.nextPosition!==null) {
   page=yield* call('t3_thread_read',{threadId,afterPosition:page.nextPosition},'Follow afterPosition=nextPosition'); pages.push(page);
  }
  const largerRead=yield* call('t3_thread_read',{threadId,limit:20,maxCharsPerItem:2000},'Explicit read limit 20 and maxCharsPerItem 2000');
  const long=largerRead.items.find((x:any)=>x.type==='assistant_message' && x.text?.length>500) ?? largerRead.items.find((x:any)=>x.text?.length>500);
  if(!long) throw new Error('No long assistant item');
  const chunks=[];let offset=0;let part;
  do {
   part=yield* call('t3_thread_read',{threadId,itemId:long.itemId,textOffset:offset},'Read item and follow textOffset=nextTextOffset');
   const item=part.items[0];chunks.push(item.text??'');offset=item.nextTextOffset;
  }while(offset!==null && offset!==undefined);
  const summary={commit:sha,environment:'Real T3 server on Linux; isolated fresh database; synthetic replay history; MCP over loopback HTTP',seed:{threads:12,prompts:6,visibleMessages:largerRead.items.length,assistantCharacters:long.text.length},defaultListCount:list.threads.length,defaultListNextCursor:list.nextCursor,explicitListCount:largerList.threads.length,defaultReadCount:read.items.length,defaultReadTextLengths:read.items.map((x:any)=>({type:x.type,characters:x.text?.length??0,truncated:x.textTruncated,nextTextOffset:x.nextTextOffset})),defaultReadHasMore:read.hasMore,defaultReadNextPosition:read.nextPosition,paginatedReadTotal:pages.flatMap(x=>x.items).length,explicitReadCount:largerRead.items.length,explicitReadMaxCharacters:Math.max(...largerRead.items.map((x:any)=>x.text?.length??0)),textContinuationChunks:chunks.map(x=>x.length),textContinuationMatchesFullText:chunks.join('')===long.text};
  writeFileSync(output,JSON.stringify({summary,records},null,2)+'\n');
  console.log(JSON.stringify(summary,null,2));
 }).pipe(Effect.scoped,Effect.provide(EnvironmentAuth.layerRuntime.pipe(Layer.provide(ServerConfig.layer(config)),Layer.provide(Layer.succeed(References.MinimumLogLevel,'Error')))));
}).pipe(Effect.provide([NodeServices.layer,NodeCrypto.layer,NetService.layer]));
await Effect.runPromise(main);
