"""Package completed recordings only; never connects to a stream."""
import csv
import hashlib
import json
import pathlib
import shutil

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

root = pathlib.Path(__file__).resolve().parent.parent
manifest = json.loads((root / 'manifest.json').read_text())
results = json.loads((root / 'reports/batch_results.json').read_text())
names = {x['camera_id']: x['name'] for x in manifest['targets']}
dest = root / 'Contextra_Camera6-21_Frame_Review'
dest.mkdir()
assert shutil.disk_usage(root).free > 15 * 1024**3
listing = []
for result in results:
    cid = result['camera_id']
    row = dict(camera_id=cid, name=names[cid], status=result['status'], frames='', elapsed_seconds='', max_pts_error_seconds='', interval_outliers='', failure_reason='')
    if result['status'] == 'READY':
        source = root / f'camera{cid}'
        target = dest / f'camera{cid}'
        target.mkdir()
        validation = 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) == validation[name]['sha256']
        shutil.copy2(source / 'reports/video_validation.json', target / 'validation.json')
        row.update(frames=validation['frame_count'], elapsed_seconds=validation['elapsed_seconds'], max_pts_error_seconds=validation['max_mapping_error_seconds'], interval_outliers=validation['intervals_above_1p5x_median'])
    else:
        target = dest / f'camera{cid}'
        target.mkdir()
        error = root / f'camera{cid}/reports/record_execution.json'
        if error.exists():
            shutil.copy2(error, target / 'capture_failure.json')
            output = json.loads(error.read_text()).get('output', '')
            if '401 Unauthorized' in output:
                row['failure_reason'] = 'RTSP 401 Unauthorized; no retry'
            elif 'banner exchange' in output:
                row['failure_reason'] = 'SSH banner timeout before remote capture; no retry'
            else:
                row['failure_reason'] = 'See capture_failure.json; no retry'
    listing.append(row)
with (dest / 'camera_list.csv').open('x', encoding='utf-8-sig', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=list(listing[0]))
    writer.writeheader()
    writer.writerows(listing)
shutil.copy2(root / 'reports/operational_comparison.json', dest / 'operational_comparison.json')
shutil.copy2(root / 'reports/common_failure.json', dest / 'common_failure.json')
ready = sum(x['status'] == 'READY' for x in results)
(dest / '00_README_ko.md').write_text(f'''# camera6~21 Frame 검토 자료

승인16채널 중 완료 {ready}개. 실제ID와 Web 이름, 실패/보류 여부는 camera_list.csv를 따른다.
완료 채널은60초를 목표로1회 새 수신했다. 실패/미실행 채널을 녹화 완료로 해석하지 않는다. 원본은 무표시이며 검토본은 Frame 번호와 경과시간만 표시한다.

camera12/17은401 Unauthorized·0bytes, camera13은SSH banner timeout으로 녹화 명령 전달 실패다.
camera18의 도구 실행 전 확인에서도SSH255가 발생해 중단했으며18~21은미실행이다. 인증 갱신·자동 재시도하지 않았다. SSH timeout의 근본 원인은 미확정이다.

- Frame1은 저장된 파일의 첫 디코딩 Frame이다. Runtime 번호·송출 원본 번호·반복 시작점이 아니다.
- 재편집 요청은 camera ID와 검토본 Frame 시작~끝(양 끝 포함)을 적는다. 이번에는 반복 경계를 자르지 않았다.
- 실제 상대PTS를 유지했다. r_frame_rate 같은 컨테이너 추정 FPS는 재인코딩 후 다르게 표기될 수 있으므로 프레임별 대응표와 PTS 오차를 기준으로 비교한다. 임의10FPS 변환·보간·Frame 복제·배속은 하지 않았다.
- 60초 길이는 녹화 목표다. 첫/마지막 디코딩 Frame의 시각 차이와 컨테이너 duration은 마지막 Frame 길이 등에 따라 다를 수 있다.
- validation.json은 원본/검토본 전체 디코딩, Frame 수, 상대PTS, 간격 이상, SHA256을 포함한다. 큰 간격은 원천 Frame 손실의 확정 근거가 아니다.
- bbox/skeleton·엔진 판정 없음. 행동·인원·반복 포함 여부는 사용자가 판단한다.
- 운영 설정·Start/Stop·모델 추론·인증 갱신은 수행하지 않았다. 짧은 전후 snapshot은 운영 무영향 보장이 아니다.
- 이 폴더는 서버의 다운로드용 자료다. 사용자 Windows 다운로드 폴더에 실제 복사했다고 주장하지 않는다.
- 이전 camera1의401/0bytes 실패와21대상manifest는 별도 historical run에 보존했다. 이번 대상은 camera6~21뿐이다.
''', encoding='utf-8')
checks = {}
for path in sorted(dest.rglob('*')):
    if path.is_file():
        checks[str(path.relative_to(dest))] = {'bytes': path.stat().st_size, 'sha256': digest(path)}
(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())}))
