#!/usr/bin/env python3
"""Comprueba que tu ordenador está listo para el curso.

Uso:
    python3 verificar_entorno.py            # lo imprescindible para empezar
    python3 verificar_entorno.py --todo     # también lo que usarás más adelante

Cada comprobación que falla te muestra el comando exacto para arreglarla.
"""

import argparse
import importlib
import platform
import shutil
import subprocess
import sys

VERDE, ROJO, AMARILLO, FIN = "\033[32m", "\033[31m", "\033[33m", "\033[0m"
if not sys.stdout.isatty() or platform.system() == "Windows":
    VERDE = ROJO = AMARILLO = FIN = ""


def version_de(comando):
    """Primera línea de `comando --version`, o None si no está instalado."""
    ruta = shutil.which(comando)
    if not ruta:
        return None
    try:
        r = subprocess.run([ruta, "--version"], capture_output=True, text=True, timeout=10)
    except (OSError, subprocess.SubprocessError):
        return None
    texto = (r.stdout or r.stderr).strip().splitlines()
    return texto[0] if texto else "instalado"


def numero_mayor(texto):
    """Primer número de una cadena de versión: 'v22.4.0' -> 22."""
    digitos = "".join(c if c.isdigit() else " " for c in texto or "").split()
    return int(digitos[0]) if digitos else 0


def comprobar_python():
    v = sys.version_info
    ok = (v.major, v.minor) >= (3, 11)
    return ok, f"Python {platform.python_version()} en {sys.executable}", "uv python install 3.12"


def comprobar_programa(comando, minimo=None, arreglo=""):
    def prueba():
        v = version_de(comando)
        if v is None:
            return False, f"no se encuentra «{comando}»", arreglo
        if minimo and numero_mayor(v) < minimo:
            return False, f"{v} (necesitas la versión {minimo} o superior)", arreglo
        return True, v, ""
    return prueba


def comprobar_libreria(nombre, arreglo):
    def prueba():
        try:
            modulo = importlib.import_module(nombre)
        except ImportError:
            return False, f"«{nombre}» no está instalado en este Python", arreglo
        return True, f"{nombre} {getattr(modulo, '__version__', '')}".strip(), ""
    return prueba


def comprobar_entorno_virtual():
    dentro = sys.prefix != getattr(sys, "base_prefix", sys.prefix)
    return dentro, ("estás dentro de un entorno virtual" if dentro else "no estás en un entorno virtual"), \
        "uv venv && source .venv/bin/activate   (en Windows: .venv\\Scripts\\activate)"


def comprobar_gpu():
    try:
        import torch
    except ImportError:
        return True, "PyTorch no instalado: lo instalarás cuando lo pida una lección", ""
    if torch.cuda.is_available():
        return True, f"GPU NVIDIA: {torch.cuda.get_device_name(0)}", ""
    if getattr(torch.backends, "mps", None) and torch.backends.mps.is_available():
        return True, "GPU de Apple (MPS) disponible", ""
    return True, "sin GPU: no pasa nada, la mayoría de lecciones funcionan en CPU", ""


IMPRESCINDIBLE = [
    ("Python 3.11 o superior", comprobar_python),
    ("Git", comprobar_programa("git", arreglo="macOS: xcode-select --install · Ubuntu: sudo apt install -y git")),
    ("uv (gestor de paquetes)", comprobar_programa("uv", arreglo="curl -LsSf https://astral.sh/uv/install.sh | sh")),
    ("Entorno virtual activo", comprobar_entorno_virtual),
    ("NumPy", comprobar_libreria("numpy", "uv pip install numpy")),
]

MAS_ADELANTE = [
    ("Node.js 20 o superior", comprobar_programa("node", 20, "curl -fsSL https://fnm.vercel.app/install | bash && fnm install 22")),
    ("pnpm", comprobar_programa("pnpm", arreglo="npm install -g pnpm")),
    ("Jupyter", comprobar_libreria("jupyter", "uv pip install jupyter")),
    ("Matplotlib", comprobar_libreria("matplotlib", "uv pip install matplotlib")),
    ("Docker", comprobar_programa("docker", arreglo="https://docs.docker.com/get-docker/")),
    ("GPU", comprobar_gpu),
]


def ejecutar(lista, obligatorias):
    fallos = 0
    for nombre, prueba in lista:
        ok, detalle, arreglo = prueba()
        if ok:
            print(f"  {VERDE}✔{FIN} {nombre}: {detalle}")
        else:
            marca = f"{ROJO}✘{FIN}" if obligatorias else f"{AMARILLO}•{FIN}"
            print(f"  {marca} {nombre}: {detalle}")
            print(f"      Arreglo: {arreglo}")
            fallos += obligatorias
    return fallos


def main():
    p = argparse.ArgumentParser(description="Comprueba tu entorno para el curso.")
    p.add_argument("--todo", action="store_true", help="incluye las herramientas de lecciones posteriores")
    args = p.parse_args()

    print(f"\nSistema: {platform.system()} {platform.release()} ({platform.machine()})\n")
    print("Imprescindible para empezar:")
    fallos = ejecutar(IMPRESCINDIBLE, obligatorias=True)
    if args.todo:
        print("\nPara más adelante (no bloquea):")
        ejecutar(MAS_ADELANTE, obligatorias=False)

    if fallos:
        print(f"\n{ROJO}Hay {fallos} comprobaciones pendientes.{FIN} Aplica los arreglos y vuelve a ejecutar el script.\n")
        sys.exit(1)
    print(f"\n{VERDE}¡Todo listo! Tu entorno está preparado para empezar el curso.{FIN}\n")


if __name__ == "__main__":
    main()
