"""Package name mappings for Nix.

Most nixpkgs names match Debian conventions, but some differ.
This module provides translation between canonical (Debian-style)
package names and nixpkgs attribute names.
"""

from __future__ import annotations

import logging

logger = logging.getLogger(__name__)

# Canonical package name -> nixpkgs attribute name
# Only include packages that differ from the canonical name
NIX_PACKAGE_MAPPINGS: dict[str, str] = {
    # Python
    "python3": "python3",
    "python3-pip": "python3Packages.pip",
    "python3-venv": "python3",  # venv is built-in to python3
    "python3-dev": "python3",  # headers included in python3
    "python3-setuptools": "python3Packages.setuptools",
    "python3-wheel": "python3Packages.wheel",
    "python3-poetry": "poetry",
    "pipx": "pipx",
    # Node.js
    "nodejs": "nodejs",
    "npm": "nodejs",  # npm is included with nodejs in nixpkgs
    "yarn": "yarn",
    "pnpm": "pnpm",
    # Build tools
    "build-essential": "gcc",  # stdenv provides most build tools
    "cmake": "cmake",
    "make": "gnumake",
    "autoconf": "autoconf",
    "automake": "automake",
    "pkg-config": "pkg-config",
    "ninja-build": "ninja",
    "meson": "meson",
    # Compilers and languages
    "gcc": "gcc",
    "g++": "gcc",  # g++ is part of gcc
    "clang": "clang",
    "rustc": "rustc",
    "cargo": "cargo",
    "golang": "go",
    "golang-go": "go",
    "openjdk-17-jdk": "jdk17",
    "openjdk-21-jdk": "jdk21",
    "default-jdk": "jdk",
    # Libraries (dev packages)
    "libssl-dev": "openssl.dev",
    "libffi-dev": "libffi.dev",
    "zlib1g-dev": "zlib.dev",
    "libreadline-dev": "readline.dev",
    "libsqlite3-dev": "sqlite.dev",
    "libpq-dev": "postgresql.dev",
    "libmysqlclient-dev": "libmysqlclient.dev",
    "libcurl4-openssl-dev": "curl.dev",
    "libxml2-dev": "libxml2.dev",
    "libxslt1-dev": "libxslt.dev",
    "libyaml-dev": "libyaml.dev",
    "libbz2-dev": "bzip2.dev",
    "liblzma-dev": "xz.dev",
    "libncurses-dev": "ncurses.dev",
    "libgmp-dev": "gmp.dev",
    # CLI tools
    "vim": "vim",
    "neovim": "neovim",
    "emacs": "emacs",
    "nano": "nano",
    "git": "git",
    "git-lfs": "git-lfs",
    "curl": "curl",
    "wget": "wget",
    "httpie": "httpie",
    "ripgrep": "ripgrep",
    "fd-find": "fd",
    "bat": "bat",
    "exa": "eza",  # exa is now eza
    "fzf": "fzf",
    "jq": "jq",
    "yq": "yq",
    "tree": "tree",
    "htop": "htop",
    "btop": "btop",
    "tmux": "tmux",
    "screen": "screen",
    "zsh": "zsh",
    "fish": "fish",
    "starship": "starship",
    "direnv": "direnv",
    # Network tools
    "openssh-client": "openssh",
    "openssh-server": "openssh",
    "netcat": "netcat",
    "netcat-openbsd": "netcat",
    "nmap": "nmap",
    "tcpdump": "tcpdump",
    "traceroute": "traceroute",
    "dnsutils": "dnsutils",
    "bind9-dnsutils": "dnsutils",
    "iproute2": "iproute2",
    "iputils-ping": "iputils",
    "whois": "whois",
    "socat": "socat",
    # Databases
    "postgresql": "postgresql",
    "postgresql-client": "postgresql",
    "mysql-client": "mariadb-client",
    "mariadb-client": "mariadb-client",
    "redis": "redis",
    "redis-tools": "redis",
    "sqlite3": "sqlite",
    "mongodb-clients": "mongosh",
    # Containers and virtualization
    "docker.io": "docker",
    "docker-compose": "docker-compose",
    "podman": "podman",
    "buildah": "buildah",
    "skopeo": "skopeo",
    # Cloud tools
    "awscli": "awscli2",
    "aws-cli": "awscli2",
    "azure-cli": "azure-cli",
    "google-cloud-sdk": "google-cloud-sdk",
    # Kubernetes
    "kubectl": "kubectl",
    "helm": "kubernetes-helm",
    "k9s": "k9s",
    "kubectx": "kubectx",
    # Infrastructure
    "terraform": "terraform",
    "ansible": "ansible",
    "packer": "packer",
    "vault": "vault",
    # Version control
    "subversion": "subversion",
    "mercurial": "mercurial",
    # Archivers
    "zip": "zip",
    "unzip": "unzip",
    "p7zip": "p7zip",
    "p7zip-full": "p7zip",
    "xz-utils": "xz",
    "gzip": "gzip",
    "bzip2": "bzip2",
    "zstd": "zstd",
    # Text processing
    "sed": "gnused",
    "gawk": "gawk",
    "grep": "gnugrep",
    "less": "less",
    "most": "most",
    # File tools
    "rsync": "rsync",
    "rclone": "rclone",
    "sshfs": "sshfs",
    # Development utilities
    "shellcheck": "shellcheck",
    "shfmt": "shfmt",
    "hadolint": "hadolint",
    "pre-commit": "pre-commit",
    # Misc
    "ca-certificates": "cacert",
    "gnupg": "gnupg",
    "gnupg2": "gnupg",
    "pass": "pass",
    "age": "age",
    "sops": "sops",
    "man-db": "man",
    "locales": "",  # Not needed in Nix, handled differently
    "tzdata": "tzdata",
    "file": "file",
    "strace": "strace",
    "ltrace": "ltrace",
    "gdb": "gdb",
    "lldb": "lldb",
    "valgrind": "valgrind",
}

