import shutil
import subprocess
import sys
import platform
from dataclasses import dataclass


@dataclass
class BuildRequirement:
    name: str                  # Human-readable name e.g. "GCC compiler"
    check_command: str         # Command to check if it exists e.g. "gcc"
    install_apt: str | None    # apt package name
    install_brew: str | None   # brew package name
    install_hint: str | None   # Fallback manual hint if auto-install fails


# Known build requirements mapped to PyPI package name fragments
KNOWN_BUILD_DEPS: dict[str, list[BuildRequirement]] = {
    "numpy": [
        BuildRequirement("GCC compiler", "gcc", "gcc", "gcc", None),
        BuildRequirement("Python headers", "python3-config", "python3-dev", None, "Install python3-dev"),
    ],
    "pillow": [
        BuildRequirement("libjpeg", None, "libjpeg-dev", "jpeg", None),
        BuildRequirement("zlib", None, "zlib1g-dev", "zlib", None),
        BuildRequirement("GCC compiler", "gcc", "gcc", "gcc", None),
    ],
    "cryptography": [
        BuildRequirement("Rust toolchain", "rustc", None, "rust", "Install from https://rustup.rs"),
        BuildRequirement("GCC compiler", "gcc", "gcc", "gcc", None),
        BuildRequirement("OpenSSL headers", None, "libssl-dev", "openssl", None),
    ],
    "lxml": [
        BuildRequirement("libxml2", None, "libxml2-dev", "libxml2", None),
        BuildRequirement("libxslt", None, "libxslt-dev", "libxslt", None),
        BuildRequirement("GCC compiler", "gcc", "gcc", "gcc", None),
    ],
    "psycopg2": [
        BuildRequirement("PostgreSQL headers", "pg_config", "libpq-dev", "postgresql", None),
        BuildRequirement("GCC compiler", "gcc", "gcc", "gcc", None),
    ],
}

# Generic build tools needed for any source compilation
GENERIC_BUILD_DEPS = [
    BuildRequirement("GCC compiler", "gcc", "build-essential", "gcc", None),
    BuildRequirement("Python headers", "python3-config", "python3-dev", None, "Install python3-dev or python-dev"),
    BuildRequirement("Make", "make", "make", "make", None),
]


def get_package_manager() -> str | None:
    """Detect the available system package manager."""
    if platform.system() == "Darwin":
        if shutil.which("brew"):
            return "brew"
        return None
    elif platform.system() == "Linux":
        if shutil.which("apt-get"):
            return "apt"
        elif shutil.which("dnf"):
            return "dnf"
        elif shutil.which("pacman"):
            return "pacman"
        return None
    return None


def _is_present(req: BuildRequirement) -> bool:
    """Check if a build requirement is already satisfied."""
    if req.check_command:
        return shutil.which(req.check_command) is not None
    return False


def _install_dep(req: BuildRequirement, pkg_manager: str) -> bool:
    """Attempt to install a build dependency via the system package manager."""
    pkg = None

    if pkg_manager == "apt":
        pkg = req.install_apt
        cmd = ["sudo", "apt-get", "install", "-y", pkg] if pkg else None
    elif pkg_manager == "brew":
        pkg = req.install_brew
        cmd = ["brew", "install", pkg] if pkg else None
    elif pkg_manager == "dnf":
        pkg = req.install_apt  # dnf package names are often the same as apt
        cmd = ["sudo", "dnf", "install", "-y", pkg] if pkg else None
    elif pkg_manager == "pacman":
        pkg = req.install_apt
        cmd = ["sudo", "pacman", "-S", "--noconfirm", pkg] if pkg else None
    else:
        return False

    if not cmd:
        return False

    try:
        result = subprocess.run(cmd, capture_output=True, text=True)
        return result.returncode == 0
    except Exception:
        return False


def check_and_fix_build_deps(package_name: str, needs_compilation: bool) -> dict:
    """
    Check for and silently fix missing build dependencies for a package.
    Returns a report of what was found and what was done.
    """
    if not needs_compilation:
        return {"needed": False}

    pkg_manager = get_package_manager()
    pkg_lower = package_name.lower()

    # Gather required build deps: generic ones + any package-specific ones
    required: list[BuildRequirement] = list(GENERIC_BUILD_DEPS)
    for key, deps in KNOWN_BUILD_DEPS.items():
        if key in pkg_lower or pkg_lower in key:
            required.extend(deps)

    # Deduplicate by name
    seen = set()
    unique_required = []
    for r in required:
        if r.name not in seen:
            seen.add(r.name)
            unique_required.append(r)

    missing = [r for r in unique_required if not _is_present(r)]

    if not missing:
        return {"needed": True, "missing": [], "fixed": [], "failed": []}

    fixed = []
    failed = []

    for req in missing:
        if pkg_manager:
            success = _install_dep(req, pkg_manager)
            if success:
                fixed.append(req.name)
            else:
                failed.append({"dep": req.name, "hint": req.install_hint})
        else:
            failed.append({"dep": req.name, "hint": req.install_hint})

    return {
        "needed": True,
        "missing": [r.name for r in missing],
        "fixed": fixed,
        "failed": failed,
        "pkg_manager": pkg_manager,
    }