#!/usr/bin/env python3
"""Psutil-backed process inventory adapter."""

from typing import Any, ContextManager, Iterable, Sequence, cast

import psutil

from ..application.process_inventory import ProcessInventoryGateway


class PsutilProcessInventoryGateway(ProcessInventoryGateway):
    """Use psutil to provide process inventory operations."""

    def __init__(self, psutil_module: Any = psutil) -> None:
        self._psutil = psutil_module

    def iter_processes(self, attrs: Sequence[str]) -> Iterable[Any]:
        """Iterate process handles while prefetching selected attributes."""
        return cast(Iterable[Any], self._psutil.process_iter(list(attrs)))

    def get_process(self, pid: int) -> Any:
        """Resolve a process handle by PID."""
        return self._psutil.Process(pid)

    def list_pids(self) -> list[int]:
        """Return current process IDs."""
        return list(self._psutil.pids())

    def snapshot_current_pids(self) -> set[int]:
        """Return current process IDs as a set for pruning."""
        return {proc.pid for proc in self._psutil.process_iter(["pid"])}

    def process_errors(self) -> tuple[type[BaseException], ...]:
        """Return recoverable process-access exception types."""
        return (
            self._psutil.NoSuchProcess,
            self._psutil.AccessDenied,
            self._psutil.ZombieProcess,
        )

    def process_info_pid(self, proc: Any) -> int:
        """Return PID from prefetched process metadata."""
        info = getattr(proc, "info", None)
        if isinstance(info, dict) and "pid" in info:
            return int(info["pid"])
        return int(proc.pid)

    def process_info_name(self, proc: Any) -> str:
        """Return process name from prefetched metadata."""
        info = getattr(proc, "info", None)
        if isinstance(info, dict):
            return str(info.get("name", "") or "")
        return str(proc.name())

    def process_pid(self, proc: Any) -> int:
        """Return PID from a resolved process handle."""
        return int(proc.pid)

    def process_name(self, proc: Any) -> str:
        """Return process name from a process handle."""
        return str(proc.name())

    def process_cmdline(self, proc: Any) -> list[str]:
        """Return process command line arguments."""
        return list(proc.cmdline())

    def process_ppid(self, proc: Any) -> int:
        """Return process parent PID."""
        return int(proc.ppid())

    def process_username(self, proc: Any) -> str:
        """Return process owner name."""
        return str(proc.username())

    def process_cpu_percent(self, proc: Any) -> float:
        """Return current process CPU percent."""
        return float(proc.cpu_percent())

    def process_memory_percent(self, proc: Any) -> float:
        """Return current process memory percent."""
        return float(proc.memory_percent())

    def process_status(self, proc: Any) -> str:
        """Return process status string."""
        return str(proc.status())

    def process_create_time(self, proc: Any) -> float:
        """Return process create time."""
        return float(proc.create_time())

    def process_cpu_times(self, proc: Any) -> Any:
        """Return process CPU times payload."""
        return proc.cpu_times()

    def process_memory_info(self, proc: Any) -> Any:
        """Return process memory info payload."""
        return proc.memory_info()

    def process_num_threads(self, proc: Any) -> int:
        """Return process thread count."""
        return int(proc.num_threads())

    def process_io_counters(self, proc: Any) -> Any:
        """Return process IO counters payload."""
        return proc.io_counters()

    def process_net_connection_count(self, proc: Any) -> int:
        """Return number of active INET connections for process."""
        return len(proc.net_connections(kind="inet"))

    def process_cpu_affinity(self, proc: Any) -> list[int]:
        """Return process CPU affinity."""
        return list(proc.cpu_affinity())

    def process_is_running(self, proc: Any) -> bool:
        """Return whether process is currently running."""
        return bool(proc.is_running())

    def process_oneshot(self, proc: Any) -> ContextManager[Any]:
        """Return context manager for batched process reads."""
        return cast(ContextManager[Any], proc.oneshot())


__all__ = ["PsutilProcessInventoryGateway", "psutil"]
