"""Rebuild evidence from each run directory. All input files are included in the archive."""
import json,sys,pathlib,subprocess
from PIL import Image,ImageDraw,ImageFont
root=pathlib.Path(sys.argv[1])
font='/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
f=lambda n:ImageFont.truetype(font,n)
for name in sys.argv[2:]:
 p=root/name
 summary=json.loads((p/'summary.json').read_text()); samples=json.loads((p/'samples.json').read_text());events=json.loads((p/'command-events.json').read_text())
 shots=sorted(p.glob('running-*.png'),key=lambda p:int(p.stem.split('-')[1]));shot=shots[len(shots)//2];s=samples[int(shot.stem.split('-')[1])-1];item=summary['items'][-1]
 frames=json.loads((p/'frames.json').read_text());target=frames[0]['timestamp']+s['elapsedMs']/1000;source=min(frames,key=lambda a:abs(a['timestamp']-target));im=Image.open(p/('frame-%05d.jpg'%source['n'])).convert('RGB');d=ImageDraw.Draw(im);d.rectangle((900,0,1440,1000),fill='#111d2b')
 def txt(y,t,size=20,col='#dce7f2'): d.text((928,y),t,font=f(size),fill=col)
 txt(28,'SQLITE • READ ONLY',24,'#8ecbff');txt(70,'Observation outside the app',17);txt(112,summary['commit'][:10],24);txt(150,'Real Pi 1.5.2 / Haiku 5.5',18);txt(182,'300 lines × 700 x • 50 ms per line',17)
 txt(242,'WHILE OUTPUT WAS RUNNING',17,'#8ecbff');txt(277,f"{s['elapsedMs']/1000:.1f}s after dispatch",18);txt(315,f"{s['rows']:,} stored rows",30);txt(359,f"{s['bytes']:,} payload bytes",25);txt(405,'Output visible in the app at left',17)
 txt(463,'AFTER COMMAND COMPLETED',17,'#8ecbff');txt(500,f"{item['rows']:,} stored rows",30);txt(545,f"{item['bytes']:,} payload bytes",25);txt(588,f"{item['largest']:,} largest row",19)
 # Cumulative persisted bytes from the read-only samples, fixed scale on both sides.
 x0,y0,x1,y1=943,847,1395,685;d.line((x0,y1,x0,y0,x1,y0),fill='#75899c',width=2)
 pts=[(x0+min(s['elapsedMs']/23000,1)*(x1-x0),y0-min(s['bytes']/8000000,1)*(y0-y1)) for s in samples]
 if len(pts)>1:d.line(pts,fill='#63b3ff',width=3)
 txt(642,'Stored payload growth (0–8 MB)',17);txt(864,'0                         23 seconds',16)
 txt(922,'Video frame; generated test output.',16);txt(950,'No replay or synthetic event injection.',16)
 im.save(p/'evidence.png')
 # CDP frames retain real timing; no simulated UI or chart frames.
 frames=json.loads((p/'frames.json').read_text());lines=[]
 for a,b in zip(frames,frames[1:]):
  lines.extend([f"file '{(p / ('frame-%05d.jpg'%a['n'])).resolve()}'",f"duration {max(0.001,b['timestamp']-a['timestamp']):.6f}"])
 if frames:lines.extend([f"file '{(p / ('frame-%05d.jpg'%frames[-1]['n'])).resolve()}'",'duration 1'])
 (p/'encode-list.txt').write_text('\n'.join(lines))
 subprocess.run(['ffmpeg','-y','-loglevel','error','-f','concat','-safe','0','-i',str(p/'encode-list.txt'),'-vf','fps=25','-c:v','libx264','-preset','fast','-crf','23','-pix_fmt','yuv420p','-movflags','+faststart',str(p/'recording.mp4')],check=True)
 print(name,summary['items'], 'shot',shot.name)
