"""Check serialized conditions, identity references, causality and render contracts."""
import csv
import hashlib
import json
import os
from pathlib import Path

clip=os.environ['CLIP'];data=Path('/download')/clip/'data';prior=Path('/previous')/clip/'data'
summary=json.loads((data/'engine_summary.json').read_text());cfg=summary['settings']
raw=Path('/cache/inputs/pose.json').read_bytes();assert hashlib.sha256(raw).hexdigest()==summary['cache_sha256']
cache={r['sample']:r for r in json.loads(raw)}
rgb=json.loads((data/'input_rgb_sha256.json').read_text());assert rgb==json.loads((prior/'input_rgb_sha256.json').read_text())
prior_map=list(csv.DictReader((prior/'frame_map.csv').open()));all_events={}
for mode in ('A','B'):
    diagnostics=data/(mode+'_diagnostics.jsonl');assert hashlib.sha256(diagnostics.read_bytes()).hexdigest()==summary['modes'][mode]['diagnostics_sha256']
    rows=[json.loads(s) for s in diagnostics.read_text().splitlines()];shown=[r for r in rows if r['displayed']]
    maps=list(csv.DictReader((data/(mode+'_frame_map_v2.csv')).open()))
    assert maps==[{k:m[k] for k in maps[0]} for m in prior_map]
    assert len(shown)==len(maps)==summary['display_observations']
    event_times=[];count=0
    for r in rows:
        source=cache[r['sample']]
        assert (r['source_frame'],r['seconds'])==(source['source_frame'],source['seconds']) and r['causal']
        assert r['state_count']<=cfg['max_states'] and r['candidate_pairs']<=cfg['max_pairs']
        assert r['current_rules']==sorted({rule for p in r['pairs'] for rule in p['rules']})
        for p in r['pairs']:
            assert p['input_end']==r['seconds'] and (p['input_start'] is None or p['input_start']<=r['seconds'])
            assert abs(p['window_start']-(r['seconds']-cfg['window_seconds']))<1e-8
            assert all(p['window_start']-1e-8<=t<=r['seconds'] for t in p['pulse_times'])
            assert p['pulses']==len(p['pulse_times'])
            for order,tid,ref,box in zip(p['orders'],p['ids'],p['refs'],p['bboxes']):
                src=source['persons'][order]
                assert src['track_id']==tid and src['bbox']==box and src['score']>=cfg['person_score']
                assert ref==('T'+str(tid) if tid is not None else 'q'+str(src['query']))
            if mode=='A':assert all(tid is not None for tid in p['ids'])
            f=p['features'];strike=False;contact=False
            if f:
                for a in f['arms']:
                    expected=a['valid'] and a['relative_motion']>=cfg['strike_motion'] and a['toward']>=cfg['strike_toward'] and a['approach']>=cfg['strike_approach'] and a['hand_distance']<=cfg['strike_hand_distance']
                    assert a['strike']==expected
                    assert a['contact']==(a['valid'] and a['hand_distance']<=cfg['contact_hand_distance'])
                    assert a['dynamic']==(a['contact'] and a['relative_motion']>=cfg['contact_motion'] and f['pair_relative_change']>=cfg['pair_relative_change'])
                assert f['strike']==any(a['strike'] for a in f['arms'])
                assert f['dynamic']==any(a['dynamic'] for a in f['arms'])
                strike=f['strike'] and p['pulses']>=cfg['required_strike_pulses']
                contact=f['dynamic'] and p['contact_seconds']>=cfg['contact_seconds']-1e-8 and p['dynamic_seconds']>=cfg['dynamic_contact_seconds']-1e-8 and p['dynamic_samples']>=cfg['min_dynamic_samples']
            assert p['rules']==(['STRIKE'] if strike else [])+(['PUSH_GRAPPLE'] if contact else [])
            assert p['hit']==bool(p['rules']) and p['dynamic_seconds']<=p['contact_seconds']+1e-8<=cfg['window_seconds']+2e-8
            if mode=='A' and p['episode']['event']:assert p['hit']
        for e in r['events']:
            count+=1;assert e['test_event']==count and e['seconds']==r['seconds'] and e['rules']
            event_times.append(r['seconds'])
        assert r['event_count']==count
    assert all(b-a>=cfg['event_cooldown']-1e-8 for a,b in zip(event_times,event_times[1:]))
    for r,m in zip(shown,maps):assert r['sample']==int(m['sample']) and r['source_frame']==int(m['source_frame']) and r['seconds']==float(m['source_seconds'])
    video=json.loads((data/(mode+'_video_validation_v2.json')).read_text());assert video['status']=='PASS' and video['frames']==len(shown) and video['max_pts_error']==0
    all_events[mode]=dict(all=len(event_times),in_display=sum(len(r['events']) for r in shown),current_rule_frames=sum(bool(r['current_rules']) for r in shown))
result=dict(status='PASS',display_observations=summary['display_observations'],processed_observations=summary['processed_observations'],
    checks=['raw condition predicates','OR and cooldown','past-only inputs','source bbox/score/ID references','state bounds','original RGB/Frame/PTS','A/B equal frame maps'],events=all_events,semantic_accuracy_evaluated=False)
(data/'technical_audit_v2.json').write_text(json.dumps(result,indent=2));print(json.dumps(result))
