"""Profile command - Hardware profile management."""

from __future__ import annotations

import logging

import click

from ..utils.console import get_console

logger = logging.getLogger(__name__)


@click.command()
@click.option("--refresh", is_flag=True, help="Force refresh all profiles")
@click.option("--hardware", type=str, help="Check specific hardware (cuda/ascend/cpu)")
def profile(refresh: bool, hardware: str | None) -> None:
    """Show or refresh hardware profiles (cache-driven detection).

    Display cached hardware capabilities or perform fresh detection.
    This command uses the L0-L3 detection framework to provide detailed
    hardware information without repeated expensive detection operations.

    \b
    Examples:
        sage-llm profile                     # Show cached profiles
        sage-llm profile --refresh           # Refresh all profiles
        sage-llm profile --hardware cuda     # Refresh only CUDA
        sage-llm profile --hardware ascend   # Refresh only Ascend
    """
    from sagellm_backend.hardware import ProfileCache

    console = get_console()
    cache = ProfileCache()

    if refresh:
        # Only import inspectors when actually refreshing (avoids loading torch for cache reads)
        from sagellm_backend.hardware.inspectors import get_inspector, list_supported_hardware

        console.print("[cyan]🔄 正在刷新硬件配置...[/cyan]\n")

        hardware_types = [hardware] if hardware else list_supported_hardware()

        for hw_type in hardware_types:
            try:
                console.print(f"[cyan]正在检测 {hw_type}...[/cyan]")
                inspector = get_inspector(hw_type)
                # Don't include functional tests for quick refresh (use L2 only)
                profile = inspector.get_profile(include_functional=False)
                cache.update(hw_type, profile)

                if profile.is_usable():
                    console.print(f"  [green]✅ {hw_type}: {profile.max_level.value}[/green]")
                    if profile.device_count:
                        console.print(f"     设备数量: {profile.device_count}")
                else:
                    console.print(f"  [yellow]⚠️  {hw_type}: {profile.max_level.value}[/yellow]")
                    if profile.l2_framework.suggestions:
                        console.print(f"     建议: {profile.l2_framework.suggestions[0]}")
            except Exception as e:
                console.print(f"  [red]❌ {hw_type}: {e}[/red]")
                logger.debug("Hardware detection failed for %s: %s", hw_type, e, exc_info=True)

        console.print(f"\n[green]✅ 配置已保存至 {cache.cache_path}[/green]")

        # Display software-level environment configuration with connectivity test
        _display_software_env(console, test_connectivity=True)

    else:
        # Display cached profiles (use load_raw to avoid importing HardwareProfile)
        raw_profiles = cache.load_raw()

        if not raw_profiles:
            console.print("[yellow]⚠️  未找到缓存的硬件配置[/yellow]")
            console.print("\n[dim]运行以下命令创建配置：[/dim]")
            console.print("  [cyan]sage-llm profile --refresh[/cyan]")
            console.print("\n[dim]或安装后端时自动生成：[/dim]")
            console.print("  [cyan]sage-llm install cuda[/cyan]")
            console.print("  [cyan]sage-llm install ascend[/cyan]")
            return

        console.print(f"[bold]硬件配置[/bold] (来自 {cache.cache_path})\n")

        from rich.table import Table

        # Display only cached hardware (no need to query all supported types)
        for hw_type, prof_dict in raw_profiles.items():
            # Determine status based on max_level from raw dict
            # max_level is a string value like "framework", "driver", "physical", etc.
            max_level_value = prof_dict.get("max_level", "unavailable")
            is_usable = max_level_value in ("framework", "runtime_env", "functional")

            if is_usable:
                status_icon = "✅"
                status_text = "可用"
            elif max_level_value == "unavailable":
                status_icon = "❌"
                status_text = "不可用"
            else:
                status_icon = "⚠️ "
                status_text = "部分可用"

            # Use friendly names for hardware types
            hw_display_name = {
                "cpu": "CPU",
                "cuda": "NVIDIA",
                "ascend": "ASCEND",
            }.get(hw_type, hw_type.upper())

            # Build device summary from raw dict
            device_count = prof_dict.get("device_count")
            device_names = prof_dict.get("device_names")
            device_summary = ""
            if device_count and device_names:
                unique_names = list(dict.fromkeys(device_names))
                device_summary = f" ({device_count}x {unique_names[0]})"

            console.print(
                f"\n[bold cyan]{hw_display_name}[/bold cyan]: {status_icon} {status_text}{device_summary}"
            )
            console.print()

            # Create unified table matching the detection architecture
            table = Table(
                show_header=True, header_style="bold white on blue", border_style="blue", width=100
            )
            table.add_column("层级", style="bold cyan", width=14, no_wrap=True)
            table.add_column("组件", style="yellow", width=20)
            table.add_column("状态", width=4, justify="center")
            table.add_column("检测结果", style="white", width=52, no_wrap=False)

            # Get L2.5 details for building rows from raw dict
            l25_details = prof_dict.get("l2_5_runtime_env", {}).get("details", {})

            # Helper to get status icon
            def status_icon_for(available: bool, skipped: bool = False) -> str:
                if skipped:
                    return "-"
                return "✅" if available else "❌"

            def is_skipped(layer_result) -> bool:
                if isinstance(layer_result, dict):
                    message = layer_result.get("message", "")
                else:
                    message = layer_result.message
                return "Skipped" in message or "Not checked" in message

            # === L0 硬件层 ===
            l0_raw = prof_dict.get("l0_physical", {})
            l0_available = l0_raw.get("available", False)
            l0_skip = is_skipped(l0_raw)
            if hw_type == "cpu":
                l0_component = "CPU"
                l0_result = l0_raw.get("message", "") if not l0_skip else "—"
            elif hw_type == "ascend":
                l0_component = "NPU (Ascend 910B)"
                l0_result = (
                    f"{device_count} devices via PCI" if l0_available else l0_raw.get("message", "")
                )
            else:  # cuda/nvidia
                l0_component = "GPU (NVIDIA)"
                l0_result = (
                    f"{device_count} devices via PCI" if l0_available else l0_raw.get("message", "")
                )
            table.add_row(
                "L0 硬件", l0_component, status_icon_for(l0_available, l0_skip), l0_result
            )

            # === L1 驱动层 ===
            l1_raw = prof_dict.get("l1_driver", {})
            l1_available = l1_raw.get("available", False)
            l1_skip = is_skipped(l1_raw)
            driver_version = prof_dict.get("driver_version", "")
            if hw_type == "cpu":
                l1_component = "—"
                l1_result = "CPU 无需驱动"
            elif hw_type == "ascend":
                l1_component = "NPU Driver"
                cann_ver = l25_details.get("cann_version", driver_version or "unknown")
                l1_result = f"CANN {cann_ver}" if l1_available else l1_raw.get("message", "")
            else:
                l1_component = "NVIDIA Driver"
                l1_result = (
                    f"Driver {driver_version}"
                    if l1_available and driver_version
                    else l1_raw.get("message", "")
                )
            table.add_row(
                "L1 驱动", l1_component, status_icon_for(l1_available, l1_skip), l1_result
            )

            # === L1.5 管理工具 ===
            l15_raw = prof_dict.get("l1_5_management_tools", {})
            l15_available = l15_raw.get("available", False) if l15_raw else False
            l15_skip = is_skipped(l15_raw) if l15_raw else True
            if hw_type == "cpu":
                l15_component = "—"
                l15_result = "不适用"
                l15_available = False
            elif hw_type == "ascend":
                l15_component = "npu-smi"
                l15_result = (
                    "NPU health OK"
                    if l15_available
                    else l15_raw.get("message", "未检测")
                    if l15_raw
                    else "未检测"
                )
            else:
                l15_component = "nvidia-smi"
                l15_result = (
                    "可用"
                    if l15_available
                    else l15_raw.get("message", "未检测")
                    if l15_raw
                    else "未检测"
                )
            table.add_row(
                "L1.5 管理", l15_component, status_icon_for(l15_available, l15_skip), l15_result
            )

            # === L2 开发套件 ===
            l2_raw = prof_dict.get("l2_framework", {})
            l2_available = l2_raw.get("available", False)
            l2_skip = is_skipped(l2_raw)
            framework_version = prof_dict.get("framework_version", "")
            if hw_type == "cpu":
                l2_component = "PyTorch"
                l2_result = (
                    f"PyTorch {framework_version}" if l2_available else l2_raw.get("message", "")
                )
            elif hw_type == "ascend":
                l2_component = "CANN + torch_npu"
                l2_result = (
                    f"PyTorch {framework_version}" if l2_available else l2_raw.get("message", "")
                )
            else:
                l2_component = "CUDA Toolkit"
                l2_result = (
                    f"PyTorch {framework_version}" if l2_available else l2_raw.get("message", "")
                )
            table.add_row(
                "L2 套件", l2_component, status_icon_for(l2_available, l2_skip), l2_result
            )

            # === L2.5-A 算子库 ===
            if hw_type == "cpu":
                table.add_row("L2.5-A 算子", "—", "-", "不适用")
            elif hw_type == "ascend":
                acl_lib = l25_details.get("acl_lib_path")
                acl_ok = bool(acl_lib)
                acl_result = "libascendcl.so" if acl_ok else "未找到"
                table.add_row("L2.5-A 算子", "ACL", status_icon_for(acl_ok), acl_result)
            else:  # nvidia
                cudnn_ok = l25_details.get("cudnn_found", False)
                cudnn_ver = l25_details.get("cudnn_version", "")
                cudnn_result = f"cuDNN {cudnn_ver}" if cudnn_ok else "未找到"
                table.add_row("L2.5-A 算子", "cuDNN", status_icon_for(cudnn_ok), cudnn_result)

            # === L2.5-B 通信库 ===
            if hw_type == "cpu":
                table.add_row("L2.5-B 通信", "—", "-", "不适用")
            elif hw_type == "ascend":
                hccl_lib = l25_details.get("hccl_lib_path")
                hccl_ok = bool(hccl_lib)
                hccl_result = "libhccl.so" if hccl_ok else "缺失 (可选)"
                table.add_row("L2.5-B 通信", "HCCL", status_icon_for(hccl_ok), hccl_result)
            else:  # nvidia
                nccl_ok = l25_details.get("nccl_found", False)
                nccl_ver = l25_details.get("nccl_version", "")
                nccl_result = f"NCCL {nccl_ver}" if nccl_ok else "缺失 (可选)"
                table.add_row("L2.5-B 通信", "NCCL", status_icon_for(nccl_ok), nccl_result)

            # === L2.5-C 环境变量 ===
            if hw_type == "cpu":
                table.add_row("L2.5-C 环境", "—", "-", "不适用")
            elif hw_type == "ascend":
                # Check critical Ascend environment variables
                env_issues = []
                env_ok_items = []

                ld_path = l25_details.get("ld_library_path", "")
                if ld_path:
                    env_ok_items.append("LD_LIBRARY_PATH")
                else:
                    env_issues.append("LD_LIBRARY_PATH")

                pythonpath = l25_details.get("pythonpath", "")
                if pythonpath and "torch_npu" in pythonpath:
                    env_ok_items.append("PYTHONPATH")
                else:
                    env_issues.append("PYTHONPATH")

                opp_path = l25_details.get("ascend_opp_path")
                if opp_path:
                    env_ok_items.append("ASCEND_OPP_PATH")
                else:
                    env_issues.append("ASCEND_OPP_PATH(关键)")

                aicpu_path = l25_details.get("ascend_aicpu_path")
                if aicpu_path:
                    env_ok_items.append("ASCEND_AICPU_PATH")
                else:
                    env_issues.append("ASCEND_AICPU_PATH")

                env_all_ok = len(env_issues) == 0
                if env_all_ok:
                    env_result = f"✓ {', '.join(env_ok_items)}"
                else:
                    env_result = f"缺失: {', '.join(env_issues)}"
                table.add_row("L2.5-C 环境", "环境变量", status_icon_for(env_all_ok), env_result)
            else:  # nvidia
                cuda_in_path = l25_details.get("cuda_in_ld_library_path", False)
                cuda_visible = l25_details.get("cuda_visible_devices", "not_set")

                env_issues = []
                if not cuda_in_path:
                    env_issues.append("LD_LIBRARY_PATH缺失(可选)")
                if cuda_visible in ("", "-1"):
                    env_issues.append("GPU已禁用")

                env_all_ok = len(env_issues) == 0 and cuda_in_path
                if env_all_ok:
                    env_result = "LD_LIBRARY_PATH ✓"
                    if cuda_visible != "not_set":
                        env_result += f", CUDA_VISIBLE_DEVICES={cuda_visible}"
                else:
                    env_result = ", ".join(env_issues) if env_issues else "未检测"
                table.add_row("L2.5-C 环境", "环境变量", status_icon_for(env_all_ok), env_result)

            # === L3 框架适配 ===
            l3_raw = prof_dict.get("l3_functional", {})
            l3_skip = is_skipped(l3_raw) if l3_raw else True
            if hw_type == "cpu":
                l3_component = "PyTorch CPU"
                l3_result = "import torch ✓" if l2_available else "—"
                l3_ok = l2_available
            elif hw_type == "ascend":
                l3_component = "torch_npu"
                l3_result = "import torch_npu ✓" if l2_available else "—"
                l3_ok = l2_available
            else:
                l3_component = "torch.cuda"
                l3_result = "torch.cuda.is_available() ✓" if l2_available else "—"
                l3_ok = l2_available
            table.add_row(
                "L3 框架", l3_component, status_icon_for(l3_ok, l3_skip and not l3_ok), l3_result
            )

            console.print(table)

            # Show suggestions if not fully usable
            l25_env = prof_dict.get("l2_5_runtime_env", {})
            suggestions = l25_env.get("suggestions", []) if l25_env else []
            if not is_usable and suggestions:
                console.print()
                console.print("[bold red]🔧 问题修复建议:[/bold red]")
                for suggestion in suggestions[:3]:
                    console.print(f"  [red]•[/red] {suggestion}")

            console.print()

        # Display software-level environment configuration (no connectivity test in show mode)
        _display_software_env(console, test_connectivity=False)

        # Note: Removed missing_types check to avoid importing inspectors
        # Users can always run --refresh to detect additional hardware

        # Footer: update hint and env show hint
        console.print("[dim]运行 'sage-llm profile --refresh' 更新配置[/dim]")
        console.print("[dim]查看环境变量: sage-llm env show[/dim]")


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 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 'sage-llm profile --refresh' for accurate cached results.[/dim]\n")

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

    def _try_import(module: str) -> bool:
        """Return True if a module can be imported."""
        try:
            __import__(module)
            return True
        except ImportError:
            return False

    # Check Ascend (priority 100)
    if _try_import("torch_npu"):
        detected_backends.append((100, "ascend", "ascend"))

    # Check Kunlun (priority 90)
    if _try_import("torch_xmlir"):
        detected_backends.append((90, "kunlun", "kunlun"))

    # Check Haiguang DCU (priority 85)
    if _try_import("torch_dcu"):
        detected_backends.append((85, "haiguang", "haiguang"))

    # Check Muxi (priority 80)
    if _try_import("torch_muxin"):
        detected_backends.append((80, "muxi", "muxi"))

    # Check NVIDIA CUDA (priority 50)
    try:
        import torch

        if torch.cuda.is_available():
            detected_backends.append((50, "cuda", "cuda"))
    except ImportError:
        pass

    # 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]sage-llm install cuda[/cyan]      # CUDA 12.1 (推荐)")
    console.print("    [cyan]sage-llm install cuda11[/cyan]    # CUDA 11.8 (旧驱动)")
    console.print()
    console.print('[dim]或使用 CPU 运行：sage-llm 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"

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


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"
    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}"