# Packages that should be skipped in Nix (not needed or handled differently)
NIX_SKIP_PACKAGES: set[str] = {
    "locales",
    "apt-transport-https",
    "software-properties-common",
    "lsb-release",
    "gnupg-agent",
}

# Meta-packages that expand to multiple Nix packages
NIX_META_PACKAGES: dict[str, list[str]] = {
    "build-essential": ["gcc", "gnumake", "binutils", "coreutils"],
    "python3-dev-full": ["python3", "python3Packages.pip", "python3Packages.setuptools"],
}


def translate_to_nix(canonical: str) -> str | None:
    """Translate a canonical package name to nixpkgs attribute name.

    Args:
        canonical: Canonical (Debian-style) package name

    Returns:
        Nixpkgs attribute name, or None if should be skipped
    """
    # Check if should be skipped
    if canonical in NIX_SKIP_PACKAGES:
        logger.debug(f"Skipping package '{canonical}' (not needed in Nix)")
        return None

    # Check if we have a mapping
    if canonical in NIX_PACKAGE_MAPPINGS:
        mapped = NIX_PACKAGE_MAPPINGS[canonical]
        if not mapped:  # Empty string means skip
            return None
        return mapped

    # Pass through as-is (most packages have the same name)
    return canonical


def translate_packages_to_nix(packages: list[str]) -> list[str]:
    """Translate a list of canonical package names to nixpkgs names.

    Args:
        packages: List of canonical package names

    Returns:
        List of nixpkgs attribute names (skipped packages removed)
    """
    result = []
    seen = set()

    for pkg in packages:
        # Check for meta-packages first
        if pkg in NIX_META_PACKAGES:
            for expanded in NIX_META_PACKAGES[pkg]:
                if expanded not in seen:
                    result.append(expanded)
                    seen.add(expanded)
            continue

        # Translate single package
        nix_name = translate_to_nix(pkg)
        if nix_name and nix_name not in seen:
            result.append(nix_name)
            seen.add(nix_name)

    return result


def get_nix_package_attr(package: str) -> str:
    """Get the full nixpkgs attribute path for a package.

    Args:
        package: Package name (either canonical or nixpkgs)

    Returns:
        Full attribute path like 'nixpkgs.python3'
    """
    # If already looks like an attribute path, return as-is
    if "." in package and not package.endswith(".dev"):
        return package

    # Translate if needed
    nix_name = translate_to_nix(package)
    if not nix_name:
        return package

    return f"nixpkgs.{nix_name}"


def validate_nix_packages(packages: list[str]) -> tuple[list[str], list[str]]:
    """Validate package names for Nix compatibility.

    Args:
        packages: List of package names to validate

    Returns:
        Tuple of (valid_packages, invalid_packages)
    """
    valid = []
    invalid = []

    for pkg in packages:
        # Basic validation - nixpkgs names are alphanumeric with dashes/underscores
        if not pkg:
            continue

        # Check for obviously invalid characters
        if any(c in pkg for c in ["$", "{", "}", "(", ")", ";", "&", "|", ">", "<"]):
            invalid.append(pkg)
            continue

        valid.append(pkg)

    return valid, invalid
