#!/usr/bin/env python3
"""Check an existing MP4 against an explicit delivery specification.

Uses Python's standard library and an ffprobe executable on PATH.
Does not render, modify or upload the input file.
"""
import argparse
import json
import math
import subprocess
import sys
from fractions import Fraction
from pathlib import Path


def positive(value):
    number = float(value)
    if not math.isfinite(number) or number <= 0:
        raise argparse.ArgumentTypeError("must be a finite positive number")
    return number


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("video", type=Path)
    parser.add_argument("--width", type=int, required=True)
    parser.add_argument("--height", type=int, required=True)
    parser.add_argument("--fps", type=positive, required=True)
    parser.add_argument("--duration", type=positive, required=True)
    parser.add_argument("--require-audio", action="store_true")
    args = parser.parse_args()
    errors = []
    try:
        if not args.video.is_file():
            raise ValueError("input file does not exist")
        if args.width <= 0 or args.height <= 0:
            raise ValueError("dimensions must be positive")
        result = subprocess.run(
            ["ffprobe", "-v", "error", "-count_frames", "-show_streams",
             "-show_format", "-of", "json", str(args.video.resolve())],
            check=True, capture_output=True, text=True, timeout=120,
        )
        data = json.loads(result.stdout)
        streams = data.get("streams", [])
        videos = [s for s in streams if s.get("codec_type") == "video"]
        if len(videos) != 1:
            raise ValueError("expected exactly one video stream")
        video = videos[0]
        fps = float(Fraction(video["avg_frame_rate"]))
        duration = float(video.get("duration") or data["format"]["duration"])
        frames = int(video["nb_read_frames"])
        audio = any(s.get("codec_type") == "audio" for s in streams)
        observed = {"width": video["width"], "height": video["height"],
                    "fps": fps, "duration": duration, "frames": frames,
                    "has_audio": audio}
        if (video["width"], video["height"]) != (args.width, args.height):
            errors.append("dimensions do not match")
        if abs(fps - args.fps) > 0.001:
            errors.append("frame rate does not match")
        if abs(duration - args.duration) > 1 / args.fps + 0.002:
            errors.append("duration differs by more than one frame")
        expected_frames = math.ceil(args.duration * args.fps - 0.000001)
        if frames != expected_frames:
            errors.append(f"expected {expected_frames} decoded frames, got {frames}")
        if args.require_audio and not audio:
            errors.append("required audio stream is missing")
        print(json.dumps({"pass": not errors, "observed": observed,
                          "errors": errors}, indent=2))
    except (OSError, ValueError, KeyError, ZeroDivisionError,
            subprocess.SubprocessError) as exc:
        print(json.dumps({"pass": False, "errors": [str(exc)]}, indent=2))
        return 1
    return 1 if errors else 0


if __name__ == "__main__":
    sys.exit(main())
