#!/usr/bin/env python3
from __future__ import annotations
import argparse,json
from pathlib import Path
from PIL import Image

def rgb(path:Path)->Image.Image:
    im=Image.open(path);im.load();return im.convert("RGB")

def metric(a:Path,b:Path,page:int)->dict:
    x,y=rgb(a),rgb(b)
    if x.size!=y.size: raise RuntimeError(f"page {page}: {x.size} != {y.size}")
    total=changed=maxd=0
    for p,q in zip(x.getdata(),y.getdata()):
        ds=[abs(p[i]-q[i]) for i in range(3)];total+=sum(ds);maxd=max(maxd,*ds);changed+=bool(ds[0] or ds[1] or ds[2])
    n=x.width*x.height;mae=total/(3*255*n)
    return {"page":page,"similarity":1-mae,"mae":mae,"changed_pixels":changed,"changed_fraction":changed/n,"max_channel_delta":maxd}

def main()->int:
    ap=argparse.ArgumentParser();ap.add_argument("source_dir",type=Path);ap.add_argument("candidate_dir",type=Path);ap.add_argument("output",type=Path);args=ap.parse_args()
    src=sorted(args.source_dir.glob("page-*.png"));can=sorted(args.candidate_dir.glob("page-*.png"))
    if len(src)!=len(can) or not src: raise RuntimeError(f"set mismatch source={len(src)} candidate={len(can)}")
    rows=[metric(a,b,i) for i,(a,b) in enumerate(zip(src,can),1)]
    result={"schema":"pdf2html-source-pdf-pixel-evidence/v1","renderer":"pdftoppm 72dpi vs Chromium candidate","pages":len(rows),"mean_similarity":sum(r["similarity"] for r in rows)/len(rows),"minimum_similarity":min(r["similarity"] for r in rows),"maximum_changed_fraction":max(r["changed_fraction"] for r in rows),"max_channel_delta":max(r["max_channel_delta"] for r in rows),"worst_pages":sorted(rows,key=lambda r:r["similarity"])[:20],"rows":rows}
    args.output.write_text(json.dumps(result,indent=2)+"\n");print(json.dumps({k:v for k,v in result.items() if k not in {"rows","worst_pages"}},sort_keys=True));return 0
if __name__=="__main__": raise SystemExit(main())
