#!/usr/bin/env python3
"""Four-row repair, adapted from the supplied repair-comparisons.py.
Requires curl and ImageMagick convert/compare. No resampling or noise removal.
All boxes are half-open ORIGINAL source pixels; opaque masks are identical on both sides.
"""
import hashlib
import json
from pathlib import Path
import subprocess
import sys
HOST='https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/'
ROWS={
 'S2':dict(before='41e0c6a92b20ec35/before-S2-clean.png',after='2b18f4fd8e6118be/after-S2-clean.png',crop=(0,24,1080,810),masks=[]),
 'I3-up':dict(before='278bfdecf4b4a46f/before-I3-anchor-up.png',after='ed035e8374b354de/after-I3-anchor-up.png',crop=(0,30,375,812),masks=[]),
 'N1a':dict(before='11832851b0d1ce23/before-keyboard-up.png',after='82f35ac00186149e/after-keyboard-up.png',crop=(0,40,375,812),masks=[('generated title',(49,54,196,78)),('before reply timestamp',(78,139,130,154)),('after sent timestamp',(279,155,327,170)),('after reply timestamp',(78,227,130,242))]),
 'N1b':dict(before='4ac1edb11a5ca068/before-keyboard-down.png',after='0c9fb587705aff41/after-keyboard-down.png',crop=(0,40,375,812),masks=[('generated title',(49,54,196,78)),('before reply timestamp',(78,139,130,154)),('after sent timestamp',(279,155,327,170)),('after reply timestamp',(78,227,130,242))]),
}
def compare(a,b,out,fuzz):
 r=subprocess.run(['compare','-metric','AE','-fuzz',fuzz,'-highlight-color','#ff00ff','-lowlight-color','#151515','-compose','src',str(a),str(b),str(out)],capture_output=True,text=True)
 if r.returncode not in (0,1):raise RuntimeError(r.stderr)
 return int(r.stderr)

def render(root,row,config):
 left,top,right,bottom=config['crop'];outputs=[]
 for side in ('before','after'):
  src=root/f'{row}-{side}-source.png';out=root/f'{row}-{side}-crop.png'
  subprocess.run(['curl','-fsSL',HOST+config[side],'-o',str(src)],check=True)
  cmd=['convert',str(src),'-alpha','off']
  if config['masks']:
   cmd+=['-fill','#888888','-draw',' '.join(f'rectangle {x0},{y0} {x1-1},{y1-1}' for _,(x0,y0,x1,y1) in config['masks'])]
  cmd+=['-crop',f'{right-left}x{bottom-top}+{left}+{top}','+repage',str(out)]
  subprocess.run(cmd,check=True);outputs.append(out)
 exact=compare(*outputs,root/f'{row}-Diff0.png','0%')
 threshold=compare(*outputs,root/f'{row}-DiffT-5pct.png','5%')
 files=[root/f'{row}-{side}-source.png' for side in ('before','after')]+outputs+[root/f'{row}-Diff0.png',root/f'{row}-DiffT-5pct.png']
 return dict(source_urls={s:HOST+config[s] for s in ('before','after')},crop_half_open=config['crop'],masks=config['masks'],diff0_pixels=exact,diffT_5pct_pixels=threshold,sha256={f.name:hashlib.sha256(f.read_bytes()).hexdigest() for f in files})
def main():
 root=Path(sys.argv[1]).resolve();root.mkdir(parents=True,exist_ok=True)
 manifest={row:render(root,row,config) for row,config in ROWS.items()}
 (root/'manifest.json').write_text(json.dumps(manifest,indent=2)+'\n')
 (root/'masks.json').write_text(json.dumps({r:dict(crop_half_open=c['crop'],masks=c['masks'],mask_color='#888888') for r,c in ROWS.items()},indent=2)+'\n')
 print(json.dumps({r:{k:v for k,v in c.items() if k.startswith('diff')} for r,c in manifest.items()},indent=2))
if __name__=='__main__':main()
