import bootstrap
import sys,json,unittest,itertools
from pathlib import Path
sys.path.insert(0,'/out/source/tests')
suite=unittest.TestSuite(unittest.defaultTestLoader.loadTestsFromName(n) for n in ('test_fight_r5','test_fight_notification'))
tests=unittest.TextTestRunner(verbosity=2).run(suite)
from data_robust import OUT,read,paths,lines
from inputs import load
from app.fight_runtime import FightScenario,FightSettings
from app.contracts.frames import FrameEnvelope,PoseObservation
from app.pose_config import POSE_MODEL
from app.event_cooldown import EventCooldown
prior=Path('/evidence/2026-10-31_contextra-fight-r5-integration_r01/download')
summaries={};total=0
for clip,spec in read(OUT/'specs.json').items():
 rows,chosen,_,_,_=load(clip)
 expected=list(lines(prior/clip/'runtime_trace.jsonl'))
 runtime=FightScenario(FightSettings.parse({'enabled':True,'engine':'r5'}))
 clock=[0.];gate=EventCooldown(10,clock=lambda:clock[0]);runtime.engine.event_admission=gate.allow
 events=[];old_events=[];errors=[];trace=[]
 for row,old in itertools.zip_longest(rows,expected):
  assert row is not None and old is not None
  assert row['sample']==old['sample'] and row['seconds']==old['seconds']
  clock[0]=row['seconds'];w,h=row['width'],row['height']
  frame=FrameEnvelope('fixture',clip,'fixture',clip,1,row['sample'],round(clock[0]*1e9),1,w,h,'R5')
  poses=[PoseObservation(frame,POSE_MODEL,p['score'],tuple(max(0.,min(1.,v/(w if i%2==0 else h))) for i,v in enumerate(p['bbox'])),tuple((x/w,y/h,s) for x,y,s in p['keypoints'])) for p in row['persons']]
  _,emitted=runtime.evaluate(frame,poses,[p['track_id'] for p in row['persons']])
  current=runtime.engine.last_result
  if current['hit']!=old['hit'] or current['display_orders']!=old['display_orders']:errors.append(row['source_frame'])
  if emitted:events.append(row['source_frame'])
  if old['event']:old_events.append(row['source_frame'])
  trace.append(dict(frame=row['source_frame'],seconds=clock[0],hit=current['hit'],display_orders=current['display_orders'],event=bool(emitted),notification=runtime.engine.notification_status()))
 assert not errors,(clip,errors[:10])
 assert all(b['seconds']-a['seconds']>=10-1e-8 for a,b in zip([r for r in trace if r['event']],[r for r in trace if r['event']][1:]))
 total+=len(chosen)
 (OUT/'download'/ (clip+'.json')).write_text(json.dumps(trace))
 summaries[clip]=dict(observations=len(chosen),processed=len(trace),old_events=old_events,new_events=events,errors=errors)
 print('PASS',clip,len(chosen),len(old_events),len(events),flush=True)
assert total==18045 and len(summaries)==32
(OUT/'download/validation.json').write_text(json.dumps(dict(tests=tests.testsRun,unit_pass=tests.wasSuccessful(),inputs=32,observations=total,clips=summaries),indent=2))
raise SystemExit(not tests.wasSuccessful())
