import subprocess
from typing import Any

import aitop.core.gpu.factory as factory_module
from aitop.core.gpu.factory import GPUMonitorFactory


def _non_detecting_result(cmd: list[str]) -> subprocess.CompletedProcess[str]:
    return subprocess.CompletedProcess(cmd, returncode=1, stdout="", stderr="")


def test_detect_vendors_detects_intel_via_intel_gpu_top_list(monkeypatch: Any) -> None:
    def fake_run(
        cmd: list[str], *args: Any, **kwargs: Any
    ) -> subprocess.CompletedProcess[str]:
        del args, kwargs
        executable = cmd[0]
        if executable == "/usr/bin/intel_gpu_top":
            return subprocess.CompletedProcess(
                cmd,
                returncode=0,
                stdout="card0: Intel Arc Graphics\n",
                stderr="",
            )
        return _non_detecting_result(cmd)

    monkeypatch.setattr(factory_module.subprocess, "run", fake_run)
    monkeypatch.setattr(
        factory_module.shutil,
        "which",
        lambda name: "/usr/bin/intel_gpu_top",
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_drm_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_npu_accel_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_npu_accel_device",
        staticmethod(lambda: False),
    )

    vendors = GPUMonitorFactory.detect_vendors()

    assert vendors == ["intel"]


def test_detect_vendors_falls_back_to_drm_vendor_id_for_intel(
    monkeypatch: Any,
) -> None:
    def fake_run(
        cmd: list[str], *args: Any, **kwargs: Any
    ) -> subprocess.CompletedProcess[str]:
        del args, kwargs
        return _non_detecting_result(cmd)

    monkeypatch.setattr(factory_module.subprocess, "run", fake_run)
    monkeypatch.setattr(factory_module.shutil, "which", lambda name: None)
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_drm_device",
        staticmethod(lambda: True),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_npu_accel_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_npu_accel_device",
        staticmethod(lambda: False),
    )

    vendors = GPUMonitorFactory.detect_vendors()

    assert vendors == ["none"]


def test_detect_vendors_uses_drm_fallback_when_intel_gpu_top_exists(
    monkeypatch: Any,
) -> None:
    def fake_run(
        cmd: list[str], *args: Any, **kwargs: Any
    ) -> subprocess.CompletedProcess[str]:
        del args, kwargs
        # Simulate intel_gpu_top -L not returning clear card lines;
        # DRM fallback should still activate when tool exists.
        if cmd[0] == "/usr/bin/intel_gpu_top":
            return subprocess.CompletedProcess(cmd, returncode=0, stdout="", stderr="")
        return _non_detecting_result(cmd)

    monkeypatch.setattr(factory_module.subprocess, "run", fake_run)
    monkeypatch.setattr(
        factory_module.shutil,
        "which",
        lambda name: "/usr/bin/intel_gpu_top" if name == "intel_gpu_top" else None,
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_drm_device",
        staticmethod(lambda: True),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_npu_accel_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_npu_accel_device",
        staticmethod(lambda: False),
    )

    vendors = GPUMonitorFactory.detect_vendors()

    assert vendors == ["intel"]


def test_detect_vendors_detects_amd_npu_via_xrt_smi(monkeypatch: Any) -> None:
    def fake_run(
        cmd: list[str], *args: Any, **kwargs: Any
    ) -> subprocess.CompletedProcess[str]:
        del args, kwargs
        if cmd[0] == "/usr/bin/xrt-smi":
            return subprocess.CompletedProcess(
                cmd,
                returncode=0,
                stdout="Device(s) Present\n| [0000:c5:00.1] | NPU Strix |\n",
                stderr="",
            )
        return _non_detecting_result(cmd)

    monkeypatch.setattr(factory_module.subprocess, "run", fake_run)
    monkeypatch.setattr(
        factory_module.shutil,
        "which",
        lambda name: "/usr/bin/xrt-smi" if name == "xrt-smi" else None,
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_drm_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_npu_accel_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_npu_accel_device",
        staticmethod(lambda: False),
    )

    vendors = GPUMonitorFactory.detect_vendors()

    assert vendors == ["amd_npu"]


def test_detect_vendors_detects_intel_npu_via_accel_sysfs(monkeypatch: Any) -> None:
    monkeypatch.setattr(
        factory_module.subprocess,
        "run",
        lambda *a, **k: _non_detecting_result(["noop"]),
    )
    monkeypatch.setattr(factory_module.shutil, "which", lambda name: None)
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_drm_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_npu_accel_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_npu_accel_device",
        staticmethod(lambda: True),
    )

    vendors = GPUMonitorFactory.detect_vendors()

    assert vendors == ["intel_npu"]


def test_detect_dependency_warnings_reports_missing_intel_gpu_tools(
    monkeypatch: Any,
) -> None:
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_nvidia_gpu_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_drm_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_intel_drm_device",
        staticmethod(lambda: True),
    )
    monkeypatch.setattr(
        GPUMonitorFactory,
        "_has_amd_npu_accel_device",
        staticmethod(lambda: False),
    )
    monkeypatch.setattr(factory_module.shutil, "which", lambda _name: None)

    warnings = GPUMonitorFactory.detect_dependency_warnings()

    assert any("intel_gpu_top" in warning for warning in warnings)
