"""Intel GPU configuration handler.

This module provides Intel-specific GPU configuration for different
container runtimes.
"""

from __future__ import annotations

from pathlib import Path

from gaol.gpu.models import DeviceMapping, GPUConfig


class IntelGPU:
    """Intel GPU configuration handler.

    Provides methods to generate runtime-specific GPU configurations
    for Intel integrated and discrete GPUs.

    Example:
        >>> config = GPUConfig(enabled=True)
        >>> docker_config = IntelGPU.get_docker_config(config)
        >>> any("/dev/dri" in d for d in docker_config["devices"])
        True
    """

    DRI_PATH = Path("/dev/dri")

    @staticmethod
    def get_docker_config(gpu_config: GPUConfig) -> dict:
        """Get Docker-specific GPU configuration for Intel.

        Intel GPUs use device mappings for /dev/dri devices.

        Args:
            gpu_config: GPU configuration.

        Returns:
            Dictionary with devices and environment variables.
        """
        devices = IntelGPU.get_required_devices(gpu_config.device_ids)

        environment: dict[str, str] = {}

        return {
            "devices": devices,
            "environment": environment,
        }

    @staticmethod
    def get_nspawn_config(gpu_config: GPUConfig) -> dict:
        """Get nspawn-specific GPU configuration for Intel.

        Args:
            gpu_config: GPU configuration.

        Returns:
            Dictionary with bind mounts and environment variables.
        """
        binds = IntelGPU.get_required_devices(gpu_config.device_ids)

        return {
            "binds": binds,
            "environment": {},
        }

    @staticmethod
    def get_libvirt_config(
        gpu_config: GPUConfig,  # noqa: ARG004
        pci_addresses: list[str],
    ) -> dict:
        """Get libvirt-specific GPU configuration for Intel.

        Note: Intel integrated GPUs typically cannot be passed through
        as they share memory with the CPU. This is mainly for discrete
        Intel GPUs (Arc series).

        Args:
            gpu_config: GPU configuration (reserved for future use).
            pci_addresses: List of PCI addresses.

        Returns:
            Dictionary with XML snippets.
        """
        xml_parts: list[str] = []

        for pci_addr in pci_addresses:
            parts = pci_addr.replace(":", ".").split(".")
            if len(parts) >= 4:
                domain = parts[0]
                bus = parts[1]
                slot = parts[2]
                function = parts[3]

                xml_parts.append(
                    f"""    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x{domain}' bus='0x{bus}' slot='0x{slot}' function='0x{function}'/>
      </source>
    </hostdev>"""
                )

        return {
            "hostdev_xml": "\n".join(xml_parts),
            "requires_iommu": True,
        }

    @staticmethod
    def get_required_devices(device_ids: list[int] | None = None) -> list[str]:
        """Get list of required Intel GPU device paths.

        Args:
            device_ids: Specific GPU device IDs, or None for all.

        Returns:
            List of device paths.
        """
        devices: list[str] = []

        if not IntelGPU.DRI_PATH.exists():
            return devices

        if device_ids:
            # Map device IDs to render nodes
            for device_id in device_ids:
                render_node = IntelGPU.DRI_PATH / f"renderD{128 + device_id}"
                if render_node.exists():
                    devices.append(str(render_node))

                card_node = IntelGPU.DRI_PATH / f"card{device_id}"
                if card_node.exists():
                    devices.append(str(card_node))
        else:
            # Add all render and card devices
            for path in sorted(IntelGPU.DRI_PATH.iterdir()):
                if path.name.startswith("renderD") or path.name.startswith("card"):
                    devices.append(str(path))

        return devices

    @staticmethod
    def get_device_mappings(
        device_ids: list[int] | None = None,
    ) -> list[DeviceMapping]:
        """Get device mappings for Intel GPUs.

        Args:
            device_ids: Specific GPU device IDs, or None for all.

        Returns:
            List of DeviceMapping objects.
        """
        mappings: list[DeviceMapping] = []

        for device_path in IntelGPU.get_required_devices(device_ids):
            mappings.append(
                DeviceMapping(
                    host_path=device_path,
                    container_path=device_path,
                    permissions="rwm",
                )
            )

        return mappings

    @staticmethod
    def get_required_capabilities() -> list[str]:
        """Get Linux capabilities required for Intel GPU access.

        Returns:
            List of capability names.
        """
        # Intel GPUs typically don't require special capabilities
        # for basic access, but SYS_ADMIN may be needed for some
        # advanced features.
        return []

    @staticmethod
    def is_integrated_gpu(pci_address: str | None) -> bool:
        """Check if the GPU is an integrated GPU.

        Integrated GPUs (iGPU) cannot be passed through to VMs
        as they share memory with the CPU.

        Args:
            pci_address: PCI address of the GPU.

        Returns:
            True if this is likely an integrated GPU.
        """
        if not pci_address:
            return True  # Assume integrated if no PCI address

        # Integrated GPUs are typically on bus 00
        # e.g., 0000:00:02.0
        parts = pci_address.split(":")
        if len(parts) >= 2:
            bus = parts[1].split(".")[0] if "." in parts[1] else parts[1]
            return bus == "00"

        return True
