"""Independent metadata/state checks and remote read-only transfer audit."""
import csv
import hashlib
import json
from collections import Counter
import control as c

old=c.LOCAL.parent/'2026-10-26_contextra-camera24-eval_r01'
rows=[r for r in json.loads((old/'reports/pose_cache_copy.json').read_text()) if 1<=r['source_frame']<=723]
maps=list(csv.DictReader((c.LOCAL/'outputs/frame_map.csv').open()))
states=json.loads((c.LOCAL/'outputs/observation_states.json').read_text())
assert len(rows)==len(maps)==len(states)==301
seen={}; previous=set(); q3=[]; reappearances=0
for i,(r,m,s) in enumerate(zip(rows,maps,states)):
    assert int(m['output_frame_1based'])==i+1 and int(m['sample'])==r['sample']==s['sample']
    assert int(m['source_frame_1based'])==r['source_frame']==s['source_frame']
    assert float(m['source_relative_pts_seconds'])==float(m['output_pts_seconds'])==r['seconds']
    candidates={(p['track_id'],p['query']):p for p in r['persons']}
    assert len(s['persons'])==len(candidates)
    current=set()
    for p in s['persons']:
        original=candidates[p['track_id'],p['query']]
        assert p['score']==original['score'] and p['valid_joints']==sum(k[2]>=.3 for k in original['keypoints'])
        tid=p['track_id']
        if tid is None:
            assert p['gap_observations'] is None and p['state']=='미추적 · q는 이 Frame의 출력 칸 번호'
            if p['query']==3 and 285<=r['source_frame']<=304: q3.append(r['source_frame'])
        else:
            expected='이 검토 범위에서 처음 관측' if tid not in seen else ('직전 관측에도 같은 ID 있음' if tid in previous else f'같은 ID 재등장 · 중간 {i-seen[tid]-1}개 관측에서 없음')
            assert p['state']==expected
            if tid in seen and tid not in previous: reappearances+=1
            current.add(tid); seen[tid]=i
    previous=current
assert len(q3)==9
for name in ['stgcnpp_v2','ava_slowonly_v2','posec3d','yowov2_v2']:
    prior=list(csv.DictReader((old/'outputs'/f'{name}_frame_map.csv').open()))
    assert len(prior)==201
    for p in prior:
        r=rows[int(p['sample'])]
        assert int(p['source_frame'])==r['source_frame'] and float(p['source_seconds'])==r['seconds']
a=json.loads((c.LOCAL/'reports/production_before.json').read_text())
b=json.loads((c.LOCAL/'reports/production_after.json').read_text())
for k in ['config_sha256','pose_engine_sha256','pose_config_sha256','camera24']: assert a[k]==b[k]
for key,stream in a['streams'].items():
    for field in ['state','run_id','run_generation','child_pid','last_error','cleanup_pending']: assert stream[field]==b['streams'][key][field]
assert b['health']['ready']
assert not c.checked(['docker','ps','-a','--filter','name=^/'+c.NAME+'$','--format','{{.Names}}']).strip()
files=sorted(p.relative_to(c.LOCAL).as_posix() for folder in ['outputs'] for p in (c.LOCAL/folder).glob('*') if p.is_file())
remote=c.checked(['sha256sum']+[c.REMOTE+'/'+p for p in files]).decode().splitlines()
digests={}
for rel,line in zip(files,remote):
    digest=hashlib.sha256((c.LOCAL/rel).read_bytes()).hexdigest()
    assert digest==line.split()[0]
    digests[rel]=digest
report={'status':'PASS','one_to_one_observations':301,'untracked_q3_consecutive_frames':q3,
        'prior_four_model_maps_match':True,
        'same_id_reappearance_count':reappearances,'max_person_sample':max(rows,key=lambda r:len(r['persons']))['sample'],
        'state_counts':dict(Counter(s['state'] for s in b['streams'].values())),
        'config_pose_run_child_unchanged':True,'trial_container_absent':True,'transfer_sha256':digests}
(c.LOCAL/'reports/independent_validation.json').write_text(json.dumps(report,ensure_ascii=False,indent=2))
print(json.dumps(report,ensure_ascii=False,indent=2))
