"""mldash setup — create and manage the managed Python environment."""

import os
import subprocess
import sys
import venv

from ._env import managed_venv_path, mldash_home

MIN_PYTHON = (3, 9)

DEFAULT_PACKAGES = [
    "pandas",
    "numpy",
    "scikit-learn",
    "matplotlib",
    "seaborn",
    "mldash-sdk",
]


def check_python_version():
    """Check if the current Python meets the minimum version.

    Returns:
        (ok, version_str, message)
    """
    version = sys.version_info[:2]
    version_str = f"{version[0]}.{version[1]}"

    if version >= MIN_PYTHON:
        return (True, version_str, "")

    min_str = f"{MIN_PYTHON[0]}.{MIN_PYTHON[1]}"

    if sys.platform == "darwin":
        hint = (
            f"  Install or upgrade Python:\n"
            f"    - Download from https://www.python.org/downloads/\n"
            f"    - Or: brew install python"
        )
    elif sys.platform == "win32":
        hint = (
            f"  Install or upgrade Python:\n"
            f"    - Download from https://www.python.org/downloads/\n"
            f"    - Make sure to check 'Add Python to PATH' during installation"
        )
    else:
        hint = (
            f"  Install or upgrade Python:\n"
            f"    - Download from https://www.python.org/downloads/\n"
            f"    - Or: sudo apt install python3 (Debian/Ubuntu)\n"
            f"    - Or: sudo dnf install python3 (Fedora)"
        )

    message = (
        f"Python {min_str}+ is required, but you have {version_str}.\n\n{hint}"
    )
    return (False, version_str, message)


def run_setup(force=False, no_autocomplete=False):
    """Create the managed venv and install default packages.

    Args:
        force: If True, recreate the venv from scratch.
        no_autocomplete: If True, skip llama-cpp-python install and model download.
    """
    ok, version_str, message = check_python_version()
    if not ok:
        print(f"Error: {message}")
        sys.exit(1)

    venv_path = managed_venv_path()
    home = mldash_home()
    exists = os.path.isdir(venv_path) and not force

    # Ensure ~/.mldash exists
    os.makedirs(home, exist_ok=True)

    if exists:
        # Check if all default packages are already installed
        if not force and _packages_installed(venv_path):
            print(f"Environment ready at {venv_path}")
            print("All packages already installed. Nothing to do.")
            print(f"\nRun `mldash setup --force` to reinstall from scratch.")
            return

        print(f"Managed environment exists at {venv_path}")
        print("Updating packages...\n")
    else:
        action = "Recreating" if force else "Creating"
        print(f"{action} managed environment at {venv_path}...")
        try:
            venv.create(venv_path, with_pip=True, clear=force)
        except Exception as e:
            error_msg = str(e).lower()
            if "ensurepip" in error_msg or "no module named" in error_msg:
                if sys.platform == "linux":
                    print(
                        "\nError: python3-venv is not installed.\n"
                        "  Fix: sudo apt install python3-venv  (Debian/Ubuntu)\n"
                        "       sudo dnf install python3-venv  (Fedora)"
                    )
                else:
                    print(f"\nError creating venv: {e}")
            else:
                print(f"\nError creating venv: {e}")
            sys.exit(1)
        print("Environment created.\n")

    # Upgrade pip
    print("Upgrading pip...")
    _run_pip(["install", "--upgrade", "pip"], quiet=True)

    # Install default packages
    total = len(DEFAULT_PACKAGES)
    for i, pkg in enumerate(DEFAULT_PACKAGES, 1):
        print(f"[{i}/{total}] Installing {pkg}...")
        result = _run_pip(["install", "--upgrade", pkg])
        if result != 0:
            print(f"  Warning: Failed to install {pkg}")

    # Verify core imports
    print("\nVerifying installation...")
    python = _get_venv_python(venv_path)
    verify_code = "import pandas; import numpy; import sklearn; print('OK')"
    try:
        result = subprocess.run(
            [python, "-c", verify_code],
            capture_output=True, text=True, timeout=30,
        )
        if result.returncode == 0 and "OK" in result.stdout:
            print("All core packages verified.")
        else:
            print("Warning: Some packages may not have installed correctly.")
            if result.stderr:
                print(f"  {result.stderr.strip()}")
    except Exception as e:
        print(f"Warning: Could not verify packages: {e}")

    # ── Autocomplete (best-effort) ──
    if not no_autocomplete:
        print("\nSetting up local autocomplete...")
        # Install llama-cpp-python
        print("  Installing llama-cpp-python...")
        llama_result = _run_pip(["install", "--upgrade", "llama-cpp-python"])
        if llama_result != 0:
            print("  Warning: Failed to install llama-cpp-python. Autocomplete will be unavailable.")
            print("  You can retry later or use --no-autocomplete to skip.")
        else:
            # Download the model
            from ._autocomplete import download_model, model_exists
            if model_exists():
                print("  Autocomplete model already downloaded.")
            else:
                download_model()

    print(f"\nSetup complete! Python {version_str} environment ready.")
    print(f"  Location: {venv_path}")
    print(f"\nNext steps:")
    print(f"  mldash login     # Authenticate with your API key")
    print(f"  mldash connect   # Start the bridge")


def run_install(packages):
    """Install additional packages into the managed venv.

    Args:
        packages: List of package names to install.
    """
    venv_path = managed_venv_path()
    if not os.path.isdir(venv_path):
        print("Managed environment not found. Run `mldash setup` first.")
        sys.exit(1)

    for pkg in packages:
        print(f"Installing {pkg}...")
        result = _run_pip(["install", pkg])
        if result != 0:
            print(f"Error: Failed to install {pkg}")
            sys.exit(1)

    print(f"\nInstalled: {', '.join(packages)}")


def _packages_installed(venv_path):
    """Check if all default packages are importable in the managed venv."""
    python = _get_venv_python(venv_path)
    # Map pip package names to their import names
    import_names = {
        "pandas": "pandas",
        "numpy": "numpy",
        "scikit-learn": "sklearn",
        "matplotlib": "matplotlib",
        "seaborn": "seaborn",
        "mldash-sdk": "mldash",
    }
    imports = "; ".join(f"import {name}" for name in import_names.values())
    check_code = f"{imports}; print('OK')"
    try:
        result = subprocess.run(
            [python, "-c", check_code],
            capture_output=True, text=True, timeout=15,
        )
        return result.returncode == 0 and "OK" in result.stdout
    except Exception:
        return False


def _get_venv_python(venv_path):
    """Return the python executable path inside a venv."""
    if sys.platform == "win32":
        return os.path.join(venv_path, "Scripts", "python.exe")
    return os.path.join(venv_path, "bin", "python")


def _run_pip(args, quiet=False):
    """Run pip inside the managed venv.

    Returns the process return code.
    """
    venv_path = managed_venv_path()
    python = _get_venv_python(venv_path)

    cmd = [python, "-m", "pip"] + args
    if quiet:
        cmd.insert(3, "-q")

    try:
        result = subprocess.run(
            cmd,
            capture_output=True, text=True, timeout=300,
        )
        if result.returncode != 0 and not quiet:
            # Show last line of stderr for context
            stderr_lines = result.stderr.strip().splitlines()
            if stderr_lines:
                print(f"  {stderr_lines[-1]}")
        return result.returncode
    except subprocess.TimeoutExpired:
        print("  Timed out (5 min limit)")
        return 1
    except Exception as e:
        print(f"  Error: {e}")
        return 1
