"""Distro registry and package/service translation for multi-distro support."""

from __future__ import annotations

import logging
from enum import Enum

from pydantic import BaseModel, Field

logger = logging.getLogger(__name__)


class PackageManager(str, Enum):
    """Package manager types."""

    APT = "apt"
    DNF = "dnf"
    APK = "apk"
    PACMAN = "pacman"


class ServiceManager(str, Enum):
    """Service manager types."""

    SYSTEMD = "systemd"
    OPENRC = "openrc"


class DistroFamily(str, Enum):
    """Distribution family."""

    DEBIAN = "debian"
    REDHAT = "redhat"
    ALPINE = "alpine"
    ARCH = "arch"


class DistroInfo(BaseModel):
    """Information about a Linux distribution."""

    name: str = Field(..., description="Distribution name/codename")
    family: DistroFamily = Field(..., description="Distribution family")
    package_manager: PackageManager = Field(..., description="Package manager type")
    service_manager: ServiceManager = Field(
        default=ServiceManager.SYSTEMD, description="Service manager type"
    )
    version: str | None = Field(default=None, description="Distribution version")
    docker_image: str | None = Field(default=None, description="Docker image reference")
    cloud_image_url: str | None = Field(default=None, description="Cloud image URL for libvirt")
    debootstrap_cmd: str | None = Field(
        default=None, description="Debootstrap command for nspawn (Debian/Ubuntu only)"
    )
    mirror: str | None = Field(default=None, description="Package mirror URL")


# Registry of all supported distributions
DISTRO_REGISTRY: dict[str, DistroInfo] = {
    # Debian
    "trixie": DistroInfo(
        name="trixie",
        family=DistroFamily.DEBIAN,
        package_manager=PackageManager.APT,
        service_manager=ServiceManager.SYSTEMD,
        version="13",
        docker_image="debian:trixie",
        cloud_image_url="https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-generic-amd64-daily.qcow2",
        debootstrap_cmd="debootstrap --variant=minbase trixie",
        mirror="http://deb.debian.org/debian",
    ),
    "bookworm": DistroInfo(
        name="bookworm",
        family=DistroFamily.DEBIAN,
        package_manager=PackageManager.APT,
        service_manager=ServiceManager.SYSTEMD,
        version="12",
        docker_image="debian:bookworm",
        cloud_image_url="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2",
        debootstrap_cmd="debootstrap --variant=minbase bookworm",
        mirror="http://deb.debian.org/debian",
    ),
    "bullseye": DistroInfo(
        name="bullseye",
        family=DistroFamily.DEBIAN,
        package_manager=PackageManager.APT,
        service_manager=ServiceManager.SYSTEMD,
        version="11",
        docker_image="debian:bullseye",
        cloud_image_url="https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-generic-amd64.qcow2",
        debootstrap_cmd="debootstrap --variant=minbase bullseye",
        mirror="http://deb.debian.org/debian",
    ),
    # Ubuntu
    "noble": DistroInfo(
        name="noble",
        family=DistroFamily.DEBIAN,
        package_manager=PackageManager.APT,
        service_manager=ServiceManager.SYSTEMD,
        version="24.04",
        docker_image="ubuntu:noble",
        cloud_image_url="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img",
        debootstrap_cmd="debootstrap --variant=minbase noble",
        mirror="http://archive.ubuntu.com/ubuntu",
    ),
    "jammy": DistroInfo(
        name="jammy",
        family=DistroFamily.DEBIAN,
        package_manager=PackageManager.APT,
        service_manager=ServiceManager.SYSTEMD,
        version="22.04",
        docker_image="ubuntu:jammy",
        cloud_image_url="https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img",
        debootstrap_cmd="debootstrap --variant=minbase jammy",
        mirror="http://archive.ubuntu.com/ubuntu",
    ),
    "focal": DistroInfo(
        name="focal",
        family=DistroFamily.DEBIAN,
        package_manager=PackageManager.APT,
        service_manager=ServiceManager.SYSTEMD,
        version="20.04",
        docker_image="ubuntu:focal",
        cloud_image_url="https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img",
        debootstrap_cmd="debootstrap --variant=minbase focal",
        mirror="http://archive.ubuntu.com/ubuntu",
    ),
    # Fedora
    "fedora": DistroInfo(
        name="fedora",
        family=DistroFamily.REDHAT,
        package_manager=PackageManager.DNF,
        service_manager=ServiceManager.SYSTEMD,
        version="41",
        docker_image="fedora:41",
        cloud_image_url="https://download.fedoraproject.org/pub/fedora/linux/releases/41/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2",
    ),
    # Alpine
    "alpine": DistroInfo(
        name="alpine",
        family=DistroFamily.ALPINE,
        package_manager=PackageManager.APK,
        service_manager=ServiceManager.OPENRC,
        version="3.20",
        docker_image="alpine:3.20",
        cloud_image_url="https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/nocloud_alpine-3.20.0-x86_64-bios-cloudinit-r0.qcow2",
    ),
    # Arch Linux
    "arch": DistroInfo(
        name="arch",
        family=DistroFamily.ARCH,
        package_manager=PackageManager.PACMAN,
        service_manager=ServiceManager.SYSTEMD,
        docker_image="archlinux:latest",
        # Arch doesn't provide official cloud images
    ),
}


