"""New candidate Runtime only versus saved R2 observations, including warm-up."""
import hashlib
import json
from pathlib import Path
import sys

sys.path[:0]=['/opt/contextra','/evidence/2026-11-01_contextra-fall-spatial-28clips_r01/helpers']
from common import load, read, evidence, sha
from app.fall_runtime import FallSettings,FallScenario
from app.contracts.frames import FrameEnvelope,PoseObservation
from app.pose_config import POSE_MODEL
from app.event_cooldown import EventCooldown

root=Path('/out');items=[];total=full=0
output=root/'replay-results';output.mkdir(mode=0o700)
for clip,spec in read(root/'specs.json').items():
    rows,selected,_,_,_=load(clip)
    ref_path=evidence(spec['reference']);refs=[json.loads(l) for l in ref_path.open()]
    assert len(rows)==len(refs)
    engine=FallScenario(FallSettings.parse({'enabled':True,'engine':'r2'}))
    clock=[0.];gate=EventCooldown(10,clock=lambda:clock[0]);engine.engine.event_admission=gate.allow
    count=red=events=clipped=0;differences=[];event_frames=[]
    path=output/(clip+'.jsonl')
    with path.open('x') as out:
        for row,ref in zip(rows,refs):
            assert (row['sample'],row['source_frame'],row['seconds'])==(ref['sample'],ref['source_frame'],ref['seconds'])
            clock[0]=row['seconds']-rows[0]['seconds'];w,h=row['width'],row['height']
            frame=FrameEnvelope('replay','cache',clip,clip+'-config',1,row['sample'],round(row['seconds']*1e9),0,w,h,'default-angles')
            poses=[];ids=[]
            for p,b in zip(row['persons'],ref['people']):
                box=tuple(v/(w if i%2==0 else h) for i,v in enumerate(p['bbox']))
                bounded=tuple(min(1.,max(0.,v)) for v in box);clipped+=bounded!=box
                poses.append(PoseObservation(frame,POSE_MODEL,p['score'],bounded,tuple((x/w,y/h,s) for x,y,s in p['keypoints'])))
                ids.append(b['track_id'])
            displays,issued=engine.evaluate(frame,tuple(poses),tuple(ids));result=engine.engine.last_result
            for key in ('hit','observable','display','display_hold','association_uncertain'):
                if result[key]!=ref['r2'][key]:differences.append([row['sample'],key])
            if bool(issued)!=bool(ref['r2']['event']):differences.append([row['sample'],'event'])
            expected=[(next(i for i,p in enumerate(ref['people']) if p['query']==c['query']),c['state']) for c in ref['r2']['colors']]
            colors=[(c['query'],c['state']) for c in result['colors']]
            if colors!=expected:differences.append([row['sample'],'colors'])
            confirmed=[i for i,d in enumerate(displays) if d.state=='FALL_CONFIRMED']
            if confirmed!=sorted(c['query'] for c in result['colors'] if c['state'] in ('RED_CURRENT','YELLOW_HOLD')):
                differences.append([row['sample'],'runtime_display'])
            out.write(json.dumps(dict(sample=row['sample'],source_frame=row['source_frame'],seconds=row['seconds'],
                displayed=ref['displayed'],hit=result['hit'],display=result['display'],display_hold=result['display_hold'],
                event=bool(issued),colors=colors))+'\n')
            if issued:event_frames.append(row['source_frame'])
            full+=1
            if ref['displayed']:count+=1;red+=result['hit'];events+=bool(issued)
    assert count==len(selected);total+=count
    item=dict(clip=clip,displayed=count,red=red,events=events,differences=differences,canonical_bbox_clips=clipped,
              cache_sha256=spec['cache_sha256'],reference_sha256=sha(ref_path),output_sha256=sha(path),event_frames=event_frames)
    items.append(item);print('REPLAY',clip,count,len(differences),flush=True)
assert len(items)==36 and total==20145 and full==20715
failed=[x['clip'] for x in items if x['differences']]
value=dict(status='PASS' if not failed else 'FAIL',inputs=36,displayed=total,full=full,
           red=sum(x['red'] for x in items),events=sum(x['events'] for x in items),failed=failed,items=items)
(root/'replay_result.json').write_text(json.dumps(value,indent=2))
raise SystemExit(bool(failed))
