"""
Merlya Commands - Model management handlers.

Implements /model command with subcommands: show, provider, model, test, router.
"""

from __future__ import annotations

import time
from pathlib import Path
from typing import TYPE_CHECKING

from merlya.commands.registry import CommandResult, command, subcommand

if TYPE_CHECKING:
    from merlya.core.context import SharedContext


@command("model", "Manage LLM provider and router", "/model <subcommand>")
async def cmd_model(ctx: SharedContext, args: list[str]) -> CommandResult:
    """Manage LLM provider and model configuration."""
    if not args:
        return await cmd_model_show(ctx, [])

    return CommandResult(
        success=False,
        message="Unknown subcommand. Use `/help model` for available commands.",
        show_help=True,
    )


@subcommand("model", "show", "Show current model config", "/model show")
async def cmd_model_show(ctx: SharedContext, _args: list[str]) -> CommandResult:
    """Show current model configuration."""
    config = ctx.config.model
    router_config = ctx.config.router
    key_env = config.api_key_env or f"{config.provider.upper()}_API_KEY"
    has_key = key_env and ctx.secrets.has(key_env)

    lines = [
        "**Model Configuration**\n",
        "**LLM Provider:**",
        f"  - Provider: `{config.provider}`",
        f"  - Model: `{config.model}`",
        f"  - API Key: `{'configured' if has_key else 'not set'}` ({key_env})",
        "",
        "**Router:**",
        f"  - Type: `{router_config.type}`",
        f"  - Tier: `{router_config.tier or 'auto'}`",
        f"  - LLM Fallback: `{router_config.llm_fallback or 'none'}`",
    ]

    return CommandResult(success=True, message="\n".join(lines))


@subcommand("model", "provider", "Change LLM provider", "/model provider <name>")
async def cmd_model_provider(ctx: SharedContext, args: list[str]) -> CommandResult:
    """Change the LLM provider."""
    providers = ["openrouter", "anthropic", "openai", "ollama", "litellm"]
    default_models = {
        "openrouter": "amazon/nova-2-lite-v1:free",
        "anthropic": "claude-3-5-sonnet-20241022",
        "openai": "gpt-4o",
        "ollama": "llama3.2",
        "litellm": "gpt-4o",
    }
    router_fallbacks = {
        "openrouter": "openrouter:openrouter/auto",
        "anthropic": "anthropic:claude-3-haiku-20240307",
        "openai": "openai:gpt-4o-mini",
        "litellm": "litellm:gpt-4o-mini",
        "ollama": "ollama:llama3.2",
    }

    if not args:
        return CommandResult(
            success=False,
            message=f"Usage: `/model provider <name>`\n\nAvailable: {', '.join(providers)}",
        )

    provider = args[0].lower()
    if provider not in providers:
        return CommandResult(
            success=False,
            message=f"Unknown provider: `{provider}`\nAvailable: {', '.join(providers)}",
        )

    api_key_envs = {
        "openrouter": "OPENROUTER_API_KEY",
        "anthropic": "ANTHROPIC_API_KEY",
        "openai": "OPENAI_API_KEY",
        "litellm": "LITELLM_API_KEY",
        "ollama": None,
    }

    api_key_env = api_key_envs.get(provider)

    if api_key_env and not ctx.secrets.has(api_key_env):
        api_key = await ctx.ui.prompt_secret(f"🔑 Enter {api_key_env}")
        if api_key:
            ctx.secrets.set(api_key_env, api_key)
            ctx.ui.success("✅ API key saved to keyring")
        else:
            return CommandResult(success=False, message="API key required for this provider.")

    ctx.config.model.provider = provider
    if provider in default_models:
        ctx.config.model.model = default_models[provider]
    if provider in router_fallbacks:
        ctx.config.router.llm_fallback = router_fallbacks[provider]
    ctx.config.model.api_key_env = api_key_env or ""
    ctx.config.save()

    return CommandResult(
        success=True,
        message=f"✅ Provider changed to `{provider}`",
        data={"reload_agent": True},
    )


@subcommand("model", "model", "Change LLM model", "/model model <name>")
async def cmd_model_model(ctx: SharedContext, args: list[str]) -> CommandResult:
    """Change the LLM model."""
    if not args:
        default_models = {
            "openrouter": "amazon/nova-2-lite-v1:free",
            "anthropic": "claude-3-5-sonnet-20241022",
            "openai": "gpt-4o",
            "ollama": "llama3.2",
            "litellm": "gpt-4o",
        }
        suggestions = default_models.get(ctx.config.model.provider, "")
        return CommandResult(
            success=False,
            message=f"Usage: `/model model <name>`\n\nSuggested for {ctx.config.model.provider}: `{suggestions}`",
        )

    model = args[0]
    ctx.config.model.model = model
    ctx.config.save()

    return CommandResult(
        success=True,
        message=f"✅ Model changed to `{model}`",
        data={"reload_agent": True},
    )