# Package name mappings: canonical name -> {family: actual_package}
# Canonical names match Debian package names for compatibility
# None means package is not available on that family
PACKAGE_MAPPINGS: dict[str, dict[str, str | None]] = {
    # Core utilities (base profile)
    "curl": {"debian": "curl", "redhat": "curl", "alpine": "curl", "arch": "curl"},
    "wget": {"debian": "wget", "redhat": "wget", "alpine": "wget", "arch": "wget"},
    "vim": {"debian": "vim", "redhat": "vim-enhanced", "alpine": "vim", "arch": "vim"},
    "htop": {"debian": "htop", "redhat": "htop", "alpine": "htop", "arch": "htop"},
    "less": {"debian": "less", "redhat": "less", "alpine": "less", "arch": "less"},
    "procps": {"debian": "procps", "redhat": "procps-ng", "alpine": "procps-ng", "arch": "procps-ng"},
    "net-tools": {"debian": "net-tools", "redhat": "net-tools", "alpine": "net-tools", "arch": "net-tools"},
    "iputils-ping": {"debian": "iputils-ping", "redhat": "iputils", "alpine": "iputils", "arch": "iputils"},
    # Tor profile
    "tor": {"debian": "tor", "redhat": "tor", "alpine": "tor", "arch": "tor"},
    "torsocks": {"debian": "torsocks", "redhat": "torsocks", "alpine": "torsocks", "arch": "torsocks"},
    # GUI profile
    "xauth": {"debian": "xauth", "redhat": "xorg-x11-xauth", "alpine": "xauth", "arch": "xorg-xauth"},
    "x11-apps": {"debian": "x11-apps", "redhat": "xorg-x11-apps", "alpine": "xorg-apps", "arch": "xorg-apps"},
    "dbus-x11": {"debian": "dbus-x11", "redhat": "dbus-x11", "alpine": "dbus-x11", "arch": "dbus"},
    "libgl1-mesa-glx": {"debian": "libgl1-mesa-glx", "redhat": "mesa-libGL", "alpine": "mesa-gl", "arch": "mesa"},
    "libgl1-mesa-dri": {"debian": "libgl1-mesa-dri", "redhat": "mesa-dri-drivers", "alpine": "mesa-dri-gallium", "arch": "mesa"},
    # Sunshine profile
    "udev": {"debian": "udev", "redhat": "systemd-udev", "alpine": "eudev", "arch": "systemd"},
    # RustDesk profile
    "libxdo3": {"debian": "libxdo3", "redhat": "xdotool", "alpine": "xdotool", "arch": "xdotool"},
    "libxfixes3": {"debian": "libxfixes3", "redhat": "libXfixes", "alpine": "libxfixes", "arch": "libxfixes"},
    "libxrandr2": {"debian": "libxrandr2", "redhat": "libXrandr", "alpine": "libxrandr", "arch": "libxrandr"},
    "libxcb-shape0": {"debian": "libxcb-shape0", "redhat": "libxcb", "alpine": "libxcb", "arch": "libxcb"},
    # VNC profile
    "tigervnc-standalone-server": {"debian": "tigervnc-standalone-server", "redhat": "tigervnc-server", "alpine": "tigervnc", "arch": "tigervnc"},
    "tigervnc-common": {"debian": "tigervnc-common", "redhat": "tigervnc-server", "alpine": "tigervnc", "arch": "tigervnc"},
    "xfce4": {"debian": "xfce4", "redhat": "@xfce-desktop-environment", "alpine": "xfce4", "arch": "xfce4"},
    "xfce4-terminal": {"debian": "xfce4-terminal", "redhat": "xfce4-terminal", "alpine": "xfce4-terminal", "arch": "xfce4-terminal"},
    # RDP profile
    "xrdp": {"debian": "xrdp", "redhat": "xrdp", "alpine": "xrdp", "arch": "xrdp"},
    # Dev Python profile
    "python3": {"debian": "python3", "redhat": "python3", "alpine": "python3", "arch": "python"},
    "python3-pip": {"debian": "python3-pip", "redhat": "python3-pip", "alpine": "py3-pip", "arch": "python-pip"},
    "python3-venv": {"debian": "python3-venv", "redhat": "python3-virtualenv", "alpine": "python3", "arch": "python-virtualenv"},
    "git": {"debian": "git", "redhat": "git", "alpine": "git", "arch": "git"},
    # Additional common packages
    "openssh-server": {"debian": "openssh-server", "redhat": "openssh-server", "alpine": "openssh", "arch": "openssh"},
    "openssh-client": {"debian": "openssh-client", "redhat": "openssh-clients", "alpine": "openssh-client", "arch": "openssh"},
    "sudo": {"debian": "sudo", "redhat": "sudo", "alpine": "sudo", "arch": "sudo"},
    "bash": {"debian": "bash", "redhat": "bash", "alpine": "bash", "arch": "bash"},
}


