"""macOS UVC controller — backed by pyuvc (libuvc bindings)."""

from __future__ import annotations

import logging

from app.camera.uvc.base import UvcControllerBase, UvcParam, UvcParamInfo
from app.camera.uvc.stub import NullUvcController

logger = logging.getLogger(__name__)

# pyuvc provides access to UVC controls via libuvc.
# Install: pip install pyuvc  (requires libusb + libjpeg-turbo via brew)
#
# If pyuvc is not installed, fall back silently to NullUvcController.
try:
    import uvc  # type: ignore[import-untyped]
    _PYUVC_AVAILABLE = True
except ImportError:
    _PYUVC_AVAILABLE = False
    logger.debug("pyuvc not available — macOS UVC controls disabled")


# Map UvcParam → pyuvc control name string
_CONTROL_NAME: dict[UvcParam, str] = {
    UvcParam.BRIGHTNESS:             "Brightness",
    UvcParam.CONTRAST:               "Contrast",
    UvcParam.SATURATION:             "Saturation",
    UvcParam.HUE:                    "Hue",
    UvcParam.SHARPNESS:              "Sharpness",
    UvcParam.GAMMA:                  "Gamma",
    UvcParam.WHITE_BALANCE:          "White Balance temperature",
    UvcParam.BACKLIGHT_COMPENSATION: "Backlight Compensation",
    UvcParam.EXPOSURE:               "Absolute Exposure Time",
}


class MacUvcController(UvcControllerBase):
    """
    UVC camera controls on macOS via pyuvc / libuvc.

    pyuvc: https://github.com/pupil-labs/pyuvc
    Install: pip install pyuvc  (requires: brew install libusb jpeg-turbo)
    """

    def __init__(self, device_name: str) -> None:
        if not _PYUVC_AVAILABLE:
            raise ImportError("pyuvc not installed")
        self._device_name = device_name
        self._cap: object | None = None
        self._controls: dict[str, object] = {}

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    def open(self, device_name: str) -> bool:
        self.close()
        if not _PYUVC_AVAILABLE:
            return False
        try:
            device_list = uvc.device_list()
            target = None
            for d in device_list:
                if device_name.lower() in d.get("name", "").lower():
                    target = d
                    break
            if target is None and device_list:
                target = device_list[0]
                logger.warning(
                    "UVC: camera '%s' not found by name, using '%s'",
                    device_name, target.get("name"),
                )
            if target is None:
                logger.warning("UVC: no UVC devices found on macOS")
                return False

            self._cap = uvc.Capture(target["uid"])
            # Index controls by name for fast lookup
            self._controls = {c.display_name: c for c in self._cap.controls}
            logger.info(
                "UVC: opened '%s', controls: %s",
                target.get("name"), list(self._controls.keys()),
            )
            return True
        except Exception as exc:
            logger.warning("UVC macOS open failed: %s", exc)
            self._cap = None
            return False

    def close(self) -> None:
        if self._cap is not None:
            try:
                self._cap.close()
            except Exception:
                pass
            self._cap = None
            self._controls.clear()

    def is_open(self) -> bool:
        return self._cap is not None

    # ------------------------------------------------------------------
    # Query
    # ------------------------------------------------------------------

    def get_param_info(self, param: UvcParam) -> UvcParamInfo:
        ctrl_name = _CONTROL_NAME.get(param)
        if ctrl_name is None or ctrl_name not in self._controls:
            return UvcParamInfo(param=param, supported=False)
        try:
            ctrl = self._controls[ctrl_name]
            auto_supported = hasattr(ctrl, "auto_mode")
            auto_enabled = bool(ctrl.auto_mode) if auto_supported else False
            return UvcParamInfo(
                param=param,
                supported=True,
                minimum=int(ctrl.min_val),
                maximum=int(ctrl.max_val),
                default=int(ctrl.def_val),
                current=int(ctrl.value),
                step=int(getattr(ctrl, "step", 1)),
                auto_supported=auto_supported,
                auto_enabled=auto_enabled,
            )
        except Exception as exc:
            logger.debug("UVC get_param_info(%s): %s", param.name, exc)
            return UvcParamInfo(param=param, supported=False)

    def get_all_params(self) -> list[UvcParamInfo]:
        return [self.get_param_info(p) for p in UvcParam]

    # ------------------------------------------------------------------
    # Set
    # ------------------------------------------------------------------

    def set_value(self, param: UvcParam, value: int) -> bool:
        ctrl_name = _CONTROL_NAME.get(param)
        if ctrl_name is None or ctrl_name not in self._controls:
            return False
        try:
            self._controls[ctrl_name].value = value
            logger.debug("UVC set %s = %d", param.name, value)
            return True
        except Exception as exc:
            logger.warning("UVC set_value(%s, %d): %s", param.name, value, exc)
            return False

    def set_auto(self, param: UvcParam, enabled: bool) -> bool:
        ctrl_name = _CONTROL_NAME.get(param)
        if ctrl_name is None or ctrl_name not in self._controls:
            return False
        try:
            ctrl = self._controls[ctrl_name]
            if hasattr(ctrl, "auto_mode"):
                ctrl.auto_mode = enabled
                logger.debug("UVC auto %s = %s", param.name, enabled)
                return True
            return False
        except Exception as exc:
            logger.warning("UVC set_auto(%s, %s): %s", param.name, enabled, exc)
            return False


def make_mac_uvc_controller(device_name: str) -> UvcControllerBase:
    """Factory: returns MacUvcController or NullUvcController if pyuvc absent."""
    if not _PYUVC_AVAILABLE:
        return NullUvcController()
    ctrl = MacUvcController(device_name)
    if ctrl.open(device_name):
        return ctrl
    return NullUvcController()