def _display_software_env(console, test_connectivity: bool = False) -> None:
    """Display software-level environment configuration.

    Shows network, cache, and logging configuration that affects
    model loading and runtime behavior.

    Args:
        console: Rich console instance
        test_connectivity: If True, test network connectivity (only in --refresh mode)
    """

    from ..utils.env_detector import EnvInitializer

    env_info = EnvInitializer.get_env_info()

    # Get env profile path
    from pathlib import Path

    env_profile_path = Path.home() / ".sagellm" / "env_profile.json"

    console.print("\n")
    console.print(f"[bold]环境配置[/bold] (来自 {env_profile_path})")
    console.print()

    for category, vars_dict in env_info.items():
        console.print(f"[bold cyan]{category}[/bold cyan]:")

        for var_name, var_value in vars_dict.items():
            # Format values for display
            if not var_value or var_value == "Not set":
                display_value = "[yellow]⚠️  未设置[/yellow]"
                status = "⚠️"
            else:
                display_value = f"[green]{var_value}[/green]"
                status = "✅"

            console.print(f"  {status} {var_name}: {display_value}")

        console.print()

    # Only test connectivity in --refresh mode
    if test_connectivity:
        from ..utils.env_detector import NetworkDetector

        hf_endpoint = EnvInitializer.get_env_info()["Network"]["HF_ENDPOINT"]
        if hf_endpoint != "Not set":
            console.print(f"[cyan]🔍 测试网络连接性 ({NetworkDetector.TIMEOUT}s)...[/cyan]")
            is_reachable = NetworkDetector.check_connectivity(
                hf_endpoint, timeout=NetworkDetector.TIMEOUT
            )
            status_icon = "✅" if is_reachable else "⚠️"
            status_text = "可达" if is_reachable else "不可达"
            console.print("[bold cyan]网络连接性[/bold cyan]:")
            console.print(f"  {status_icon} HF_ENDPOINT: {status_text}")
            console.print()


# Gateway command