# Service name mappings: canonical name -> {family: actual_service}
SERVICE_MAPPINGS: dict[str, dict[str, str]] = {
    "ssh": {"debian": "ssh", "redhat": "sshd", "alpine": "sshd", "arch": "sshd"},
    "sshd": {"debian": "ssh", "redhat": "sshd", "alpine": "sshd", "arch": "sshd"},
    "tor": {"debian": "tor", "redhat": "tor", "alpine": "tor", "arch": "tor"},
    "tailscaled": {"debian": "tailscaled", "redhat": "tailscaled", "alpine": "tailscaled", "arch": "tailscaled"},
    "xrdp": {"debian": "xrdp", "redhat": "xrdp", "alpine": "xrdp", "arch": "xrdp"},
    "xrdp-sesman": {"debian": "xrdp-sesman", "redhat": "xrdp-sesman", "alpine": "xrdp-sesman", "arch": "xrdp-sesman"},
    "sunshine": {"debian": "sunshine", "redhat": "sunshine", "alpine": "sunshine", "arch": "sunshine"},
}


def get_distro_info(distro: str) -> DistroInfo:
    """Get information about a distribution.

    Args:
        distro: Distribution name (e.g., 'trixie', 'fedora', 'alpine')

    Returns:
        DistroInfo for the distribution

    Raises:
        KeyError: If distribution is not supported
    """
    if distro not in DISTRO_REGISTRY:
        available = ", ".join(sorted(DISTRO_REGISTRY.keys()))
        raise KeyError(f"Unknown distribution: {distro}. Available: {available}")
    return DISTRO_REGISTRY[distro]


def get_distro_family(distro: str) -> DistroFamily:
    """Get the family of a distribution.

    Args:
        distro: Distribution name

    Returns:
        DistroFamily enum value
    """
    return get_distro_info(distro).family


def translate_package(canonical: str, distro: str) -> str | None:
    """Translate a canonical package name to distro-specific name.

    Args:
        canonical: Canonical package name (usually Debian name)
        distro: Target distribution name

    Returns:
        Distro-specific package name, or None if not available
    """
    family = get_distro_family(distro).value

    if canonical in PACKAGE_MAPPINGS:
        return PACKAGE_MAPPINGS[canonical].get(family)

    # If not in mappings, return as-is (assume it's the same across distros)
    return canonical


def translate_packages(packages: list[str], distro: str) -> list[str]:
    """Translate a list of canonical package names to distro-specific names.

    Args:
        packages: List of canonical package names
        distro: Target distribution name

    Returns:
        List of distro-specific package names (unavailable packages logged and skipped)
    """
    result = []
    for pkg in packages:
        translated = translate_package(pkg, distro)
        if translated is None:
            logger.warning(f"Package '{pkg}' not available on {distro}, skipping")
        else:
            result.append(translated)
    return result


