"""Backend detection and resolution utilities."""

import logging

from .console import get_console

logger = logging.getLogger(__name__)


def _auto_refresh_profiles_once() -> None:
    """Auto-refresh hardware profiles when cache is missing.

    This is intended for first-run UX (run/serve) and should fail fast on errors.
    """
    from sagellm_backend.hardware import ProfileCache
    from sagellm_backend.hardware.inspectors import get_inspector, list_supported_hardware

    console = get_console()
    cache = ProfileCache()

    console.print("[cyan]ℹ️  No cached hardware profile. Running one-time auto refresh...[/cyan]")

    for hw_type in list_supported_hardware():
        inspector = get_inspector(hw_type)
        profile = inspector.get_profile(include_functional=False)
        cache.update(hw_type, profile)


def detect_backend() -> str:
    """Auto-detect available backend by priority (cache-first strategy).

    This function first attempts to use cached hardware profiles for fast detection.
    If no cache exists or all cached profiles are unusable, it falls back to
    quick runtime detection (L2 Framework layer only).

    Priority order (higher = preferred):
      100 - Ascend NPU (华为昇腾)
      90  - Kunlun XPU (百度昆仑)
      85  - Haiguang DCU (海光)
      80  - Muxi (沐曦)
      50  - NVIDIA CUDA
      10  - CPU (fallback)

    Returns the canonical backend kind string (e.g., 'cuda', 'ascend').
    """
    console = get_console()

    from sagellm_backend.hardware import ProfileCache

    cache = ProfileCache()
    profiles = cache.load()

    if not profiles:
        try:
            _auto_refresh_profiles_once()
            profiles = cache.load()
        except Exception as e:
            raise RuntimeError(
                "Failed to auto-refresh hardware profiles. "
                "Run 'sagellm profile --refresh' to diagnose and fix the environment."
            ) from e

    if profiles:
        # Check cached profiles by priority
        priority_order = [
            ("ascend", "ascend", 100),
            ("kunlun", "kunlun", 90),
            ("haiguang", "haiguang", 85),
            ("muxi", "muxi", 80),
            ("cuda", "cuda", 50),
            ("cpu", "cpu", 10),
        ]

        for hw_type, internal_kind, _priority in priority_order:
            profile = profiles.get(hw_type)
            if profile and profile.is_usable():
                logger.debug(
                    "Using cached profile for %s (max_level=%s)",
                    hw_type,
                    profile.max_level.value,
                )
                return internal_kind

    # No usable cached profiles - fall back to quick runtime detection
    console.print("[yellow]⚠️  No hardware profile found. Running quick detection...[/yellow]")
    console.print("[dim]Tip: Run 'sagellm profile --refresh' for accurate cached results.[/dim]\n")

    # Import hardware check functions
    from ..hardware import (
        check_ascend_available,
        check_cuda_available,
        check_haiguang_available,
        check_kunlun_available,
        check_muxi_available,
    )

    detected_backends: list[tuple[int, str, str]] = []  # (priority, backend_key, backend_kind)

    # Check Ascend (priority 100)
    ok, _ = check_ascend_available()
    if ok:
        detected_backends.append((100, "ascend", "ascend"))

    # Check Kunlun (priority 90)
    ok, _ = check_kunlun_available()
    if ok:
        detected_backends.append((90, "kunlun", "kunlun"))

    # Check Haiguang DCU (priority 85)
    ok, _ = check_haiguang_available()
    if ok:
        detected_backends.append((85, "haiguang", "haiguang"))

    # Check Muxi (priority 80)
    ok, _ = check_muxi_available()
    if ok:
        detected_backends.append((80, "muxi", "muxi"))

    # Check NVIDIA CUDA (priority 50)
    ok, _ = check_cuda_available()
    if ok:
        detected_backends.append((50, "cuda", "cuda"))

    # CPU always available (priority 10)
    detected_backends.append((10, "cpu", "cpu"))

    # Sort by priority (descending) and return the highest
    detected_backends.sort(key=lambda x: x[0], reverse=True)
    best_backend = detected_backends[0]

    return best_backend[2]  # Return internal kind


