#!/usr/bin/env python3
"""Real CLI bootstrap comparison. Usage: driver.py CHECKOUT before|after OUTPUT_ROOT.
Requires Python Pillow and ffmpeg. Each run creates a new timestamped directory.
The recording is a live observation dashboard, not an Electron/window capture.
Raw subprocess logs stay private. The public transcript contains HTTP auth fields
(including sessionCookieName and traceId), lifecycle observations and error tags.
Issued access tokens are deliberately never printed.
"""
import os, sys, subprocess, time, threading, json, socket, urllib.request, urllib.error, pathlib, datetime, resource
from PIL import Image, ImageDraw, ImageFont
checkout, side, root = sys.argv[1:]
out = pathlib.Path(root) / (side + '-' + datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ'))
out.mkdir(parents=True)
frames=out/'frames'; frames.mkdir()
font=ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',18)
small=ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',16)
events=[]; lock=threading.Lock(); start=time.monotonic(); finished=False
sha=subprocess.check_output(['git','rev-parse','HEAD'],cwd=checkout,text=True).strip()
log=open(out/(side+'.txt'),'w',buffering=1)
def emit(msg):
    line=f'{time.monotonic()-start:06.1f}s {msg}'
    with lock: events.append(line); log.write(line+'\n'); print(line,flush=True)
def record():
    i=0
    while not finished:
        im=Image.new('RGB',(1600,1000),'#0d1724'); d=ImageDraw.Draw(im)
        d.text((25,20),f'{side.upper()} | real Linux server CLI | fresh isolated home per case',font=font,fill='#ffffff')
        d.text((25,50),sha,font=font,fill='#85bfff')
        d.text((25,80),f'LIVE observation dashboard | elapsed {time.monotonic()-start:.1f}s | 1 frame/s',font=small,fill='#afbac8')
        with lock: lines=list(events)
        # Wrap long JSON verbatim; no brace substitutions or text truncation.
        wrapped=[]
        for line in lines:
            wrapped.extend([line[j:j+148] for j in range(0,len(line),148)])
        for n,line in enumerate(wrapped[-35:]): d.text((25,125+n*23),line,font=small,fill='#dbe8f5')
        im.save(frames/f'{i:05d}.png'); i+=1
        time.sleep(1)
thread=threading.Thread(target=record); thread.start()
def request(port,path,body=None):
    try:
        req=urllib.request.Request(f'http://127.0.0.1:{port}'+path,data=None if body is None else json.dumps(body).encode(),headers={'Content-Type':'application/json'})
        with urllib.request.urlopen(req,timeout=.3) as r: return r.status,json.load(r)
    except urllib.error.HTTPError as e:
        try: return e.code,json.load(e)
        except: return e.code,{}
    except Exception: return None,None

def run(case,kind,limit):
    home=out/(case+'-home')
    s=socket.socket(); s.bind(('127.0.0.1',0)); port=s.getsockname()[1]; s.close()
    args=['node',str(pathlib.Path(checkout)/'apps/server/dist/bin.mjs'),'serve','--base-dir',str(home),'--host','127.0.0.1','--port',str(port),'--no-browser']
    extra=[]; r=w=None
    if kind=='closed': args+=['--bootstrap-fd','57']
    elif kind=='directory':
        r=os.open(out,os.O_RDONLY); extra=[r]; args+=['--bootstrap-fd',str(r)]
    elif kind=='nonblocking':
        r,w=os.pipe2(os.O_NONBLOCK); os.dup2(r,9); os.close(r); r=9; extra=[r]; args+=['--bootstrap-fd','9']
    elif kind!='none': args+=['--bootstrap-fd','0']
    emit(f'CASE {case}: launch; descriptor={kind}; deadline={limit}s')
    raw=open(out/(case+'.private.log'),'wb')
    env={k:v for k,v in os.environ.items() if not k.startswith(('T3CODE_','VITE_','OTEL_'))}; env['T3CODE_DEV_SEED']='0'
    p=subprocess.Popen(args,stdin=subprocess.PIPE,stdout=raw,stderr=raw,cwd=checkout,env=env,pass_fds=extra,preexec_fn=lambda:resource.setrlimit(resource.RLIMIT_CORE,(0,0)))
    t=time.monotonic(); ready=False; sent=False; error_tags=set(); early=False; exchanged=False
    if kind=='empty': p.stdin.close(); emit('  writer closed immediately without an envelope')
    while time.monotonic()-t<limit:
        elapsed=time.monotonic()-t
        raw.flush(); text=(out/(case+'.private.log')).read_text(errors='replace')
        import re
        for tag in re.findall(r'Bootstrap\w+Error',text):
            if tag not in error_tags: error_tags.add(tag); emit(f'  server reports {tag} at +{elapsed:.1f}s')
        if kind=='delay' and elapsed>=12 and not sent:
            envelope=dict(mode='desktop',noBrowser=True,port=port,host='127.0.0.1',desktopBootstrapToken='synthetic-proof-credential',shellEnvironmentPrepared=True,tailscaleServeEnabled=False,tailscaleServePort=443)
            try: p.stdin.write((json.dumps(envelope)+'\n').encode()); p.stdin.flush(); emit(f'  valid desktop envelope delivered at +{elapsed:.1f}s')
            except BrokenPipeError: emit('  envelope delivery failed: BrokenPipeError')
            sent=True
        if p.poll() is not None:
            emit(f'  process exited naturally: code={p.returncode}, elapsed={elapsed:.1f}s, writer_still_open={kind in ("timeout","nonblocking")}'); break
        if not ready:
            status,data=request(port,'/api/auth/session')
            if status==200:
                ready=True; early=elapsed<12
                emit(f'  HTTP ready at +{elapsed:.1f}s; auth='+json.dumps(data.get('auth',data),separators=(',',':')))
        if ready and not exchanged and (kind!='delay' or sent and elapsed>=13):
            exchanged=True
            status,data=request(port,'/api/auth/browser-session',{'credential':'synthetic-proof-credential'})
            emit('  credential exchange: HTTP '+str(status)+' '+json.dumps(data,separators=(',',':')))
            if kind=='delay': emit(f'  ready_before_envelope={early}')
            if kind!='timeout': break
        time.sleep(.1)
    else: emit(f'  observation deadline reached: process_alive={p.poll() is None}; writer kept open throughout')
    emit(f'  server_state_created={home.exists()}')
    if p.poll() is None:
        p.terminate()
        try:p.wait(timeout=8)
        except subprocess.TimeoutExpired:p.kill();p.wait()
        emit('  driver stopped its captured child PID after observation')
    if not p.stdin.closed: p.stdin.close()
    if r is not None: os.close(r)
    if w is not None: os.close(w)
    raw.close()
try:
    emit('Built CLI; no test doubles; synthetic data; HTTP responses printed without edits.')
    run('empty-input','empty',10)
    run('delayed-envelope','delay',22)
    run('held-open-timeout','timeout',38)
    run('unavailable-fd57','closed',10)
    run('nonblocking-fd9','nonblocking',38)
    run('read-error-directory','directory',10)
    run('ordinary-web','none',10)
    emit('DONE: every child stopped; raw logs retained privately; no issued tokens printed.')
finally:
    time.sleep(1); finished=True;thread.join();log.close()
subprocess.run(['ffmpeg','-hide_banner','-loglevel','error','-framerate','1','-i',str(frames/'%05d.png'),'-c:v','libx264','-preset','fast','-crf','20','-pix_fmt','yuv420p','-y',str(out/(side+'.mp4'))],check=True)
print('OUTPUT_DIRECTORY='+str(out))
