"""Inspect exactly one local render file with a trusted ffprobe. Never pick 'newest'.

Metadata matching is NOT job provenance, listening, loudness, color, full decoding
or creative acceptance. ffprobe is a separate installed dependency, not bundled.
"""
from __future__ import annotations
import argparse
from fractions import Fraction
import json
from pathlib import Path
import subprocess
from resolve_api_delivery import PreflightError, file_stamp, integer, print_result


def verify_file(path, ffprobe, *, expected_video=None, expected_audio=None,
                sample_rate=None, channels=None, bit_depth=None, video_frames=None,
                timeout=60):
    p, exe = Path(path).expanduser(), Path(ffprobe).expanduser()
    if not p.is_absolute() or p.is_symlink() or not exe.is_absolute() or not exe.is_file():
        raise PreflightError('Use an absolute regular media file and an absolute trusted ffprobe executable')
    before = file_stamp(p)
    if before[2] <= 0: raise PreflightError('Empty media file')
    if not 1 <= integer(timeout,'timeout') <= 3600: raise PreflightError('Timeout must be 1..3600 seconds')
    for v,n in ((expected_video,'expected video streams'),(expected_audio,'expected audio streams')):
        if v is not None and not 0 <= integer(v,n) <= 64: raise PreflightError('Stream count must be 0..64')
    for v,n in ((sample_rate,'sample rate'),(channels,'channels'),(bit_depth,'bit depth'),(video_frames,'video frames')):
        if v is not None and integer(v,n) <= 0: raise PreflightError(n+' must be positive')
    command = [str(exe), '-v', 'error', '-protocol_whitelist', 'file',
               '-show_entries', 'stream=index,codec_type,codec_name,sample_rate,channels,channel_layout,bits_per_sample,bits_per_raw_sample,nb_read_frames,duration,time_base:format=format_name,duration,size',
               '-of', 'json']
    if video_frames is not None: command.append('-count_frames')
    command += ['-i', str(p)]
    try:
        result = subprocess.run(command, capture_output=True, timeout=timeout, check=False, shell=False)
    except subprocess.TimeoutExpired:
        return {'status':'probe_timeout','metadata_matches':False,'content_verified':False}
    if result.returncode != 0 or result.stderr.strip():
        return {'status':'probe_failed','returncode':result.returncode,'metadata_matches':False,
                'stderr':result.stderr[:2000].decode('utf-8','replace'),'content_verified':False}
    if len(result.stdout) > 2*1024*1024: raise PreflightError('Probe report exceeds 2 MiB; not a native memory limit')
    report = json.loads(result.stdout)
    streams = report.get('streams')
    if not isinstance(streams,list) or not streams or len(streams)>128 or not all(isinstance(x,dict) for x in streams):
        raise PreflightError('Missing or invalid stream report')
    if file_stamp(p) != before: raise PreflightError('Output file changed while probing')
    video=[s for s in streams if s.get('codec_type')=='video']
    audio=[s for s in streams if s.get('codec_type')=='audio']
    mismatches=[]; checks=[]
    def compare(actual, expected, label):
        if expected is None: return
        checks.append(label)
        if actual != expected: mismatches.append({'field':label,'expected':expected,'actual':actual})
    def numeric(raw):
        if not isinstance(raw,(int,str)) or isinstance(raw,bool): return None
        try: return int(raw)
        except ValueError: return None
    compare(len(video),expected_video,'video_streams'); compare(len(audio),expected_audio,'audio_streams')
    if any(x is not None for x in (sample_rate, channels, bit_depth)):
        if not audio: mismatches.append({'field':'audio','expected':'at least one audio stream','actual':0})
        for s in audio:
            compare(numeric(s.get('sample_rate')),sample_rate,'sample_rate:'+str(s.get('index')))
            compare(numeric(s.get('channels')),channels,'channels:'+str(s.get('index')))
            raw_bits=numeric(s.get('bits_per_raw_sample')) or numeric(s.get('bits_per_sample'))
            compare(raw_bits,bit_depth,'bit_depth:'+str(s.get('index')))
    if video_frames is not None:
        if len(video)!=1: mismatches.append({'field':'counted_video_streams','expected':1,'actual':len(video)})
        else: compare(numeric(video[0].get('nb_read_frames')),video_frames,'video_frame_count')
    complete=expected_video is not None and expected_audio is not None
    return {'status':'metadata_mismatch' if mismatches else 'metadata_matches' if complete else 'observed',
            'path':str(p),'stamp':before,'probe':report,'checked_fields':checks,'mismatches':mismatches,
            'metadata_matches':complete and not mismatches,'job_provenance_verified':False,
            'content_verified':False,'audio_listened':False,'full_decode_performed':False}


def add_arguments(p):
    p.add_argument('--file',required=True); p.add_argument('--ffprobe',required=True)
    p.add_argument('--expected-video',type=int); p.add_argument('--expected-audio',type=int)
    p.add_argument('--sample-rate',type=int); p.add_argument('--channels',type=int)
    p.add_argument('--bit-depth',type=int); p.add_argument('--video-frames',type=int)
    p.add_argument('--timeout',type=int,default=60)


def from_args(a):
    return verify_file(a.file,a.ffprobe,expected_video=a.expected_video,expected_audio=a.expected_audio,
                       sample_rate=a.sample_rate,channels=a.channels,bit_depth=a.bit_depth,
                       video_frames=a.video_frames,timeout=a.timeout)


def main():
    p=argparse.ArgumentParser(description=__doc__); add_arguments(p); a=p.parse_args()
    return print_result(lambda:from_args(a))


if __name__ == "__main__":
    raise SystemExit(main())
