"""Model commands - Manage models on LiteLLM Proxy."""

from typing import Optional

import typer

from llm_cli.core.client import APIError, AuthenticationError, ConnectionError, LiteLLMClient
from llm_cli.core.context import ConfigurationError
from llm_cli.ui import confirm, error, fuzzy_select, success, text_input, warning
from llm_cli.ui.console import console, print_detail
from llm_cli.ui.tables import print_models_table, print_proxy_models_table

app = typer.Typer(no_args_is_help=True)


def _get_client(org: str | None, env: str | None) -> LiteLLMClient:
    """Get API client with error handling."""
    try:
        return LiteLLMClient(org_override=org, env_override=env)
    except ConfigurationError as e:
        error(str(e))
        raise typer.Exit(2)


@app.command("list")
def list_models(
    org: Optional[str] = typer.Option(None, "--org", "-o", help="Override organization"),
    env: Optional[str] = typer.Option(None, "--env", "-e", help="Override environment"),
) -> None:
    """List all models on the proxy."""
    client = _get_client(org, env)
    context_name = f"{client.context.organization_id}/{client.context.environment}"

    try:
        models = client.list_models()
        print_proxy_models_table(models, context_name)
    except ConnectionError:
        error("Cannot connect to LiteLLM Proxy")
        console.print(f"  URL: {client.base_url}", style="dim")
        console.print("\n  Please check:", style="dim")
        console.print("  - Is the proxy server running?", style="dim")
        console.print("  - Is the URL correct?", style="dim")
        console.print("\n  Run 'llm config current' to see current configuration", style="dim")
        raise typer.Exit(3)
    except AuthenticationError:
        error("Authentication failed")
        console.print("  Master key may be invalid or expired", style="dim")
        console.print("\n  Run 'llm init' to update credentials", style="dim")
        raise typer.Exit(4)
    except APIError as e:
        error(f"API Error: {e.message}")
        raise typer.Exit(1)


@app.command("create")
def create_model(
    provider_name: Optional[str] = typer.Option(None, "--provider", "-p", help="Provider name"),
    model_id: Optional[str] = typer.Option(None, "--model", "-m", help="Model ID"),
    alias: Optional[str] = typer.Option(None, "--alias", "-a", help="Model alias/display name"),
    api_key: Optional[str] = typer.Option(None, "--api-key", "-k", help="API key"),
    org: Optional[str] = typer.Option(None, "--org", "-o", help="Override organization"),
    env: Optional[str] = typer.Option(None, "--env", "-e", help="Override environment"),
) -> None:
    """Create a new model on the proxy."""
    # If all required params provided, run non-interactively
    if provider_name and model_id and alias:
        _create_model_non_interactive(provider_name, model_id, alias, api_key, org, env)
    else:
        create_model_interactive(
            prefill_provider=provider_name,
            prefill_model=model_id,
            prefill_alias=alias,
            prefill_api_key=api_key,
            org_override=org,
            env_override=env,
        )


def create_model_interactive(
    prefill_provider: str | None = None,
    prefill_model: str | None = None,
    prefill_alias: str | None = None,
    prefill_api_key: str | None = None,
    org_override: str | None = None,
    env_override: str | None = None,
) -> None:
    """Interactive model creation flow."""
    client = _get_client(org_override, env_override)

    # Fetch all supported models from LiteLLM
    try:
        all_providers = client.list_supported_models()
    except Exception:
        all_providers = []

    provider_ids = [p.id for p in all_providers]

    # Select provider
    if prefill_provider:
        provider = next((p for p in all_providers if p.id == prefill_provider), None)
        if not provider:
            error(f"Provider '{prefill_provider}' not found")
            raise typer.Exit(5)
    else:
        if not provider_ids:
            error("Could not fetch provider list")
            raise typer.Exit(1)

        console.print("[dim]Type to search providers (tab to complete):[/dim]")
        selection = fuzzy_select("Provider:", provider_ids)
        if selection is None or selection not in provider_ids:
            raise typer.Exit(1)
        provider = next((p for p in all_providers if p.id == selection), None)
        if not provider:
            raise typer.Exit(1)

    # Select model - show table then fuzzy select
    if prefill_model:
        model_id = prefill_model
    else:
        model_ids = [m.id for m in provider.models]
        if not model_ids:
            model_id = text_input("Enter model ID:")
            if not model_id:
                error("Model ID is required")
                raise typer.Exit(5)
        else:
            # Show available models table
            print_models_table(provider.models, title=f"{provider.name} - Available Models")

            # Fuzzy select
            console.print("\n[dim]Type to search models (tab to complete):[/dim]")
            model_selection = fuzzy_select("Model:", model_ids)
            if model_selection is None or model_selection not in model_ids:
                raise typer.Exit(1)
            model_id = model_selection

    # Build the full model string with provider prefix
    if "/" not in model_id:
        full_model_id = f"{provider.id}/{model_id}"
    else:
        full_model_id = model_id

    # Get alias
    if prefill_alias:
        alias = prefill_alias
    else:
        default_alias = model_id.split("/")[-1].split(":")[0]
        alias = text_input("Model alias (display name):", default=default_alias)
        if not alias:
            alias = default_alias

    # Get API key with retry loop for test validation
    if prefill_api_key:
        api_key = prefill_api_key
    elif provider.requires_api_key:
        env_hint = f" (or press Enter to use {provider.env_var})" if provider.env_var else ""
        api_key = text_input(f"API Key{env_hint}:", password=True)
    else:
        api_key = None

    # Test and create with retry loop
    max_retries = 3
    for attempt in range(max_retries):
        litellm_params = {"model": full_model_id}
        if api_key:
            litellm_params["api_key"] = api_key

        # Test model before creating
        console.print("\n[dim]Testing model connection...[/dim]")
        with console.status("[bold cyan]Sending test request..."):
            test_ok, test_message = client.test_model_completion(alias, litellm_params)

        if test_ok:
            success(f"Test passed: {test_message}")
            console.print()
            break
        else:
            error(f"Test failed: {test_message}")

            if attempt < max_retries - 1:
                console.print()
                retry_choices = [
                    "Re-enter API key",
                    "Re-enter model ID",
                    "Skip test and create anyway",
                    "Cancel",
                ]
                from llm_cli.ui import select_from_list
                retry_action = select_from_list("What would you like to do?", retry_choices)

                if retry_action is None or "Cancel" in retry_action:
                    raise typer.Exit(1)
                elif "Re-enter API key" in retry_action:
                    api_key = text_input("API Key:", password=True)
                    continue
                elif "Re-enter model ID" in retry_action:
                    new_model = text_input("Model ID:", default=full_model_id)
                    if new_model:
                        full_model_id = new_model
                    continue
                elif "Skip test" in retry_action:
                    warning("Skipping test - model may not work correctly")
                    console.print()
                    break
            else:
                # Last attempt failed
                if not confirm("All test attempts failed. Create model anyway?", default=False):
                    raise typer.Exit(1)
                warning("Creating model without successful test")
                console.print()

    # Create model
    try:
        client.create_model(
            model_name=alias,
            litellm_params=litellm_params,
        )
        success(f"Model '{alias}' created successfully")
        print_detail("Provider", provider.id)
        print_detail("Model", full_model_id)
    except ConnectionError:
        error("Cannot connect to LiteLLM Proxy")
        raise typer.Exit(3)
    except AuthenticationError:
        error("Authentication failed")
        raise typer.Exit(4)
    except APIError as e:
        error(f"Failed to create model: {e.message}")
        raise typer.Exit(1)


