#!/usr/bin/env python3
import argparse
import json
import os
import subprocess
import sys

def format_size(size_bytes):
    """Formats file size in bytes to a human-readable string."""
    try:
        size_bytes = int(size_bytes)
    except (ValueError, TypeError):
        return "Unknown size"

    for unit in ['B', 'KB', 'MB', 'GB']:
        if size_bytes < 1024.0:
            return f"{size_bytes:.2f} {unit}"
        size_bytes /= 1024.0
    return f"{size_bytes:.2f} TB"

def parse_fps(fps_str):
    """Parses fractional frame rates like '30/1' or '24000/1001' into floats."""
    if not fps_str:
        return "Unknown"
    if "/" in fps_str:
        try:
            num, den = map(float, fps_str.split("/"))
            if den != 0:
                val = num / den
                if val.is_integer():
                    return f"{int(val)} fps"
                return f"{val:.2f} fps"
        except (ValueError, ZeroDivisionError):
            pass
    try:
        val = float(fps_str)
        if val.is_integer():
            return f"{int(val)} fps"
        return f"{val:.2f} fps"
    except ValueError:
        return fps_str

def inspect_video(file_path, raw=False):
    """Runs ffprobe on the video file and returns parsed metadata dictionary."""
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"File not found: {file_path}")

    # Check if ffprobe is available
    try:
        subprocess.run(["ffprobe", "-version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
    except (subprocess.SubprocessError, FileNotFoundError):
        raise RuntimeError("ffprobe is not installed or not found in system PATH.")

    cmd = [
        "ffprobe",
        "-v", "error",
        "-show_format",
        "-show_streams",
        "-of", "json",
        file_path
    ]

    result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
    data = json.loads(result.stdout)

    if raw:
        return data

    # Extract format level details
    fmt = data.get("format", {})
    duration = fmt.get("duration")
    size_bytes = fmt.get("size")
    bitrate = fmt.get("bit_rate")

    # Format files size
    size_str = format_size(size_bytes) if size_bytes else "Unknown"

    # Parse duration
    try:
        duration_val = float(duration) if duration else 0.0
        duration_str = f"{duration_val:.2f}s"
    except ValueError:
        duration_str = "Unknown"
        duration_val = None

    # Parse bitrate
    try:
        bitrate_kbps = f"{int(float(bitrate) / 1000)} kbps" if bitrate else "Unknown"
    except ValueError:
        bitrate_kbps = "Unknown"

    video_streams = [s for s in data.get("streams", []) if s.get("codec_type") == "video"]
    audio_streams = [s for s in data.get("streams", []) if s.get("codec_type") == "audio"]

    has_video = len(video_streams) > 0
    has_audio = len(audio_streams) > 0

    video_info = {}
    if has_video:
        v = video_streams[0]
        width = v.get("width")
        height = v.get("height")
        codec = v.get("codec_name", "Unknown").upper()
        r_fps = parse_fps(v.get("r_frame_rate"))
        avg_fps = parse_fps(v.get("avg_frame_rate"))

        # Prefer r_frame_rate but fallback to avg
        fps = r_fps if r_fps != "0 fps" and r_fps != "Unknown" else avg_fps

        video_info = {
            "resolution": f"{width}x{height}" if width and height else "Unknown",
            "width": width,
            "height": height,
            "fps": fps,
            "codec": codec,
            "duration": v.get("duration")
        }

    audio_info = {}
    if has_audio:
        a = audio_streams[0]
        codec = a.get("codec_name", "Unknown").upper()
        channels = a.get("channels", "Unknown")
        sample_rate = a.get("sample_rate")
        sample_rate_khz = f"{float(sample_rate)/1000:.1f} kHz" if sample_rate else "Unknown"

        audio_info = {
            "codec": codec,
            "channels": channels,
            "sample_rate": sample_rate_khz
        }

    return {
        "file_name": os.path.basename(file_path),
        "file_size": size_str,
        "size_bytes": size_bytes,
        "duration": duration_str,
        "duration_seconds": duration_val,
        "bitrate": bitrate_kbps,
        "has_video": has_video,
        "video": video_info,
        "has_audio": has_audio,
        "audio": audio_info
    }

def print_terminal_report(info):
    """Prints an aligned terminal report."""
    print(f"\nVideo Inspection Report: {info['file_name']}")
    print("=" * 50)
    print(f"File Size   : {info['file_size']}")
    print(f"Duration    : {info['duration']}")
    print(f"Bitrate     : {info['bitrate']}")

    print("\nVideo Stream Details:")
    if info["has_video"]:
        v = info["video"]
        print(f"  * Resolution : {v['resolution']}")
        print(f"  * Frame Rate : {v['fps']}")
        print(f"  * Codec      : {v['codec']}")
    else:
        print("  * No Video Stream Found.")

    print("\nAudio Stream Details:")
    if info["has_audio"]:
        a = info["audio"]
        print("  * Status     : Audio Present")
        print(f"  * Codec      : {a['codec']}")
        print(f"  * Channels   : {a['channels']}")
        print(f"  * Sample Rate: {a['sample_rate']}")
    else:
        print("  * Status     : No Audio Stream Present")
    print()

def main():
    parser = argparse.ArgumentParser(description="Inspect video details (duration, frame rate, resolution, audio presence) using ffprobe.")
    parser.add_argument("file", help="Path to the video file to inspect")
    parser.add_argument("--json", action="store_true", help="Output parsed summary in JSON format")
    parser.add_argument("--raw", action="store_true", help="Output raw unmodified ffprobe JSON data")

    args = parser.parse_args()

    try:
        if args.raw:
            info = inspect_video(args.file, raw=True)
            print(json.dumps(info, indent=2))
        else:
            info = inspect_video(args.file, raw=False)
            if args.json:
                print(json.dumps(info, indent=2))
            else:
                print_terminal_report(info)
    except Exception as e:
        print(f"Error inspecting video: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()
