"""Build a separate delivery folder from this run's existing files only."""
import csv
import hashlib
import json
import pathlib
import shutil

root = pathlib.Path(__file__).resolve().parent.parent
dest = root / 'Contextra_Retry7_Frame_Review'
dest.mkdir()
assert shutil.disk_usage(root).free >= 15 * 1024**3
results = json.loads((root / 'reports/batch_results.json').read_text())
names = {x['camera_id']: x['name'] for x in json.loads((root / 'manifest.json').read_text())['targets']}

def digest(p):
    h = hashlib.sha256()
    with p.open('rb') as f:
        for chunk in iter(lambda: f.read(1048576), b''):
            h.update(chunk)
    return h.hexdigest()

rows = []
for item in results:
    cid = item['camera_id']
    source = root / f'camera{cid}'
    target = dest / f'camera{cid}'
    target.mkdir()
    row = dict(camera_id=cid, name=names[cid], status=item['status'], frames='', elapsed_seconds='', reason='')
    if item['status'] == 'READY':
        v = json.loads((source / 'reports/video_validation.json').read_text())
        for name in (f'camera{cid}_original.mp4', f'camera{cid}_review.mp4', f'camera{cid}_frame_map.csv'):
            shutil.copy2(source / 'inputs' / name, target / name)
            assert digest(target / name) == v[name]['sha256']
        shutil.copy2(source / 'reports/video_validation.json', target / 'validation.json')
        row.update(frames=v['frame_count'], elapsed_seconds=v['elapsed_seconds'])
    else:
        p = source / 'reports/record_execution.json'
        if p.exists():
            shutil.copy2(p, target / 'capture_failure.json')
            text = json.loads(p.read_text()).get('output', '')
            row['reason'] = 'RTSP 401 Unauthorized; one attempt; no further retry' if '401 Unauthorized' in text else 'See capture_failure.json'
    rows.append(row)
with (dest / 'camera_list.csv').open('x', encoding='utf-8-sig', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=list(rows[0]))
    writer.writeheader(); writer.writerows(rows)
for name in ('operational_comparison.json', 'common_failure.json'):
    p = root / 'reports' / name
    if p.exists(): shutil.copy2(p, dest / name)
ready = sum(x['status'] == 'READY' for x in results)
(dest / '00_README_ko.md').write_text(f'''# 미완료7채널 추가1회 녹화 결과

대상 camera12/13/17/18/19/20/21 중 완료{ready}채널. 상세 상태는 camera_list.csv를 따른다.
이번 폴더는 이전 완료9개와 별도 자료이며 기존 영상·실패 기록은 변경하지 않았다.

- 성공 채널에는 무표시 원본·Frame 번호/경과시간 검토본·Frame 대응표·검증을 제공한다.
- 실패 채널은 오류 기록만 있으며 자동 재수신·인증 변경은 하지 않았다.
- Frame1은 이번 저장 파일의 첫 디코딩 Frame이다. Runtime frameSeq·송출 원본 번호·반복 시작점이 아니다.
- 60초 목표로 수신했다. 마지막 Frame 시각과 container duration은 마지막 Frame 길이 등에 따라 다를 수 있다.
- 실제 상대PTS와 정상 속도를 유지했다. FPS 추정 metadata는 다를 수 있으므로 전체Frame/PTS 대응으로 검증한다.10FPS 변환·보간·복제·배속 없음.
- 원본/검토본 전체 디코딩·번호 연속성·크기/SHA256을 확인했다. 큰PTS간격은 원천Frame 손실 확정 근거가 아니다.
- bbox/skeleton·엔진 판정 없음. 반복 경계·행동·인원은 사용자 판단이다. 재편집은 후속 Frame 구간 지정 후 별도 요청한다.
- 운영 설정·채널 제어·모델 추론 없음. 전후 snapshot 일치를 장시간 운영 무영향으로 확대하지 않는다.
- 서버 다운로드용 폴더이며 사용자 Windows로 실제 복사한 것은 아니다.
''', encoding='utf-8')
checks = {str(p.relative_to(dest)): {'bytes': p.stat().st_size, 'sha256': digest(p)} for p in sorted(dest.rglob('*')) if p.is_file()}
(dest / 'SHA256.json').write_text(json.dumps(checks, ensure_ascii=False, indent=2))
print(json.dumps({'ready':ready, 'files':len(checks), 'bytes':sum(x['bytes'] for x in checks.values())}))
