"""bilge init - Initialize project with interactive setup."""

from __future__ import annotations

import os
from pathlib import Path

import typer
import yaml
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Prompt

from bilgeai.core.config import BilgeConfig
from bilgeai.core.constants import CONFIG_FILE, PROJECT_DIR, PROVIDER_DEFAULTS

console = Console()

PROVIDER_CHOICES = {
    "1": "anthropic",
    "2": "openai",
    "3": "google",
    "4": "local",
}

PROVIDER_LABELS = {
    "anthropic": "Anthropic (Claude)",
    "openai": "OpenAI (GPT-4o)",
    "google": "Google (Gemini)",
    "local": "Local (Ollama / vLLM / LM Studio)",
}

MODEL_SUGGESTIONS: dict[str, list[str]] = {
    "anthropic": [
        "claude-sonnet-4-20250514",
        "claude-opus-4-20250514",
        "claude-haiku-4-5-20251001",
    ],
    "openai": ["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "o3", "o4-mini"],
    "google": ["gemini-2.5-flash", "gemini-2.5-pro", "gemini-2.0-flash"],
}


def init_command(
    path: Path = typer.Argument(Path("."), help="Project directory"),
) -> None:
    """Initialize a new BilgeAI project."""
    project_dir = path / PROJECT_DIR
    config_path = path / CONFIG_FILE

    if config_path.exists():
        overwrite = typer.confirm(f"{CONFIG_FILE} already exists. Overwrite?", default=False)
        if not overwrite:
            console.print("[yellow]Aborted.[/yellow]")
            raise typer.Exit()

    # Create .bilge directory
    project_dir.mkdir(parents=True, exist_ok=True)

    # Interactive setup
    console.print(Panel("[bold blue]BilgeAI Project Setup[/bold blue]", expand=False))

    # 1. Model name
    console.print("[dim]HuggingFace model ID to fine-tune[/dim]")
    console.print(
        "[dim]  e.g. meta-llama/Llama-3.1-8B, mistralai/Mistral-7B-v0.3, Qwen/Qwen2.5-7B[/dim]"
    )
    model_name = typer.prompt("Model name", default="meta-llama/Llama-3.1-8B")

    # 2. Dataset path
    console.print("[dim]Path to your training dataset file or directory[/dim]")
    data_path = typer.prompt("Dataset path", default="./train.jsonl")

    # 3. LLM Provider selection
    console.print()
    console.print("[bold]LLM Provider Selection[/bold]")
    console.print("[dim]BilgeAI uses an LLM to optimize training parameters.[/dim]")
    console.print()
    for key, provider in PROVIDER_CHOICES.items():
        console.print(f"  [cyan]{key}[/cyan]) {PROVIDER_LABELS[provider]}")
    console.print()

    provider_key = Prompt.ask(
        "Select provider",
        choices=list(PROVIDER_CHOICES.keys()),
        default="1",
        console=console,
    )
    provider = PROVIDER_CHOICES[provider_key]

    # 4. Provider-specific configuration
    if provider == "local":
        llm_model, api_key_env, base_url = _setup_local_provider()
    else:
        llm_model, api_key_env, base_url = _setup_cloud_provider(provider)

    # 5. PII scan
    scan_pii = typer.confirm("Scan dataset for PII (emails, phone numbers, etc.)?", default=True)

    # Create config
    config = BilgeConfig(
        model={"name": model_name},
        data={"path": data_path},
        llm={
            "provider": provider,
            "model": llm_model,
            "api_key_env": api_key_env,
            "base_url": base_url,
        },
        privacy={"scan_pii": scan_pii},
    )
    config.save(config_path)

    console.print(f"\n[green]Created {CONFIG_FILE}[/green]")
    console.print(f"[green]Created {PROJECT_DIR}/[/green]")

    # Show generated config
    config_data = config.model_dump(exclude_none=True)
    console.print(f"\n[bold]Generated {CONFIG_FILE}:[/bold]")
    console.print(f"[dim]{yaml.dump(config_data, default_flow_style=False).strip()}[/dim]")

    console.print("\n[bold]Next steps:[/bold]")
    console.print("  1. [cyan]bilge profile[/cyan]   - Detect your hardware capabilities")
    console.print("  2. [cyan]bilge analyze[/cyan]   - Analyze your dataset structure")
    console.print("  3. [cyan]bilge doctor[/cyan]    - Validate everything is ready")
    console.print("  4. [cyan]bilge run[/cyan]       - Generate optimized training script")


def _setup_cloud_provider(provider: str) -> tuple[str, str, None]:
    """Setup cloud provider: model selection + API key input + validation."""
    defaults = PROVIDER_DEFAULTS[provider]
    api_key_env = defaults["api_key_env"]

    # Model selection
    suggestions = MODEL_SUGGESTIONS.get(provider, [])
    if suggestions:
        console.print(f"\n[dim]Available models: {', '.join(suggestions)}[/dim]")
    llm_model = typer.prompt("LLM model", default=defaults["model"])

    # Check if key is already set in environment (real API keys are 20+ chars)
    existing_key = os.environ.get(api_key_env)
    if existing_key and len(existing_key) >= 20:
        masked = existing_key[:8] + "..." + existing_key[-4:]
        console.print(f"\n[dim]{api_key_env} found in environment: {masked}[/dim]")
        use_existing = typer.confirm("Use this API key?", default=True)
        if use_existing:
            _show_validation_result(_validate_api_key(provider, llm_model, api_key_env))
            return llm_model, api_key_env, None

    # Ask user to enter API key
    console.print(f"\n[dim]Enter your {PROVIDER_LABELS.get(provider, provider)} API key.[/dim]")
    api_key = Prompt.ask(
        f"API key (saved to .env as {api_key_env})",
        password=True,
        console=console,
    )

    if api_key and api_key.strip():
        api_key = api_key.strip()
        # Save to .env file
        _save_to_dotenv(api_key_env, api_key)
        # Load into current process for validation
        os.environ[api_key_env] = api_key

        result = _validate_api_key(provider, llm_model, api_key_env)
        if result is True:
            console.print("[green]API key validated and saved to .env[/green]")
        elif result is None:
            console.print(
                "[green]API key saved to .env[/green] "
                "[dim](litellm not installed — skipped validation)[/dim]"
            )
        else:
            console.print(
                "[yellow]API key saved to .env but validation failed. "
                "Check it before running bilge run.[/yellow]"
            )
    else:
        console.print(
            f"[yellow]No key entered. Set {api_key_env} before running bilge run:[/yellow]"
        )
        console.print(f"  [cyan]export {api_key_env}=your-api-key-here[/cyan]")

    return llm_model, api_key_env, None


def _setup_local_provider() -> tuple[str, str, str]:
    """Setup local provider: base_url + model detection."""
    default_url = PROVIDER_DEFAULTS["local"]["base_url"]
    console.print("\n[dim]OpenAI-compatible endpoint URL (Ollama, vLLM, LM Studio)[/dim]")
    base_url = typer.prompt("Base URL", default=default_url)

    # Try to detect Ollama and list models
    llm_model = _detect_ollama_models(base_url)
    if llm_model is None:
        llm_model = typer.prompt("Model name", default="llama3.1")

    console.print(f"\n[green]Local endpoint: {base_url} | Model: {llm_model}[/green]")

    return llm_model, "", base_url


def _detect_ollama_models(base_url: str) -> str | None:
    """Try to detect Ollama and list available models."""
    try:
        import httpx

        # Ollama API endpoint for listing models
        # base_url is like http://localhost:11434/v1, Ollama tags is /api/tags
        ollama_url = base_url.replace("/v1", "").rstrip("/")
        response = httpx.get(f"{ollama_url}/api/tags", timeout=5.0)

        if response.status_code == 200:
            data = response.json()
            models = [m["name"] for m in data.get("models", [])]
            if models:
                console.print("\n[bold]Ollama detected! Available models:[/bold]")
                for i, name in enumerate(models[:10], 1):
                    console.print(f"  [cyan]{i}[/cyan]) {name}")

                choice = typer.prompt(
                    "Select model (number or name)",
                    default=models[0],
                )
                # If user typed a number, map to model name
                try:
                    idx = int(choice) - 1
                    if 0 <= idx < len(models):
                        return models[idx]
                except ValueError:
                    pass
                return choice
            else:
                console.print("[yellow]Ollama detected but no models installed.[/yellow]")
                console.print("[dim]Install a model: ollama pull llama3.1[/dim]")
    except Exception:
        pass

    return None


def _show_validation_result(result: bool | None) -> None:
    """Display validation result with appropriate message."""
    if result is True:
        console.print("[green]API key validated successfully.[/green]")
    elif result is None:
        console.print(
            "[dim]Validation skipped (install litellm for connection test: "
            "pip install litellm)[/dim]"
        )
    else:
        console.print(
            "[yellow]API key validation failed. Check it before running bilge run.[/yellow]"
        )


def _save_to_dotenv(key_name: str, key_value: str) -> None:
    """Save or update an API key in the project .env file."""
    env_path = Path.cwd() / ".env"
    lines: list[str] = []

    if env_path.exists():
        lines = env_path.read_text().splitlines()

    # Update existing key or append
    updated = False
    for i, line in enumerate(lines):
        if line.startswith(f"{key_name}="):
            lines[i] = f"{key_name}={key_value}"
            updated = True
            break

    if not updated:
        lines.append(f"{key_name}={key_value}")

    env_path.write_text("\n".join(lines) + "\n")


def _validate_api_key(provider: str, model: str, api_key_env: str) -> bool | None:
    """Validate API key with a minimal test call.

    Returns True (valid), False (invalid), or None (cannot validate - litellm missing).
    """
    try:
        from bilgeai.llm.router import get_llm_client

        client = get_llm_client(
            provider=provider,
            model=model,
            api_key_env=api_key_env,
        )
        return client.validate_api_key()
    except ImportError:
        return None  # litellm not installed
    except Exception:
        return False