def ensure_cuda_pytorch() -> None:
    """Check if PyTorch supports CUDA, prompt user to install if not.

    This provides an Ollama-like experience where users get clear instructions
    on how to set up their environment.
    """
    try:
        import torch

        if torch.cuda.is_available():
            return  # CUDA works, nothing to do

        # CUDA not available - check if it's CPU-only PyTorch
        torch_version = torch.__version__
        is_cpu_only = "+cpu" in torch_version or "cpu" in torch_version.lower()

        if not is_cpu_only:
            # Has CUDA PyTorch but no GPU detected
            console = get_console()
            console.print("\n[yellow]⚠️  PyTorch CUDA is installed but no GPU detected.[/yellow]")
            console.print("    Please check your NVIDIA drivers and CUDA installation.")
            console.print("    Falling back to CPU backend.\n")
            raise RuntimeError("CUDA not available. Use --backend=cpu or fix GPU setup.")

    except ImportError:
        torch_version = "not installed"
        is_cpu_only = True

    # CPU-only PyTorch detected, prompt user to install CUDA version
    console = get_console()
    console.print("\n[yellow]⚠️  当前 PyTorch 不支持 CUDA[/yellow]")
    console.print(f"    当前版本: torch {torch_version}")
    console.print()
    console.print("[bold]请先安装 CUDA 版本的 PyTorch：[/bold]")
    console.print()
    console.print("    [cyan]sagellm install cuda[/cyan]       # CUDA 12.1 (推荐)")
    console.print("    [cyan]sagellm install cuda11[/cyan]     # CUDA 11.8 (旧驱动)")
    console.print()
    console.print('[dim]或使用 CPU 运行：sagellm run -p "Hello!" --backend=cpu[/dim]\n')

    # 优雅退出，不显示 traceback
    raise SystemExit(1)


def resolve_backend_and_engine(backend: str | None) -> tuple[str, str]:
    """Resolve backend/engine kinds.

    Returns:
        Tuple of (backend_kind, engine_kind) used by sagellm-core plugin factories.
    """
    if backend is None:
        backend_kind = detect_backend()
    else:
        backend_kind = backend

    # Check if user requested CUDA but PyTorch doesn't support it
    if backend_kind == "cuda":
        ensure_cuda_pytorch()

    if backend_kind == "cpu":
        return "cpu", "cpu"
    if backend_kind == "cuda":
        return "cuda", "hf-cuda"
    if backend_kind == "ascend":
        return "ascend", "ascend"
    if backend_kind == "kunlun":
        return "kunlun", "kunlun"
    if backend_kind == "haiguang":
        return "haiguang", "haiguang"
    if backend_kind == "muxi":
        return "muxi", "muxi"

    raise RuntimeError(
        f"Unknown backend kind: '{backend_kind}'. "
        "Expected: cpu, cuda, ascend, kunlun, haiguang, muxi."
    )


def default_device_for_backend(backend_kind: str) -> str | None:
    """Return an explicit default device string for a backend kind."""
    if backend_kind == "cpu":
        return "cpu"
    if backend_kind == "cuda":
        return "cuda:0"
    if backend_kind == "ascend":
        return "ascend:0"
    if backend_kind == "kunlun":
        return "kunlun:0"
    if backend_kind == "haiguang":
        return "haiguang:0"
    if backend_kind == "muxi":
        return "muxi:0"
    return None


def detect_cuda() -> str:
    """Detect CUDA availability."""
    try:
        import torch

        if torch.cuda.is_available():
            device_count = torch.cuda.device_count()
            device_name = torch.cuda.get_device_name(0)
            return f"✅ {device_count} device(s) - {device_name}"
        else:
            return "❌ torch installed, no GPU"
    except ImportError:
        return "❌ torch not installed"
    except Exception as e:
        return f"❌ Error: {e}"


def detect_ascend() -> str:
    """Detect Ascend NPU availability."""
    try:
        import torch_npu

        if torch_npu.npu.is_available():
            device_count = torch_npu.npu.device_count()
            return f"✅ {device_count} NPU(s)"
        else:
            return "❌ torch_npu installed, no NPU"
    except ImportError:
        return "❌ torch_npu not installed"
    except Exception as e:
        return f"❌ Error: {e}"
