import assert from 'node:assert/strict';
import { performance } from 'node:perf_hooks';
import { mkdirSync, writeFileSync } from 'node:fs';
import { makeStreamingWorkTimelineFixture, withStreamedText } from '/home/ubuntu/wt/issue-24120/apps/web/src/test-fixtures.ts';
import { deriveTimelineEntriesFromVisibleTurnItems as fullEntries, deriveTimelineEntriesFromVisibleTurnItemsWithState as incrementalEntries } from '/home/ubuntu/wt/issue-24120/apps/web/src/session-logic.ts';
import { deriveMessagesTimelineRows as fullRows, deriveMessagesTimelineRowsWithState as incrementalRows } from '/home/ubuntu/wt/issue-24120/apps/web/src/components/chat/MessagesTimeline.logic.ts';
const runDir='/tmp/issue-lead-24120-r3/review-a/run-'+new Date().toISOString().replaceAll(':','-');
mkdirSync(runDir);
let seed=24120;
const random=(n:number)=>{seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed%n};
const summary={cases:0,steps:0,rowKinds:new Set<string>(),benchmarks:[] as any[]};
for(const kind of ['reasoning','proposed_plan'] as const){
 const f=makeStreamingWorkTimelineFixture(kind);
 const prototype=f.visibleTurnItems.at(-1)!;
 const extra=(type:string,i:number)=>{
  const base={...f.base('extra-'+i,f.runId,8+i),status:'running',completedAt:null};
  const item= type==='compaction'?{...base,type,driver:null,beforeTokenCount:120000}:
    type==='todo_list'?{...base,type,planId:'todo-'+i,steps:[{id:'step-'+i,text:'Do it',status:'pending'}]}:
    type==='user_message'?{...base,type,messageId:'steer-'+i,text:'Continue',attachments:[],inputIntent:'steer',createdBy:'user',creationSource:'web'}:
    type==='reasoning'?{...base,type,text:'Earlier thought',streaming:true}:
    type==='proposed_plan'?{...base,type,planId:'extra-plan-'+i,markdown:'Earlier plan',streaming:true}:
    type==='system_notice'?{...base,type,message:'Notice'}:
    type==='run_interrupt_result'?{...base,type,message:'Interrupted'}:
    type==='assistant_message'?{...base,type,messageId:'reply-'+i,text:'Reply',streaming:false,status:'completed'}:
    {...base,type:'dynamic_tool',toolName:'read_file',input:{path:'file.ts'},output:'ok',status:random(2)?'running':'completed'};
  return {...prototype,sourceItemId:item.id,item};
 };
 for(let c=0;c<1500;c++){
  let items=[...f.visibleTurnItems];
  const variant=random(4);
  if(variant===0) items[3]={...items[3],item:{...items[3].item,text:'/compact',idleCompaction:random(2)?'onReturn':'beforeCacheExpires'}};
  if(variant===1) items=items.map(row=>({...row,item:{...row.item,runId:null}}));
  const types=['compaction','todo_list','user_message','reasoning','proposed_plan','system_notice','run_interrupt_result','assistant_message','dynamic_tool'];
  const count=random(8);
  for(let j=0;j<count;j++) items.splice(3+random(items.length-2),0,extra(types[random(types.length)]!,j));
  items=items.map((row,position)=>({...row,position}));
  if(random(2)) items=items.map(row=>row.item.type==='compaction'?{...row,item:{...row.item,status:'completed',afterTokenCount:16000}}:row);
  const working=Boolean(random(2));
  const expanded=new Set(items.flatMap(row=>['work-group:'+row.item.id]));
  const context={latestRun:{runId:f.runId,status:working?'running':'completed',startedAt:f.time(5),completedAt:working?null:f.time(40)},runningRunId:working?f.runId:null,isWorking:working,runlessWorkActive:variant===1,activeTurnStartedAt:f.time(5),turnDiffSummaries:[],supportsConversationRollback:Boolean(random(2)),expandedWorkGroupIds:random(2)?expanded:undefined,expandedRunIds:random(2)?new Set([f.runId,f.historyRunId]):undefined};
  let entryProjection:any=null,rowProjection:any=null;
  for(const [i,text] of ['Let','Let me',' ','Let me check','Let me check again'].entries()){
   items=withStreamedText(items as any,f.streamingItemId,text,f.time(20+i)) as any;
   const plans=kind==='proposed_plan'?[{id:'live-plan',kind:'proposed_plan',status:'active',markdown:text}]:[];
   const input={visibleTurnItems:items as any,optimisticMessages:[],plans:plans as any};
   entryProjection=incrementalEntries(input,entryProjection);
   assert.deepEqual(entryProjection.entries,fullEntries(input),'entries '+kind+' '+c+' '+i);
   const rowsInput={...context,timelineEntries:entryProjection.entries};
   rowProjection=incrementalRows(rowsInput as any,rowProjection);
   const expected=fullRows({...rowsInput,timelineEntries:fullEntries(input)} as any);
   try{assert.deepEqual(rowProjection.rows,expected);}catch(e){writeFileSync(runDir+'/failure.json',JSON.stringify({kind,c,i,input,context,actual:rowProjection.rows,expected},null,2));throw e;}
   for(const row of rowProjection.rows)summary.rowKinds.add(row.kind);
   summary.steps++;
  }
  summary.cases++;
 }
 // Malformed markdown: 3000 whitespace-delimited unmatched bracket tokens, 14999 characters.
 const malformed=Array(3000).fill('[({*').join(' ');
 const items=withStreamedText(f.visibleTurnItems,f.streamingItemId,malformed,f.time(20));
 const before={visibleTurnItems:items,optimisticMessages:[],plans:[{id:'live-plan',kind:'proposed_plan',status:'active',markdown:malformed}]};
 const after={...before,visibleTurnItems:withStreamedText(items,f.streamingItemId,malformed+'[',f.time(21)),plans:[{...before.plans[0],markdown:malformed+'['}]};
 const entryPrev=incrementalEntries(before as any);
 const context={latestRun:{runId:f.runId,status:'running',startedAt:f.time(5),completedAt:null},runningRunId:f.runId,isWorking:true,activeTurnStartedAt:f.time(5),turnDiffSummaries:[],supportsConversationRollback:true,expandedWorkGroupIds:new Set(['work-group:live-work'])};
 const rowPrev=incrementalRows({...context,timelineEntries:entryPrev.entries} as any);
 const bench=(name:string,fn:()=>unknown)=>{
  for(let i=0;i<100;i++)fn();
  const times=[];
  for(let j=0;j<30;j++){const start=performance.now();for(let i=0;i<100;i++)fn();times.push((performance.now()-start)/100);}
  times.sort((a,b)=>a-b);
  summary.benchmarks.push({kind,name,tokens:3000,chars:malformed.length,medianMs:times[15],maxBatchMeanMs:times.at(-1)});
 };
 bench('entry patch and plan-status comparison',()=>incrementalEntries(after as any,entryPrev));
 const entryNext=incrementalEntries(after as any,entryPrev);
 bench('live row patch, including group map and blankness trim',()=>incrementalRows({...context,timelineEntries:entryNext.entries} as any,rowPrev));
}
const result={...summary,rowKinds:[...summary.rowKinds]};
writeFileSync(runDir+'/results.json',JSON.stringify(result,null,2));
console.log(JSON.stringify({runDir,...result},null,2));