def _create_model_non_interactive(
    provider_name: str,
    model_id: str,
    alias: str,
    api_key: str | None,
    org: str | None,
    env: str | None,
) -> None:
    """Non-interactive model creation with retry for missing/bad API key."""
    from llm_cli.ui import select_from_list, text_input

    client = _get_client(org, env)

    litellm_params = {"model": model_id}
    if api_key:
        litellm_params["api_key"] = api_key
    else:
        key = text_input("API key:", password=True)
        if key:
            litellm_params["api_key"] = key

    # Test model with retry loop
    max_retries = 3
    for attempt in range(max_retries):
        console.print("[dim]Testing model connection...[/dim]")
        with console.status("[bold cyan]Sending test request..."):
            test_ok, test_message = client.test_model_completion(alias, litellm_params)

        if test_ok:
            success(f"Test passed: {test_message}")
            break
        else:
            error(f"Test failed: {test_message}")
            if attempt < max_retries - 1:
                retry_choices = [
                    "Enter API key",
                    "Skip test and create anyway",
                    "Cancel",
                ]
                retry_action = select_from_list("What would you like to do?", retry_choices)

                if retry_action and "API key" in retry_action:
                    new_key = text_input("API key:", password=True)
                    if new_key:
                        litellm_params["api_key"] = new_key
                elif retry_action and "Skip" in retry_action:
                    warning("Skipping test, creating model anyway")
                    break
                else:
                    raise typer.Exit(6)
            else:
                error("Max retries reached")
                raise typer.Exit(6)

    try:
        client.create_model(
            model_name=alias,
            litellm_params=litellm_params,
        )
        success(f"Model '{alias}' created successfully")
    except ConnectionError:
        error("Cannot connect to LiteLLM Proxy")
        raise typer.Exit(3)
    except AuthenticationError:
        error("Authentication failed")
        raise typer.Exit(4)
    except APIError as e:
        error(f"Failed to create model: {e.message}")
        raise typer.Exit(1)


@app.command("delete")
def delete_model(
    model_name: Optional[str] = typer.Argument(None, help="Model name to delete"),
    yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
    org: Optional[str] = typer.Option(None, "--org", "-o", help="Override organization"),
    env: Optional[str] = typer.Option(None, "--env", "-e", help="Override environment"),
) -> None:
    """Delete a model from the proxy."""
    client = _get_client(org, env)

    try:
        models = client.list_models()
    except ConnectionError:
        error("Cannot connect to LiteLLM Proxy")
        raise typer.Exit(3)
    except AuthenticationError:
        error("Authentication failed")
        raise typer.Exit(4)
    except APIError as e:
        error(f"API Error: {e.message}")
        raise typer.Exit(1)

    if not models:
        error("No models found on proxy")
        raise typer.Exit(1)

    # Select model if not provided
    if not model_name:
        # Show deployed models table first
        context_name = f"{client.context.organization_id}/{client.context.environment}"
        print_proxy_models_table(models, context_name)

        # Fuzzy select to pick one
        model_choices = [m.get("model_name", "") for m in models if m.get("model_name")]
        console.print("\n[dim]Type to search models (tab to complete):[/dim]")
        selection = fuzzy_select("Delete model:", model_choices)
        if selection is None or selection not in model_choices:
            raise typer.Exit(1)
        model_name = selection

    # Find model by name
    model_id = None
    for m in models:
        if m.get("model_name") == model_name:
            model_id = m.get("model_info", {}).get("id")
            break

    if not model_id:
        error(f"Model '{model_name}' not found")
        raise typer.Exit(5)

    # Confirm deletion
    if not yes:
        if not confirm(f"Are you sure you want to delete '{model_name}'?"):
            raise typer.Exit(1)

    try:
        client.delete_model(model_id)
        success(f"Model '{model_name}' deleted")
    except APIError as e:
        error(f"Failed to delete model: {e.message}")
        raise typer.Exit(1)
