"""Decode-check and number every frame without rate conversion."""
import csv
import hashlib
import json
import pathlib
import statistics
import subprocess
import time

root = pathlib.Path('/work')
original = root/'inputs/camera24_original.mp4'
review = root/'inputs/camera24_review.mp4'
assert not review.exists(), 'Refuse overwrite'

def probe(path):
    command = ['ffprobe','-v','error','-threads','2','-select_streams','v:0',
               '-show_streams','-show_frames','-show_entries',
               'stream=codec_name,width,height,r_frame_rate,avg_frame_rate,time_base,duration,nb_frames:frame=best_effort_timestamp_time,key_frame,pict_type',
               '-of','json',str(path)]
    r = subprocess.run(command,capture_output=True,timeout=600,check=True)
    data = json.loads(r.stdout)
    data['decode_stderr'] = r.stderr.decode(errors='replace')[-3000:]
    return data

before = probe(original)
(root/'reports/original_probe.json').write_text(json.dumps(before))
font = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
label = "Frame %{eif\\:n+1\\:d} | Time %{pts\\:hms}"
vf = "setpts=PTS-STARTPTS,drawtext=fontfile="+font+":text='"+label+"':fontsize=32:fontcolor=white:box=1:boxcolor=black@0.75:boxborderw=10:x=20:y=20"
started = time.monotonic()
r = subprocess.run(['ffmpeg','-nostdin','-hide_banner','-loglevel','error',
    '-threads','2','-i',str(original),'-map','0:v:0','-an','-vf',vf,
    '-c:v','libx264','-preset','veryfast','-crf','20','-threads','2',
    '-fps_mode','passthrough','-enc_time_base','-1','-movflags','+faststart',str(review)],
    capture_output=True,timeout=900)
if r.returncode:
    print(r.stderr.decode(errors='replace')[-2000:])
    raise SystemExit(r.returncode)
after = probe(review)
(root/'reports/review_probe.json').write_text(json.dumps(after))
a, b = before['frames'], after['frames']
assert len(a) == len(b), 'Frame count mismatch'
ta = [float(f['best_effort_timestamp_time']) for f in a]
tb = [float(f['best_effort_timestamp_time']) for f in b]
delta = max(abs((x-ta[0])-(y-tb[0])) for x,y in zip(ta,tb))
assert delta < 0.001, 'Frame timestamps mismatch'
with (root/'inputs/camera24_frame_map.csv').open('w',newline='') as f:
    w=csv.writer(f)
    w.writerow(['frame_1based','original_pts_seconds','elapsed_seconds','review_pts_seconds','key_frame','pict_type'])
    for i,(x,y) in enumerate(zip(a,b),1):
        w.writerow([i,x['best_effort_timestamp_time'],f'{ta[i-1]-ta[0]:.6f}',y['best_effort_timestamp_time'],x['key_frame'],x.get('pict_type')])
gaps=[y-x for x,y in zip(ta,ta[1:])]
median=statistics.median(gaps)
summary=dict(frame_count=len(a),first_pts=ta[0],last_pts=ta[-1],elapsed_seconds=ta[-1]-ta[0],
    max_mapping_error_seconds=delta,median_interval_seconds=median,max_interval_seconds=max(gaps),
    nonpositive_intervals=sum(x<=0 for x in gaps),intervals_above_1p5x_median=sum(x>median*1.5 for x in gaps),
    original_stream=before['streams'][0],review_stream=after['streams'][0],
    original_decode_errors=before['decode_stderr'],review_decode_errors=after['decode_stderr'],
    render_stderr=r.stderr.decode(errors='replace')[-1000:],render_wall_seconds=time.monotonic()-started,
    source_frame_loss='Cannot prove without an upstream reference; only decode and timestamp continuity checked.')
for p in (original,review,root/'inputs/camera24_frame_map.csv'):
    h=hashlib.sha256()
    with p.open('rb') as f:
        while chunk:=f.read(1024*1024): h.update(chunk)
    summary[p.name]={'bytes':p.stat().st_size,'sha256':h.hexdigest()}
(root/'reports/video_validation.json').write_text(json.dumps(summary,indent=2))
print(json.dumps(summary))
