"""Tests for the UVC controller abstraction layer."""

from __future__ import annotations

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


class TestNullUvcController:
    """NullUvcController must implement the full interface, all as no-ops."""

    def setup_method(self) -> None:
        self.ctrl = NullUvcController()

    def test_is_not_open(self) -> None:
        assert not self.ctrl.is_open()

    def test_open_returns_false(self) -> None:
        assert self.ctrl.open("any device") is False

    def test_close_does_not_raise(self) -> None:
        self.ctrl.close()  # must not raise

    def test_get_param_info_returns_unsupported(self) -> None:
        info = self.ctrl.get_param_info(UvcParam.BRIGHTNESS)
        assert isinstance(info, UvcParamInfo)
        assert info.supported is False
        assert info.param is UvcParam.BRIGHTNESS

    def test_get_all_params_covers_all_uvc_params(self) -> None:
        infos = self.ctrl.get_all_params()
        returned_params = {i.param for i in infos}
        all_params = set(UvcParam)
        assert returned_params == all_params

    def test_get_all_params_all_unsupported(self) -> None:
        for info in self.ctrl.get_all_params():
            assert info.supported is False

    def test_set_value_returns_false(self) -> None:
        assert self.ctrl.set_value(UvcParam.CONTRAST, 50) is False

    def test_set_auto_returns_false(self) -> None:
        assert self.ctrl.set_auto(UvcParam.WHITE_BALANCE, True) is False

    def test_set_value_does_not_raise(self) -> None:
        for param in UvcParam:
            self.ctrl.set_value(param, 0)  # must not raise

    def test_set_auto_does_not_raise(self) -> None:
        for param in UvcParam:
            self.ctrl.set_auto(param, True)   # must not raise
            self.ctrl.set_auto(param, False)


class TestUvcParamInfo:
    """UvcParamInfo dataclass sanity checks."""

    def test_defaults(self) -> None:
        info = UvcParamInfo(param=UvcParam.BRIGHTNESS, supported=True)
        assert info.minimum == 0
        assert info.maximum == 100
        assert info.default == 50
        assert info.current == 50
        assert info.step == 1
        assert info.auto_supported is False
        assert info.auto_enabled is False

    def test_unsupported_flag(self) -> None:
        info = UvcParamInfo(param=UvcParam.GAMMA, supported=False)
        assert info.supported is False

    def test_custom_range(self) -> None:
        info = UvcParamInfo(
            param=UvcParam.HUE,
            supported=True,
            minimum=-180,
            maximum=180,
            default=0,
            current=-45,
        )
        assert info.minimum == -180
        assert info.maximum == 180
        assert info.current == -45


class TestMakeUvcControllerFallback:
    """make_uvc_controller falls back to NullUvcController when no lib available."""

    def test_returns_controller_instance(self) -> None:
        from app.camera.uvc import make_uvc_controller
        from app.camera.uvc.base import UvcControllerBase
        ctrl = make_uvc_controller("Test Camera")
        assert isinstance(ctrl, UvcControllerBase)

    def test_stub_used_when_native_lib_absent(self, monkeypatch) -> None:
        """If the native import fails, should return NullUvcController."""
        import builtins
        real_import = builtins.__import__

        def patched_import(name, *args, **kwargs):
            if name in ("duvc_ctl", "uvc"):
                raise ImportError(f"Mocked missing: {name}")
            return real_import(name, *args, **kwargs)

        monkeypatch.setattr(builtins, "__import__", patched_import)
        # Re-import to exercise factory with patched import
        import importlib

        import app.camera.uvc as uvc_pkg
        importlib.reload(uvc_pkg)
        ctrl = uvc_pkg.make_uvc_controller("Test Camera")
        # Should be functional (not raise), may be Null or platform controller
        from app.camera.uvc.base import UvcControllerBase
        assert isinstance(ctrl, UvcControllerBase)
        # Reload back to normal
        importlib.reload(uvc_pkg)
