"""Camera enumeration — discovers available video input devices."""

from __future__ import annotations

import logging
from dataclasses import dataclass, field

from PySide6.QtMultimedia import QCameraDevice, QMediaDevices, QVideoFrameFormat

logger = logging.getLogger(__name__)

# Map Qt pixel format enum → human-readable string
_PIXEL_FORMAT_NAMES: dict[QVideoFrameFormat.PixelFormat, str] = {
    QVideoFrameFormat.PixelFormat.Format_BGRA8888:  "BGRA",
    QVideoFrameFormat.PixelFormat.Format_BGRX8888:  "BGRX",
    QVideoFrameFormat.PixelFormat.Format_ABGR8888:  "ABGR",
    QVideoFrameFormat.PixelFormat.Format_ARGB8888:  "ARGB",
    QVideoFrameFormat.PixelFormat.Format_RGBA8888:  "RGBA",
    QVideoFrameFormat.PixelFormat.Format_RGBX8888:  "RGBX",
    QVideoFrameFormat.PixelFormat.Format_YUV420P:   "YUV420P",
    QVideoFrameFormat.PixelFormat.Format_YUV422P:   "YUV422P",
    QVideoFrameFormat.PixelFormat.Format_NV12:      "NV12",
    QVideoFrameFormat.PixelFormat.Format_NV21:      "NV21",
    QVideoFrameFormat.PixelFormat.Format_UYVY:      "UYVY",
    QVideoFrameFormat.PixelFormat.Format_YUYV:      "YUY2",
    QVideoFrameFormat.PixelFormat.Format_Jpeg:      "MJPG",
    QVideoFrameFormat.PixelFormat.Format_Y8:        "GRAY8",
    QVideoFrameFormat.PixelFormat.Format_Y16:       "GRAY16",
}


def pixel_format_name(fmt: QVideoFrameFormat.PixelFormat) -> str:
    """Return a short human-readable name for a Qt pixel format."""
    return _PIXEL_FORMAT_NAMES.get(fmt, f"fmt_{fmt.value if hasattr(fmt, 'value') else fmt}")


@dataclass
class CameraFormat:
    """One supported video format entry for a camera device."""

    width: int
    height: int
    max_fps: float
    pixel_format: str       # e.g. "MJPG", "YUY2", "NV12", "BGRA"

    def __str__(self) -> str:
        return f"{self.pixel_format:6s} {self.width}x{self.height} @ {self.max_fps:.1f} fps"


@dataclass
class CameraInfo:
    """Lightweight descriptor of a detected camera."""

    device: QCameraDevice
    name: str
    id: str
    formats: list[CameraFormat] = field(default_factory=list)

    def __str__(self) -> str:
        return f"{self.name} [{self.id}]"


class CameraEnumerator:
    """Discovers available video input devices via QMediaDevices."""

    @staticmethod
    def list_cameras() -> list[CameraInfo]:
        """Return all available camera devices with their supported formats."""
        devices = QMediaDevices.videoInputs()
        cameras: list[CameraInfo] = []

        for device in devices:
            formats: list[CameraFormat] = []
            for qfmt in device.videoFormats():
                res = qfmt.resolution()
                formats.append(CameraFormat(
                    width=res.width(),
                    height=res.height(),
                    max_fps=qfmt.maxFrameRate(),
                    pixel_format=pixel_format_name(qfmt.pixelFormat()),
                ))

            # deduplicate; sort: largest area first, then fps descending
            seen: set[tuple[int, int, float, str]] = set()
            unique: list[CameraFormat] = []
            for f in sorted(
                formats,
                key=lambda x: (x.width * x.height, x.max_fps),
                reverse=True,
            ):
                key = (f.width, f.height, f.max_fps, f.pixel_format)
                if key not in seen:
                    seen.add(key)
                    unique.append(f)

            cam_id = CameraEnumerator._device_id(device)
            cameras.append(CameraInfo(
                device=device,
                name=device.description(),
                id=cam_id,
                formats=unique,
            ))

        logger.info("Cameras found: %d", len(cameras))
        for idx, cam in enumerate(cameras):
            logger.info("  [%d] %s (id: %s)", idx, cam.name, cam.id)
            for fmt in cam.formats:
                logger.info("        %s", fmt)

        return cameras

    @staticmethod
    def default_camera() -> CameraInfo | None:
        """Return the system default camera, or None if no camera is available."""
        device = QMediaDevices.defaultVideoInput()
        if device.isNull():
            return None

        default_id = CameraEnumerator._device_id(device)
        for cam in CameraEnumerator.list_cameras():
            if cam.id == default_id:
                return cam

        cameras = CameraEnumerator.list_cameras()
        return cameras[0] if cameras else None

    @staticmethod
    def _device_id(device: QCameraDevice) -> str:
        raw = device.id()
        if hasattr(raw, "toStdString"):
            return raw.toStdString()
        return raw.data().decode("utf-8", errors="replace")
