"""Render timestamped CLI output into a readable video; retain the full original cast and transcript.
JSON output is projected to launch IDs, status, branch, and worktree path. No result values are invented.
"""
import pathlib,json,subprocess,textwrap,html,sys
root=pathlib.Path(sys.argv[1])
for side in ['before','after']:
    run=pathlib.Path((root/(side+'-run.txt')).read_text())
    cast=[json.loads(s) for s in (run/'terminal.cast').read_text().splitlines()]
    frames=run/'frames';frames.mkdir(exist_ok=True)
    lines=[];durations=[]
    def project(text):
        try: value=json.loads(text)
        except: return text
        keys=['threadId','runId','status','branch','worktreePath']
        def pick(x):return {k:x[k] for k in keys if k in x}
        if isinstance(value,list): return json.dumps([pick(x) for x in value])
        if 'thread' in value:return json.dumps({'thread':pick(value['thread'])})
        return json.dumps(value)
    for i,e in enumerate(cast[1:]):
        raw=e[2].replace('\r','').strip('\n')
        if raw.startswith('CASE:'):lines=[]
        value=project(raw)
        for line in value.splitlines() or ['']:
            lines+=textwrap.wrap(line,112,replace_whitespace=False,drop_whitespace=False) or ['']
        lines=lines[-33:]
        title=side.upper()+' | '+ ('1a2a4a5e9684585c31eb55c1974fb32f8b1db2b7' if side=='before' else '9d2c911b27d85bba9d5002e17deead519837d517')
        svg=['<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="900"><rect width="1440" height="900" fill="#10151d"/>']
        def txt(y,s,color,size=18):svg.append(f'<text x="30" y="{y}" fill="{color}" font-family="DejaVu Sans Mono" font-size="{size}">{html.escape(s)}</text>')
        txt(38,title,'#f1f5f9',21)
        txt(70,'Linux | real dev server + CLI | fresh DB | synthetic Git seed | dark terminal','#a3b4c8')
        txt(99,'Timestamped output replay; JSON fields projected. Full transcript attached.','#a3b4c8')
        txt(128,'Paths and configured model redacted | elapsed '+str(e[0])+'s','#a3b4c8')
        for n,line in enumerate(lines):txt(170+n*21,line,'#85d6b1' if line.startswith('$ ') else '#e2e8f0')
        svg.append('</svg>')
        source=frames/f'{i:04}.svg';png=frames/f'{i:04}.png';source.write_text(''.join(svg))
        if not png.exists(): subprocess.run(['convert',str(source),str(png)],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.PIPE)
        following=cast[i+2][0] if i+2<len(cast) else e[0]+5
        hold = 3 if i+2<len(cast) and cast[i+2][2].strip().startswith('CASE:') else 0.6
        durations.append((png,max(hold,following-e[0])))
    playlist=run/'frames.txt'
    playlist.write_text(''.join(f"file '{p}'\nduration {d}\n" for p,d in durations)+f"file '{durations[-1][0]}'\n")
    subprocess.run(['ffmpeg','-y','-v','error','-f','concat','-safe','0','-i',str(playlist),'-vf','fps=10','-c:v','libx264','-pix_fmt','yuv420p','-crf','20','-movflags','+faststart',str(root/(side+'.mp4'))],check=True)
