import bootstrap
from bootstrap import PRIOR,SOURCE
import csv,json
from pathlib import Path
from data_robust import OUT,read,sha,lines

root=OUT/'download';specs=read(OUT/'specs.json');total=0;table=[]
for clip,spec in specs.items():
 d=root/clip/'data';v=read(d/'video_validation.json');r=read(d/'replay_validation.json')
 assert v['status']==r['status']=='PASS'
 assert v['frames']==len(spec['selected_samples'])
 assert sha(root/clip/'B_R5_Space_Review.mp4')==v['sha256']
 frames=list(csv.DictReader((d/'B_R5_Space_Review_frame_map.csv').open()))
 display=read(d/'B_R5_Space_Review_display.json');trace=[x for x in lines(d/'R5_diagnostics.jsonl') if x['displayed']]
 assert len(frames)==len(display)==len(trace)==v['frames']
 for m,s,t in zip(frames,display,trace):
  assert int(m['source_frame'])==t['source_frame'] and abs(float(m['source_seconds'])-t['seconds'])<1e-8
  assert (s['hit'],s['event'],s['display'])==(t['hit'],t['episode']['event'],t['episode']['display'])
  assert s['state']==('RED' if t['hit'] else 'YELLOW' if t['episode']['display'] else 'BLUE')
  if clip=='SELF_L02':m['l02_frame']=int(m['source_frame'])-2542;m['l01_frame']=int(m['source_frame'])
 if clip=='SELF_L02':
  with (d/'L02_number_map.csv').open('w',newline='') as f:
   w=csv.DictWriter(f,fieldnames=frames[0]);w.writeheader();w.writerows(frames)
 total+=v['frames'];table.append(dict(input=clip,video=clip+'/B_R5_Space_Review.mp4',frames=v['frames'],red=len(r['red_frames']),events=len(r['event_frames']),sha256=v['sha256']))
assert len(table)==12 and total==11939
for name,digest in read(OUT/'baseline_fingerprints.json').items():assert sha(Path('/evidence')/name)==digest
with (root/'video_list.csv').open('w',encoding='utf-8-sig',newline='') as f:
 w=csv.DictWriter(f,fieldnames=table[0]);w.writeheader();w.writerows(table)
(root/'00_README_ko.txt').write_text('R5 고정 후보 공간 검토: 12영상, 11939관측. 운영 미적용.\n청색=공간 후보, 빨강=현재 최종 조건, 노랑=마지막 충족 뒤1초 유지. 새 시험 Event와 빨강은 별개입니다.\nR5 조건은 과거 probe와 동일하며 전체 집계를 재현했습니다. 기준/R4/ApproachLink 재실행과 모델 추론은 없습니다.\nL02의 source_frame/PTS는 기존 L01 기준입니다. 화면과 L02_number_map.csv에 L02 번호도 제공합니다.\n입력별 data에 원시 진단, 전체 빨강/Event Frame, 기준 대비 변경 위치, RGB/출력 대응과 전체 디코딩 검증을 보관합니다.\nCxx/L02는 같은 장면의 다른 입력이며 독립 사례로 합산하지 않습니다. 점수와 사용자 참고 구간은 정확한 GT가 아닙니다.\n',encoding='utf-8')
fingerprints={str(p):sha(p) for p in [PRIOR/'helpers/refined.py',PRIOR/'helpers/probe_robust.py',PRIOR/'reports/probe_robust_weak.json',SOURCE/'candidates.json',*sorted((OUT/'helpers').glob('*.py'))]}
(root/'conditions.json').write_text(json.dumps(dict(R5=read(PRIOR/'reports/probe_robust_weak.json')['R5']['config'],base=read(SOURCE/'candidates.json')['candidates'][0]['config'],fingerprints=fingerprints),indent=2))
(root/'validation.json').write_text(json.dumps(dict(status='PASS',inputs=12,observations=total,reproduction='exact saved R5 aggregates and recorded locations',frame_color_event_alignment=True,baseline_hashes=True,videos=table),indent=2))
manifest={str(p.relative_to(root)):sha(p) for p in sorted(root.rglob('*')) if p.is_file() and p.name!='sha256_manifest.json'}
(root/'sha256_manifest.json').write_text(json.dumps(manifest,indent=2));print('PACKAGE_PASS',len(manifest),total)
