#!/usr/bin/env python3
from pathlib import Path
from PIL import Image,ImageDraw,ImageFont
import numpy as np
import json,subprocess
ROOT=Path('/tmp/lead-28684-r1/evidence-capture')
OUT=ROOT/'assembled'
OUT.mkdir(exist_ok=True)
manifest=json.loads((ROOT/'capture-manifest.json').read_text())
font=ImageFont.truetype('/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',18)
def source(side,key,name):return Path(manifest[side][key]['run'])/name
pairs=[]
def pair(key,row,viewport,theme,bkey,bname,akey,aname,scroll='top'):
 b=source('before',bkey,bname+'.png');a=source('after',akey,aname+'.png')
 pairs.append(dict(key=key,row=row,viewport=viewport,theme=theme,scroll=scroll,before=str(b),after=str(a)))
pair('r03-dark','03','1440×1000','dark','n4','before-collapsed-dark-n4','n4','after-collapsed-dark-n4')
pair('r04-dark','04','1440×1000','dark','n5','before-collapsed-dark-n5','n5','after-collapsed-dark-n5')
pair('r07-dark','07','1440×1000','dark','paging-dark','before-rows07-10-collapsed-dark','paging-dark','after-rows07-10-collapsed-dark')
pair('r07-light','07','1440×1000','light','collapsed-light','before-collapsed-light-n10','collapsed-light','after-collapsed-light-n10')
pair('r07-mobile','07','390×844','dark','mobile-dark','before-mobile-dark-n10','mobile-dark','after-mobile-dark-n10')
for row in ['10','11','12']:
 pair('r'+row+'-dark',row,'1440×1000','dark','paging-dark','before-row'+row+'-dark','paging-dark','after-row'+row+'-dark')
pair('r11-light','11','1440×1000','light','expanded-light','before-expanded-light-n10','expanded-light','after-expanded-light-n10')
for row,key,viewport in [('13','short-dark','1440×640'),('14','floating-dark','1024×640')]:
 for scroll in ['top','bottom']:
  pair('r'+row+'-'+scroll,row,viewport,'dark',key,'before-'+key+'-expanded-'+scroll,key,'after-'+key+'-expanded-'+scroll,scroll)
stats=[]
for p in pairs:
 b=Image.open(p['before']).convert('RGB');a=Image.open(p['after']).convert('RGB')
 assert a.size==b.size,(p,a.size,b.size)
 expected=tuple(map(int,p['viewport'].split('×')));assert a.size==expected
 ba=np.array(b).astype(np.int16);aa=np.array(a).astype(np.int16)
 delta=np.abs(aa-ba).max(axis=2)
 for threshold,name in [(0,'diff0'),(16,'diffT')]:
  mask=delta>threshold
  bg=(np.array(b)*.20).astype(np.uint8)
  bg[mask]=[255,0,255]
  file=OUT/(p['key']+'-'+name+'.png');Image.fromarray(bg).save(file);p[name]=str(file)
 yy,xx=np.where(delta>0)
 bbox=[int(xx.min()),int(yy.min()),int(xx.max()+1),int(yy.max()+1)] if len(xx) else None
 p['stats']={'changedPixels0':int((delta>0).sum()),'changedPixels16':int((delta>16).sum()),'bbox':bbox,'maxChannelDelta':int(delta.max())}
 # No cropping or resizing: each labeled side retains its entire CSS viewport at DPR 1.
 sheet=Image.new('RGB',(b.width*2,b.height+62),(35,35,35));d=ImageDraw.Draw(sheet)
 d.text((12,7),f"Row {p['row']} · {p['viewport']} · {p['theme']} · {p['scroll']}",font=font,fill='white')
 d.text((12,34),'Before · 60aec28a9c',font=font,fill='white');d.text((b.width+12,34),'After · 7c0cf8340c',font=font,fill='white')
 sheet.paste(b,(0,62));sheet.paste(a,(b.width,62));file=OUT/(p['key']+'-sheet.png');sheet.save(file);p['sheet']=str(file)
 stats.append(p)
(ROOT/'pairs.json').write_text(json.dumps(stats,indent=2))
# Trim actual browser recordings. Times are relative to page creation and are retained in metadata.
recordings=[]
for side,keys in [('before',['paging-dark','short-dark','floating-dark']),('after',['paging-dark','short-dark','floating-dark','switch-dark'])]:
 for key in keys:
  run=manifest[side][key];meta=json.loads((Path(run['run'])/'metadata.json').read_text());events=meta['timeline']
  start=next(e['timeMs'] for e in events if e['action']=='recording-start')/1000
  end=next(e['timeMs'] for e in events if e['action']=='recording-end')/1000
  out=OUT/(side+'-'+key+'.mp4')
  subprocess.run(['ffmpeg','-hide_banner','-loglevel','error','-y','-ss',str(start),'-i',run['raw'],'-t',str(end-start),'-an','-c:v','libx264','-crf','15','-preset','veryfast','-pix_fmt','yuv420p','-movflags','+faststart',str(out)],check=True)
  recordings.append({'side':side,'scenario':key,'file':str(out),'start':start,'end':end,'events':[{**e,'videoSeconds':round(e['timeMs']/1000-start,3)} for e in events if start*1000<=e['timeMs']<=end*1000]})
(ROOT/'recordings.json').write_text(json.dumps(recordings,indent=2))
print(json.dumps([{'key':p['key'],**p['stats']} for p in stats],indent=2))
