from pathlib import Path
import json,os,subprocess,sys,urllib.parse,hashlib
from PIL import Image,ImageChops
ROOT=Path(__file__).resolve().parent
CHROMIUM=os.environ.get('CHROMIUM_BIN') or '/usr/bin/chromium'

def load():
    stories=json.loads((ROOT/'catalog/stories.json').read_text('utf-8'))['stories']
    env=json.loads((ROOT/'config/environment.json').read_text('utf-8'))
    return stories,env

def envsig(env):
    core={k:env.get(k) for k in ['browser_family','viewport','device_scale_factor','font_assumption','clock_policy','random_policy','animation_policy','qualified_portability']}
    return hashlib.sha256(json.dumps(core,sort_keys=True).encode()).hexdigest()[:16]

def url_for(story,env,noise_override=None):
    noise=noise_override or ('random' if env.get('random_policy')=='uncontrolled' else 'fixed')
    q=urllib.parse.urlencode({'state':story['state'],'title':story['title'],'focus':'1' if story.get('focus') else '0','noise':noise})
    return (ROOT/'visual/render.html').resolve().as_uri()+'?'+q

def capture(story,out,env,noise_override=None):
    vp=env.get('viewport',[900,640]); out.parent.mkdir(parents=True,exist_ok=True)
    cmd=[CHROMIUM,'--headless=new','--no-sandbox','--disable-gpu','--hide-scrollbars','--force-device-scale-factor=1',f'--window-size={vp[0]},{vp[1]}',f'--screenshot={out}',url_for(story,env,noise_override)]
    cp=subprocess.run(cmd,capture_output=True,text=True,timeout=30)
    if cp.returncode!=0 or not out.is_file():
        raise SystemExit('chromium capture failed: '+cp.stderr[-1000:])

def diff(a,b):
    ia=Image.open(a).convert('RGB'); ib=Image.open(b).convert('RGB')
    if ia.size!=ib.size:return {'different_pixels':ia.size[0]*ia.size[1],'bbox':[0,0,*ia.size]}
    d=ImageChops.difference(ia,ib); bbox=d.getbbox()
    if not bbox:return {'different_pixels':0,'bbox':None}
    mask=d.convert('L').point(lambda x:255 if x else 0); hist=mask.histogram(); return {'different_pixels':sum(hist[1:]),'bbox':list(bbox)}

def main():
    mode=sys.argv[1] if len(sys.argv)>1 else 'compare'; stories,env=load(); only=os.environ.get('WEB018_STORY'); stories=[s for s in stories if not only or s['id']==only]
    base=ROOT/'baseline/images'; cur=ROOT/'current'; reports=[]
    if mode=='baseline':
        for s in stories:capture(s,base/f"{s['id']}.png",env)
        print(json.dumps({'mode':'baseline','stories':[s['id'] for s in stories],'environment_signature':envsig(env)},indent=2));return
    if mode=='compare':
        for s in stories:
            bp=base/f"{s['id']}.png"; cp=cur/f"{s['id']}.png"; capture(s,cp,env)
            if not bp.exists(): reports.append({'story':s['id'],'status':'NO_BASELINE'});continue
            r=diff(bp,cp); reports.append({'story':s['id'],'status':'MATCH' if r['different_pixels']==0 else 'DIFF',**r})
        print(json.dumps({'mode':'compare','environment_signature':envsig(env),'reports':reports},indent=2));return
    if mode=='noise-probe':
        s=stories[0]; a=cur/'noise-a.png'; b=cur/'noise-b.png'; capture(s,a,env,'random'); capture(s,b,env,'random'); print(json.dumps({'mode':'noise-probe',**diff(a,b)},indent=2));return
    raise SystemExit('mode must be baseline|compare|noise-probe')
if __name__=='__main__':main()
