"""Infrastructure adapter tests for psutil-backed process inventory."""

from typing import Any, Sequence

from aitop.infrastructure.process_inventory import PsutilProcessInventoryGateway


class _FakeNoSuchProcess(Exception):
    pass


class _FakeAccessDenied(Exception):
    pass


class _FakeZombieProcess(Exception):
    pass


class _FakeOneshot:
    def __enter__(self) -> "_FakeOneshot":
        return self

    def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> bool:
        del exc_type, exc, tb
        return False


class _FakeCpuTimes:
    user = 1.5
    system = 0.5


class _FakeMemInfo:
    rss = 1024
    vms = 4096


class _FakeIoCounters:
    read_bytes = 1234
    write_bytes = 5678


class _FakeProcess:
    def __init__(self, pid: int, name: str) -> None:
        self.pid = pid
        self.info = {"pid": pid, "name": name}

    def oneshot(self) -> _FakeOneshot:
        return _FakeOneshot()

    def name(self) -> str:
        return str(self.info["name"])

    def cmdline(self) -> list[str]:
        return ["python", "train.py", "--model", "demo"]

    def ppid(self) -> int:
        return 1

    def username(self) -> str:
        return "tester"

    def cpu_percent(self) -> float:
        return 12.5

    def memory_percent(self) -> float:
        return 3.2

    def status(self) -> str:
        return "running"

    def create_time(self) -> float:
        return 123.0

    def cpu_times(self) -> _FakeCpuTimes:
        return _FakeCpuTimes()

    def memory_info(self) -> _FakeMemInfo:
        return _FakeMemInfo()

    def num_threads(self) -> int:
        return 4

    def io_counters(self) -> _FakeIoCounters:
        return _FakeIoCounters()

    def net_connections(self, kind: str = "inet") -> list[int]:
        assert kind == "inet"
        return [1, 2]

    def cpu_affinity(self) -> list[int]:
        return [0, 1]

    def is_running(self) -> bool:
        return True


class _FakePsutilModule:
    NoSuchProcess = _FakeNoSuchProcess
    AccessDenied = _FakeAccessDenied
    ZombieProcess = _FakeZombieProcess

    def __init__(self, processes: Sequence[_FakeProcess]) -> None:
        self._processes = list(processes)

    def process_iter(self, attrs: Sequence[str]) -> list[_FakeProcess]:
        assert set(attrs) <= {"pid", "name"}
        return self._processes

    def Process(self, pid: int) -> _FakeProcess:
        for process in self._processes:
            if process.pid == pid:
                return process
        raise _FakeNoSuchProcess(pid)

    def pids(self) -> list[int]:
        return [proc.pid for proc in self._processes]


def test_gateway_exposes_iter_and_pid_operations() -> None:
    fake_psutil = _FakePsutilModule(
        [_FakeProcess(11, "alpha"), _FakeProcess(12, "beta")]
    )
    gateway = PsutilProcessInventoryGateway(psutil_module=fake_psutil)

    assert [proc.pid for proc in gateway.iter_processes(["pid", "name"])] == [11, 12]
    assert gateway.list_pids() == [11, 12]
    assert gateway.snapshot_current_pids() == {11, 12}


def test_gateway_delegates_process_accessors() -> None:
    process = _FakeProcess(42, "trainer")
    gateway = PsutilProcessInventoryGateway(psutil_module=_FakePsutilModule([process]))

    assert gateway.process_info_pid(process) == 42
    assert gateway.process_info_name(process) == "trainer"
    assert gateway.process_pid(process) == 42
    assert gateway.process_name(process) == "trainer"
    assert gateway.process_cmdline(process)[:2] == ["python", "train.py"]
    assert gateway.process_ppid(process) == 1
    assert gateway.process_username(process) == "tester"
    assert gateway.process_cpu_percent(process) == 12.5
    assert gateway.process_memory_percent(process) == 3.2
    assert gateway.process_status(process) == "running"
    assert gateway.process_create_time(process) == 123.0
    assert gateway.process_cpu_times(process).user == 1.5
    assert gateway.process_memory_info(process).rss == 1024
    assert gateway.process_num_threads(process) == 4
    assert gateway.process_io_counters(process).read_bytes == 1234
    assert gateway.process_net_connection_count(process) == 2
    assert gateway.process_cpu_affinity(process) == [0, 1]
    assert gateway.process_is_running(process) is True
    with gateway.process_oneshot(process):
        assert True
    errors = gateway.process_errors()
    assert _FakeNoSuchProcess in errors
    assert _FakeAccessDenied in errors
    assert _FakeZombieProcess in errors