@subcommand("model", "test", "Test LLM connection", "/model test")
async def cmd_model_test(ctx: SharedContext, _args: list[str]) -> CommandResult:
    """Test the LLM provider connection."""
    import os

    provider = ctx.config.model.provider
    key_env = ctx.config.model.api_key_env or f"{provider.upper()}_API_KEY"

    # Ensure API key is available for the test
    if not os.getenv(key_env):
        secret_value = ctx.secrets.get(key_env) if hasattr(ctx.secrets, "get") else None
        if secret_value:
            os.environ[key_env] = secret_value

    ctx.ui.info(f"🔍 Testing connection to {provider}...")

    try:
        from pydantic_ai import Agent

        primary_model = f"{provider}:{ctx.config.model.model}"

        def _normalize(model_name: str) -> str:
            return model_name if ":" in model_name else f"{provider}:{model_name}"

        candidates: list[str] = [primary_model]
        if provider == "openrouter":
            if ctx.config.router.llm_fallback:
                candidates.append(_normalize(ctx.config.router.llm_fallback))
            # Broadly available fallbacks (free + auto router)
            candidates.extend(
                [
                    "openrouter:amazon/nova-2-lite-v1:free",
                    "openrouter:openrouter/auto",
                ]
            )

        # Remove duplicates while preserving order
        seen: set[str] = set()
        unique_candidates: list[str] = []
        for m in candidates:
            if m not in seen:
                seen.add(m)
                unique_candidates.append(m)
        candidates = unique_candidates

        errors: list[tuple[str, str]] = []

        with ctx.ui.spinner(f"Testing {len(candidates)} model option(s)..."):
            for model_path in candidates:
                start = time.time()
                try:
                    agent = Agent(
                        model_path,
                        system_prompt="Reply with exactly: OK",
                    )
                    result = await agent.run("Test")
                    elapsed = time.time() - start

                    # Support both legacy result.data and newer attributes
                    raw_data = getattr(result, "data", None)
                    if raw_data is None and hasattr(result, "output"):
                        raw_data = result.output
                    response_text = str(raw_data)

                    if "ok" in response_text.lower():
                        fallback_note = ""
                        if model_path != primary_model:
                            fallback_note = f"\n  - Fallback used: `{model_path}`"
                        return CommandResult(
                            success=True,
                            message=f"✅ LLM connection OK\n"
                            f"  - Provider: `{provider}`\n"
                            f"  - Model: `{model_path}`\n"
                            f"  - Latency: `{elapsed:.2f}s`"
                            f"{fallback_note}",
                        )

                    errors.append((model_path, f"Unexpected response: {response_text}"))
                except Exception as e:
                    errors.append((model_path, str(e)))

        # If we reach here, all attempts failed
        error_lines = [f"  - {m}: {err}" for m, err in errors[:2]]
        if len(errors) > 2:
            error_lines.append(f"  - ... and {len(errors) - 2} more")

        return CommandResult(
            success=False,
            message="❌ LLM connection failed\n"
            + "\n".join(error_lines)
            + "\n\nCheck your API key with `/secret list` and provider with `/model show`",
        )

    except Exception as e:
        return CommandResult(
            success=False,
            message=f"❌ LLM connection failed\n"
            f"  - Error: `{e}`\n\n"
            "Check your API key with `/secret list` and provider with `/model show`",
        )


@subcommand("model", "router", "Configure intent router", "/model router <show|local|llm>")
async def cmd_model_router(ctx: SharedContext, args: list[str]) -> CommandResult:
    """Configure the intent router."""
    if not args:
        return CommandResult(
            success=False,
            message="Usage:\n"
            "  `/model router show` - Show router config\n"
            "  `/model router local` - Use local ONNX model\n"
            "  `/model router llm <model>` - Use LLM for routing",
        )

    action = args[0].lower()

    if action == "show":
        return _show_router_config(ctx)
    elif action == "local":
        return _set_local_router(ctx)
    elif action == "llm":
        return _set_llm_router(ctx, args)
    else:
        return CommandResult(success=False, message=f"Unknown router action: `{action}`")


def _show_router_config(ctx: SharedContext) -> CommandResult:
    """Show router configuration."""
    router_config = ctx.config.router
    lines = [
        "**Router Configuration**\n",
        f"  - Type: `{router_config.type}`",
        f"  - Model: `{router_config.model or 'default'}`",
        f"  - Tier: `{router_config.tier or 'auto'}`",
        f"  - LLM Fallback: `{router_config.llm_fallback or 'none'}`",
    ]

    model_path = Path.home() / ".merlya" / "models" / "router.onnx"
    if model_path.exists():
        size_mb = model_path.stat().st_size / (1024 * 1024)
        lines.append(f"\n  ✅ ONNX model loaded ({size_mb:.1f}MB)")
    else:
        lines.append("\n  ⚠️ ONNX model not found (using pattern matching)")

    return CommandResult(success=True, message="\n".join(lines))


def _set_local_router(ctx: SharedContext) -> CommandResult:
    """Set router to local ONNX model."""
    ctx.config.router.type = "local"
    ctx.config.save()
    return CommandResult(success=True, message="✅ Router set to local (ONNX embedding model)")


def _set_llm_router(ctx: SharedContext, args: list[str]) -> CommandResult:
    """Set router to use LLM."""
    if len(args) < 2:
        return CommandResult(
            success=False,
            message="Usage: `/model router llm <model>`\nExample: `/model router llm gpt-4o-mini`",
        )
    llm_model = args[1]
    if ":" not in llm_model:
        llm_model = f"{ctx.config.model.provider}:{llm_model}"
    ctx.config.router.type = "llm"
    ctx.config.router.llm_fallback = llm_model
    ctx.config.save()
    return CommandResult(success=True, message=f"✅ Router set to LLM ({llm_model})")
