"""Utility functions for video-thumbnail-creator."""

import json
import os
import subprocess
import sys


def check_dependencies(ffmpeg: str = "ffmpeg", ffprobe: str = "ffprobe") -> None:
    """Check that required external tools are available in PATH."""
    for tool in (ffmpeg, ffprobe):
        try:
            subprocess.run(
                [tool, "-version"],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
                check=True,
            )
        except (FileNotFoundError, subprocess.CalledProcessError):
            print(
                f"Error: '{tool}' not found. Please install ffmpeg and ensure it is in your PATH.",
                file=sys.stderr,
            )
            sys.exit(1)


def get_video_duration(video_path: str, ffprobe: str = "ffprobe") -> float:
    """Return the duration of a video file in seconds using ffprobe."""
    result = subprocess.run(
        [
            ffprobe,
            "-v", "error",
            "-show_entries", "format=duration",
            "-of", "default=noprint_wrappers=1:nokey=1",
            video_path,
        ],
        capture_output=True,
        text=True,
        check=True,
    )
    duration_str = result.stdout.strip()
    if not duration_str:
        raise ValueError(f"Could not determine duration of '{video_path}'.")
    return float(duration_str)


def open_image(path: str) -> None:
    """Open an image file with the system default viewer."""
    import platform
    system = platform.system()
    if system == "Darwin":
        subprocess.run(["open", path], check=False)
    elif system == "Windows":
        os.startfile(path)  # type: ignore[attr-defined]
    else:
        subprocess.run(["xdg-open", path], check=False)


# ---------------------------------------------------------------------------
# HDR helpers
# ---------------------------------------------------------------------------

# Transfer characteristics that indicate HDR / PQ content.
_HDR_TRANSFERS = frozenset({"smpte2084", "arib-std-b67"})

SRGB_ICC_PROFILE = "/System/Library/ColorSync/Profiles/sRGB Profile.icc"


def get_video_properties(video_path: str, ffprobe: str = "ffprobe") -> dict:
    """Detect video stream properties: resolution, HDR status and frame rate.

    Returns a dict with keys:
    - "width": int
    - "height": int
    - "is_hdr": bool
    - "is_4k": bool (width >= 3840 or height >= 2160)
    - "is_hd": bool (width >= 1920 or height >= 1080, and not 4K)
    - "fps": float (average frame rate; 0.0 if unknown)
    - "is_hfr": bool (fps >= 48)
    """
    props = {
        "width": 0, "height": 0,
        "is_hdr": False, "is_4k": False, "is_hd": False,
        "fps": 0.0, "is_hfr": False,
    }

    try:
        result = subprocess.run(
            [
                ffprobe,
                "-v", "error",
                "-select_streams", "v:0",
                "-show_entries", "stream=width,height,color_transfer,avg_frame_rate",
                "-of", "json",
                video_path,
            ],
            capture_output=True,
            text=True,
        )
    except FileNotFoundError:
        return props

    if result.returncode != 0:
        return props

    try:
        data = json.loads(result.stdout)
    except json.JSONDecodeError:
        return props

    for stream in data.get("streams", []):
        w = stream.get("width", 0)
        h = stream.get("height", 0)
        transfer = stream.get("color_transfer", "").lower()

        avg_fr = stream.get("avg_frame_rate", "0/1")
        try:
            num, den = avg_fr.split("/")
            fps = float(num) / float(den) if float(den) != 0 else 0.0
        except (ValueError, ZeroDivisionError):
            fps = 0.0

        props["width"] = w
        props["height"] = h
        props["is_hdr"] = transfer in _HDR_TRANSFERS
        props["is_4k"] = (w >= 3840 or h >= 2160)
        props["is_hd"] = (w >= 1920 or h >= 1080) and not props["is_4k"]
        props["fps"] = fps
        props["is_hfr"] = fps >= 48
        break

    return props


def is_hdr_video(video_path: str, ffprobe: str = "ffprobe") -> bool:
    """Return *True* if the first video stream uses an HDR transfer function.

    Detects SMPTE ST 2084 (PQ / HDR10 / HDR10+) and ARIB STD-B67 (HLG).
    """
    result = subprocess.run(
        [
            ffprobe,
            "-v", "error",
            "-select_streams", "v:0",
            "-show_entries", "stream=color_transfer",
            "-of", "json",
            video_path,
        ],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        return False
    try:
        data = json.loads(result.stdout)
    except json.JSONDecodeError:
        return False
    for stream in data.get("streams", []):
        if stream.get("color_transfer", "").lower() in _HDR_TRANSFERS:
            return True
    return False


def tonemap_to_srgb_jpeg(png_path: str, jpeg_path: str) -> str:
    """Convert a 16-bit PNG (with HDR colour profile) to an sRGB JPEG using macOS *sips*.

    Steps performed:
    1. ``sips -s format jpeg`` – converts to JPEG (macOS CoreImage
       automatically tone-maps the HDR pixel data).
    2. ``sips -m <sRGB ICC>`` – assigns the sRGB IEC61966-2.1 profile.

    Returns the absolute path of *jpeg_path*.
    Raises ``RuntimeError`` if *sips* is not available (non-macOS).
    """
    try:
        # Step 1: PNG → JPEG (CoreImage auto-tonemaps)
        subprocess.run(
            ["sips", "-s", "format", "jpeg", png_path, "--out", jpeg_path],
            capture_output=True,
            check=True,
        )
        # Step 2: Assign sRGB profile
        subprocess.run(
            ["sips", "-m", SRGB_ICC_PROFILE, jpeg_path, "--out", jpeg_path],
            capture_output=True,
            check=True,
        )
    except FileNotFoundError as exc:
        raise RuntimeError(
            "sips is not available – HDR tone-mapping requires macOS."
        ) from exc
    except subprocess.CalledProcessError as exc:
        raise RuntimeError(
            f"sips tone-mapping failed: {exc.stderr.decode(errors='replace').strip()}"
        ) from exc
    return os.path.abspath(jpeg_path)
