from __future__ import annotations

import types

import pytest

from pymulator.core import can as can_module


def test_should_apply_macos_gs_usb_shim_defaults_on(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(can_module.sys, "platform", "darwin")
    monkeypatch.delenv(can_module.GS_USB_MACOS_SHIM_ENV, raising=False)

    assert can_module._should_apply_macos_gs_usb_shim() is True


def test_should_apply_macos_gs_usb_shim_respects_env_kill_switch(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(can_module.sys, "platform", "darwin")
    monkeypatch.setenv(can_module.GS_USB_MACOS_SHIM_ENV, "0")

    assert can_module._should_apply_macos_gs_usb_shim() is False


def test_install_macos_gs_usb_start_shim_patches_start(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(can_module.sys, "platform", "darwin")
    monkeypatch.delenv(can_module.GS_USB_MACOS_SHIM_ENV, raising=False)

    class _DeviceMode:
        def __init__(self, mode: int, flags: int) -> None:
            self.mode = mode
            self.flags = flags

        def pack(self) -> bytes:
            return b"PACK"

    class _FakeUsbHandle:
        def __init__(self) -> None:
            self.calls: list[tuple[int, int, int, int, bytes]] = []

        def ctrl_transfer(self, request_type: int, req: int, value: int, index: int, payload: bytes) -> None:
            self.calls.append((request_type, req, value, index, payload))

    class _FakeGsUsb:
        def __init__(self) -> None:
            self.device_capability = types.SimpleNamespace(feature=0xFFFF)
            self.device_flags: int | None = None
            self.gs_usb = _FakeUsbHandle()

        def start(self, flags: int = 0) -> None:
            self.device_flags = flags

    fake_module = types.SimpleNamespace(
        GsUsb=_FakeGsUsb,
        DeviceMode=_DeviceMode,
        _GS_USB_BREQ_MODE=2,
        GS_CAN_MODE_START=1,
        GS_CAN_MODE_NORMAL=1 << 0,
        GS_CAN_MODE_HW_TIMESTAMP=1 << 1,
        GS_CAN_MODE_LISTEN_ONLY=1 << 2,
        GS_CAN_MODE_LOOP_BACK=1 << 3,
        GS_CAN_MODE_ONE_SHOT=1 << 4,
    )
    monkeypatch.setattr(can_module.importlib, "import_module", lambda _name: fake_module)

    assert can_module._install_macos_gs_usb_start_shim() is True
    instance = fake_module.GsUsb()
    instance.start(fake_module.GS_CAN_MODE_NORMAL | fake_module.GS_CAN_MODE_HW_TIMESTAMP)

    assert instance.device_flags is not None
    assert instance.gs_usb.calls == [(0x41, fake_module._GS_USB_BREQ_MODE, 0, 0, b"PACK")]
    assert getattr(fake_module.GsUsb, "_pymulator_start_patched") is True

    # Idempotent: second install should no-op and still report success.
    assert can_module._install_macos_gs_usb_start_shim() is True


def test_open_calls_macos_gs_usb_shim_for_gs_usb(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(can_module.sys, "platform", "darwin")
    monkeypatch.setattr(can_module.importlib.util, "find_spec", lambda _name: object())

    class _FakeCan:
        class Message:  # pragma: no cover - structural stub
            def __init__(self, **kwargs):
                self.kwargs = kwargs

        @staticmethod
        def Bus(**kwargs):
            return types.SimpleNamespace(**kwargs)

    monkeypatch.setattr(can_module, "python_can", _FakeCan)

    called = {"count": 0}

    def _fake_install() -> bool:
        called["count"] += 1
        return True

    monkeypatch.setattr(can_module, "_install_macos_gs_usb_start_shim", _fake_install)

    wires = can_module.SocketCANInterface("can0")
    wires.open(500000)

    assert wires.backend == "gs_usb"
    assert called["count"] == 1
