#!/usr/bin/env python3
"""Record real CLI requests and persisted events. Supply checkout, workspace, output, side, SHA.
Set REPLAY_PROVIDER and REPLAY_RECORDING to the installed replay fixture identifiers.
The database is opened read-only; application commands create all state.
"""
import sys,os,subprocess,sqlite3,json,time,datetime,pathlib
checkout,workspace,out,side,sha=sys.argv[1:]
out=pathlib.Path(out);out.mkdir(parents=True)
start=time.monotonic(); records=[]; metrics=[]
def emit(text):
 r={'elapsed':round(time.monotonic()-start,3),'text':text};records.append(r)
 print(f"+{r['elapsed']:07.3f}s  {text}",flush=True)
 (out/'recording.json').write_text(json.dumps(records,indent=2))
def cli(args):return ['node','apps/server/src/bin.ts',*args,'--base-dir',str(pathlib.Path(checkout)/'.t3')]
def iso(s):return datetime.datetime.fromisoformat(s.replace('Z','+00:00'))
db=sqlite3.connect(f'file:{checkout}/.t3/userdata/statev2.sqlite?mode=ro',uri=True)
cursor=db.execute('select coalesce(max(sequence),0) from orchestration_events').fetchone()[0]
emit(f'{side.upper()} | commit {sha[:10]} | Linux web-server CLI | fresh synthetic state')
emit('Workspace: committed README + 40 x 100 MB untracked random files')
emit('Real git add -A calibration: 85.935 seconds (no injected delay)')
r=subprocess.run(cli(['project','add',workspace,'--title','Slow workspace']),cwd=checkout,capture_output=True,text=True)
if r.returncode and 'ProjectAlreadyExistsError' not in r.stdout:raise RuntimeError('project add failed')
emit('t3 project add: project registered')
thread=None
for turn in (1,2):
 emit(f'TURN {turn}: sending '+('go' if turn==1 else 'go again')+' with t3 thread '+('start --wait' if turn==1 else 'send --wait'))
 args=['thread','start',workspace,'go','--title','Slow workspace timing','--provider',os.environ['REPLAY_PROVIDER'],'--model',os.environ['REPLAY_RECORDING']] if turn==1 else ['thread','send',thread,'go again']
 proc=subprocess.Popen(cli([*args,'--wait','--json']),cwd=checkout,stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True)
 requested=None;provider_seen=False;output_seen=False; running_seen=False;end_seen=False;run_id=None
 while True:
  rows=db.execute('select sequence,event_type,occurred_at,payload_json from orchestration_events where sequence>? order by sequence',(cursor,)).fetchall()
  for seq,kind,at,payload in rows:
   cursor=seq;p=json.loads(payload)
   if kind=='run.created':
    run_id=p['id'];requested=iso(p['requestedAt']);thread=p['threadId']
    emit(f'TURN {turn}: prompt accepted at {p["requestedAt"]}')
   if kind=='run.updated' and p.get('id')==run_id:
    if p['status']=='running' and not running_seen:
     running_seen=True;emit(f'TURN {turn}: app run status = running; waiting for provider start')
    if p['status'] in ('completed','failed','stopped') and not end_seen:
     end_seen=True;emit(f'TURN {turn}: app run status = {p["status"]}')
   if kind=='provider-turn.updated' and run_id is not None and not provider_seen:
    provider_seen=True;delay=(iso(at)-requested).total_seconds()
    metrics.append({'turn':turn,'requestedAt':requested.isoformat(),'providerStartedAt':at,'delaySeconds':delay})
    emit(f'TURN {turn}: PROVIDER STARTED at {at}; prompt-to-provider = {delay:.3f}s')
   if kind=='turn-item.updated' and p.get('runId')==run_id and p.get('type') in ('reasoning','assistant_message') and not output_seen:
    output_seen=True;emit(f'TURN {turn}: provider output persisted ({p["type"]}): '+p.get('text','').split('.')[0]+'.')
  if proc.poll() is not None:break
  if time.monotonic()-start>400:proc.terminate();raise RuntimeError('capture exceeded budget')
  time.sleep(.1)
 stdout,stderr=proc.communicate()
 (out/f'turn-{turn}-private.json').write_text(stdout)
 if proc.returncode not in (0,2):raise RuntimeError('CLI turn failed: '+stderr)
 result=json.loads(stdout);thread=result['threadId']
 emit(f'TURN {turn}: CLI --wait returned status {result["status"]}')
 if not provider_seen:raise RuntimeError('No provider start observed')
 # --wait has settled the checkpoint effects, so the next prompt has no prior capture lock.
 time.sleep(1)
(out/'metrics.json').write_text(json.dumps(metrics,indent=2))
emit('Both requested turns observed through the real server and replay provider.')
(out/'transcript.txt').write_text('\n'.join(f"+{r['elapsed']:07.3f}s  {r['text']}" for r in records)+'\n')