def translate_service(canonical: str, distro: str) -> str:
    """Translate a canonical service name to distro-specific name.

    Args:
        canonical: Canonical service name
        distro: Target distribution name

    Returns:
        Distro-specific service name
    """
    family = get_distro_family(distro).value

    if canonical in SERVICE_MAPPINGS:
        return SERVICE_MAPPINGS[canonical].get(family, canonical)

    # If not in mappings, return as-is
    return canonical


def get_package_manager_commands(distro: str) -> dict[str, str]:
    """Get package manager commands for a distribution.

    Args:
        distro: Distribution name

    Returns:
        Dict with 'update', 'install', and 'clean' commands
    """
    info = get_distro_info(distro)

    commands = {
        PackageManager.APT: {
            "update": "apt-get update",
            "install": "apt-get install -y",
            "clean": "apt-get clean && rm -rf /var/lib/apt/lists/*",
        },
        PackageManager.DNF: {
            "update": "dnf check-update || true",
            "install": "dnf install -y",
            "clean": "dnf clean all",
        },
        PackageManager.APK: {
            "update": "apk update",
            "install": "apk add --no-cache",
            "clean": "rm -rf /var/cache/apk/*",
        },
        PackageManager.PACMAN: {
            "update": "pacman -Sy",
            "install": "pacman -S --noconfirm",
            "clean": "pacman -Scc --noconfirm",
        },
    }

    return commands[info.package_manager]


def get_service_commands(distro: str, service: str, action: str) -> str:
    """Get service management command for a distribution.

    Args:
        distro: Distribution name
        service: Service name (will be translated)
        action: Action to perform ('enable', 'start', 'stop', 'restart', 'status')

    Returns:
        Shell command to perform the action
    """
    info = get_distro_info(distro)
    translated_service = translate_service(service, distro)

    if info.service_manager == ServiceManager.SYSTEMD:
        return f"systemctl {action} {translated_service}"
    elif info.service_manager == ServiceManager.OPENRC:
        openrc_commands = {
            "enable": f"rc-update add {translated_service}",
            "start": f"rc-service {translated_service} start",
            "stop": f"rc-service {translated_service} stop",
            "restart": f"rc-service {translated_service} restart",
            "status": f"rc-service {translated_service} status",
        }
        return openrc_commands.get(action, f"rc-service {translated_service} {action}")
    else:
        # Default to systemctl
        return f"systemctl {action} {translated_service}"


def list_supported_distros() -> list[str]:
    """List all supported distribution names.

    Returns:
        Sorted list of distribution names
    """
    return sorted(DISTRO_REGISTRY.keys())


def get_docker_image(distro: str) -> str:
    """Get Docker image for a distribution.

    Args:
        distro: Distribution name

    Returns:
        Docker image reference

    Raises:
        KeyError: If distro not found or no Docker image available
    """
    info = get_distro_info(distro)
    if not info.docker_image:
        raise KeyError(f"No Docker image available for: {distro}")
    return info.docker_image


def get_cloud_image_url(distro: str) -> str:
    """Get cloud image URL for a distribution.

    Args:
        distro: Distribution name

    Returns:
        Cloud image URL

    Raises:
        KeyError: If distro not found or no cloud image available
    """
    info = get_distro_info(distro)
    if not info.cloud_image_url:
        raise KeyError(f"No cloud image available for: {distro}")
    return info.cloud_image_url


def get_debootstrap_cmd(distro: str) -> str:
    """Get debootstrap command for a distribution.

    Args:
        distro: Distribution name

    Returns:
        Debootstrap command

    Raises:
        KeyError: If distro not found or not a Debian-based distro
    """
    info = get_distro_info(distro)
    if not info.debootstrap_cmd:
        raise KeyError(f"Debootstrap not available for: {distro}")
    return info.debootstrap_cmd


def get_mirror(distro: str) -> str | None:
    """Get package mirror URL for a distribution.

    Args:
        distro: Distribution name

    Returns:
        Mirror URL or None
    """
    info = get_distro_info(distro)
    return info.mirror
