import csv
import hashlib
import itertools
import json
import math
import pathlib

ROOT=pathlib.Path('/trial'); OUT=ROOT/'download'
clips=sorted((ROOT/'clips').iterdir()); assert len(clips)==7
names={'stgcnpp':'STGCNpp','posec3d':'PoseC3D','ava_slowonly':'AVA_SlowOnly','yowov2':'YOWOv2'}
catalog=[];checks=[]
for clip in clips:
    rows=json.loads((clip/'inputs/pose.json').read_text()); camera=clip.name=='01_camera24'
    indices=list(range(100,301)) if camera else list(range(len(rows)))
    assert len({r['source_frame'] for r in rows})==len(rows)
    assert all(b['seconds']>a['seconds'] for a,b in zip(rows,rows[1:]))
    invalid_boxes=sum(not all(math.isfinite(v) for v in p['bbox']) or p['bbox'][2]<=p['bbox'][0] or p['bbox'][3]<=p['bbox'][1] for r in rows for p in r['persons'])
    assert invalid_boxes==0
    for name,title in names.items():
        data=OUT/clip.name/'data';results=json.loads((data/f'{name}_raw.json').read_text())
        assert [r['sample'] for r in results]==indices
        assert json.loads((data/f'{name}_summary.json').read_text())['status']=='PASS'
        for r in results:
            i=r['sample'];p=rows[i]
            assert r['source_frame']==p['source_frame'] and r['seconds']==p['seconds']
            expected=list(range(i-8,i+8)) if name in ('stgcnpp','posec3d') else ([i-10,i-5,i,i+5] if name=='ava_slowonly' else list(range(i-15,i+1)))
            if min(expected)<0 or max(expected)>=len(rows):
                assert r['window'] is None and r['status']=='INSUFFICIENT_WINDOW' and not r['items'];continue
            assert r['window']['samples']==expected
            assert r['window']['source_frames']==[rows[j]['source_frame'] for j in expected]
            if name=='stgcnpp':
                candidates=[]
                for a,b in itertools.combinations([o for o in p['persons'] if o['track_id'] is not None],2):
                    A=a['bbox'];B=b['bbox'];h=min(A[3]-A[1],B[3]-B[1])
                    dx=max(0,max(A[0],B[0])-min(A[2],B[2]));dy=max(0,max(A[1],B[1])-min(A[3],B[3]))
                    if math.hypot(dx,dy)/h<=.25:candidates.append(tuple(sorted([a['track_id'],b['track_id']])))
                assert sorted(tuple(x['ids']) for x in r['items'])==sorted(candidates)
            if name=='ava_slowonly':
                assert [(x['track_id'],x['query'],x['bbox']) for x in r['items']]==[(x['track_id'],x['query'],x['bbox']) for x in p['persons']]
            if name=='yowov2': assert [x['display_id'] for x in r['items']]==['Y'+str(j+1) for j in range(len(r['items']))]
            for item in r['items']:
                if 'scores' not in item:continue
                assert len(item['scores'])==(60 if name in ('stgcnpp','posec3d') else 80)
                assert all(math.isfinite(v) and 0<=v<=1 for v in item['scores'])
                if name in ('stgcnpp','posec3d'): assert item['input_shape'][3]==16
        maps=[]
        for limit in ([9,5] if name in ('stgcnpp','posec3d') else [10,5]):
            file=OUT/clip.name/f'{title}_aux{limit:02d}.mp4'
            report=json.loads((data/f'{title}_aux{limit:02d}_validation.json').read_text())
            assert report['status']=='PASS' and report['frames']==len(indices) and report['max_pts_error']<.001
            assert hashlib.sha256(file.read_bytes()).hexdigest()==report['sha256']
            mapping=list(csv.DictReader((data/f'{title}_aux{limit:02d}_frame_map.csv').open()))
            assert len(mapping)==len(results)
            for m,r in zip(mapping,results):
                assert int(m['sample'])==r['sample'] and int(m['source_frame'])==r['source_frame'] and float(m['source_seconds'])==r['seconds']
                assert abs(float(m['output_pts_seconds'])-(r['seconds']-results[0]['seconds']))<.001
            maps.append(mapping)
            catalog.append(dict(folder=clip.name,file=file.name,model=title,aux_count=limit,frames=report['frames'],width=report['resolution'][0],height=report['resolution'][1],last_pts=report['duration_last_pts'],sha256=report['sha256']))
        assert maps[0]==maps[1]
        checks.append(dict(clip=clip.name,model=name,observations=len(indices),insufficient_windows=sum(r.get('status')=='INSUFFICIENT_WINDOW' for r in results),output_items=sum('scores' in it for r in results for it in r['items']),invalid_bbox=invalid_boxes))
assert len(catalog)==56 and len(list(OUT.glob('*/*.mp4')))==56
with (OUT/'00_video_index.csv').open('w',encoding='utf-8-sig',newline='') as f:
    w=csv.DictWriter(f,fieldnames=catalog[0]);w.writeheader();w.writerows(catalog)
verify=OUT/'validation';verify.mkdir(exist_ok=True)
(verify/'technical_audit.json').write_text(json.dumps(dict(status='PASS',videos=56,model_clip_units=28,checks=checks),ensure_ascii=False,indent=2))
hashes={str(p.relative_to(OUT)):hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(OUT.rglob('*')) if p.is_file() and p.name!='SHA256SUMS.json'}
(OUT/'SHA256SUMS.json').write_text(json.dumps(hashes,ensure_ascii=False,indent=2))
print(json.dumps(dict(status='PASS',videos=56,files=len(hashes),bytes=sum(p.stat().st_size for p in OUT.rglob('*') if p.is_file()),checks=checks),ensure_ascii=False))
