"""
Provider Connector - Interactive LLM provider configuration.

Handles API key input, validation, and .env file management
for connecting to various LLM providers.
"""

import os
from getpass import getpass
from pathlib import Path
from typing import Any

from dotenv import load_dotenv
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from groknroll.connect.providers import (
    PROVIDERS,
    ProviderDefinition,
    get_provider,
    get_provider_names,
    list_providers,
)


class ProviderConnector:
    """
    Manages LLM provider connections and credentials.

    Provides interactive prompts for API key entry, validates
    credentials by making test API calls, and saves configuration
    to .env file.

    Example:
        connector = ProviderConnector()

        # Interactive provider selection
        provider = connector.select_provider()

        # Configure specific provider
        success = connector.configure_provider("openai")

        # List configured providers
        configured = connector.get_configured_providers()
    """

    def __init__(
        self,
        env_path: Path | None = None,
        console: Console | None = None,
    ):
        """
        Initialize ProviderConnector.

        Args:
            env_path: Path to .env file (default: current dir)
            console: Rich console for output
        """
        self.env_path = env_path or Path.cwd() / ".env"
        self.console = console or Console()

        # Load .env file if it exists to populate environment
        if self.env_path.exists():
            load_dotenv(self.env_path, override=False)

    def select_provider(self) -> str | None:
        """
        Display interactive provider picker.

        Returns:
            Selected provider name or None if cancelled
        """
        providers = list_providers()

        # Build the picker display
        self.console.print()
        panel_content = []
        for i, provider in enumerate(providers, 1):
            status = self._get_provider_status(provider.name)
            status_icon = "[green]✓[/green]" if status else "[dim]○[/dim]"
            models_str = ", ".join(provider.models[:2])
            if len(provider.models) > 2:
                models_str += ", ..."
            panel_content.append(
                f"  {status_icon} {i}. [cyan]{provider.name:<12}[/cyan] ({models_str})"
            )

        self.console.print(
            Panel(
                "\n".join(panel_content),
                title="Select LLM Provider",
                border_style="blue",
            )
        )

        # Get user selection
        try:
            selection = input("\nEnter number (or 'q' to cancel): ").strip()
            if selection.lower() in ("q", "quit", "cancel", ""):
                return None

            idx = int(selection) - 1
            if 0 <= idx < len(providers):
                return providers[idx].name
            else:
                self.console.print("[red]Invalid selection[/red]")
                return None
        except (ValueError, EOFError, KeyboardInterrupt):
            return None

    def configure_provider(self, provider_name: str) -> bool:
        """
        Configure a specific provider interactively.

        Args:
            provider_name: Name of provider to configure

        Returns:
            True if configuration succeeded
        """
        provider = get_provider(provider_name)
        if not provider:
            self.console.print(f"[red]Unknown provider: {provider_name}[/red]")
            return False

        self.console.print(f"\n[bold]Configuring {provider.display_name}[/bold]")
        self.console.print(f"[dim]{provider.description}[/dim]\n")

        # Collect all required credentials
        credentials: dict[str, str] = {}

        # Main API key
        for env_var in provider.env_vars:
            existing = os.getenv(env_var, "")
            if existing:
                masked = self._mask_key(existing)
                self.console.print(f"[dim]Current {env_var}: {masked}[/dim]")
                overwrite = input("Overwrite? [y/N]: ").strip().lower()
                if overwrite != "y":
                    credentials[env_var] = existing
                    continue

            try:
                value = getpass(f"Enter {env_var}: ")
                if not value:
                    self.console.print("[yellow]Cancelled[/yellow]")
                    return False
                credentials[env_var] = value
            except (EOFError, KeyboardInterrupt):
                self.console.print("\n[yellow]Cancelled[/yellow]")
                return False

        # Extra vars (like Azure endpoint)
        for env_var in provider.extra_vars:
            existing = os.getenv(env_var, "")
            if existing:
                self.console.print(f"[dim]Current {env_var}: {existing}[/dim]")
                overwrite = input("Overwrite? [y/N]: ").strip().lower()
                if overwrite != "y":
                    credentials[env_var] = existing
                    continue

            try:
                value = input(f"Enter {env_var}: ").strip()
                if not value:
                    self.console.print("[yellow]Cancelled[/yellow]")
                    return False
                credentials[env_var] = value
            except (EOFError, KeyboardInterrupt):
                self.console.print("\n[yellow]Cancelled[/yellow]")
                return False

        # Validate credentials
        self.console.print("\n[dim]Testing connection...[/dim]")
        valid, error = self.validate_credentials(provider_name, credentials)

        if not valid:
            self.console.print(f"[red]Connection failed: {error}[/red]")
            save_anyway = input("Save anyway? [y/N]: ").strip().lower()
            if save_anyway != "y":
                return False

        # Save to .env
        for key, value in credentials.items():
            self.save_to_env(key, value)

        # Also set in current environment
        for key, value in credentials.items():
            os.environ[key] = value

        if valid:
            self.console.print(f"[green]✓ Connected to {provider.display_name}[/green]")
        else:
            self.console.print(f"[yellow]⚠ Saved credentials for {provider.display_name}[/yellow]")

        self.console.print(f"[dim]Saved to {self.env_path}[/dim]")
        return True

    def validate_credentials(
        self,
        provider_name: str,
        credentials: dict[str, str],
    ) -> tuple[bool, str]:
        """
        Validate provider credentials by making a test API call.

        Args:
            provider_name: Provider name
            credentials: Dict of env_var -> value

        Returns:
            Tuple of (success, error_message)
        """
        provider = get_provider(provider_name)
        if not provider:
            return False, f"Unknown provider: {provider_name}"

        try:
            if provider_name == "openai":
                return self._validate_openai(credentials, provider)
            elif provider_name == "anthropic":
                return self._validate_anthropic(credentials, provider)
            elif provider_name == "gemini":
                return self._validate_gemini(credentials, provider)
            elif provider_name == "xai":
                return self._validate_xai(credentials, provider)
            elif provider_name == "groq":
                return self._validate_groq(credentials, provider)
            elif provider_name == "openrouter":
                return self._validate_openrouter(credentials, provider)
            elif provider_name == "azure":
                return self._validate_azure(credentials, provider)
            else:
                # For unknown providers, just check key format
                api_key = credentials.get(provider.env_vars[0], "")
                if len(api_key) > 10:
                    return True, ""
                return False, "API key seems too short"
        except Exception as e:
            return False, str(e)

    def _validate_openai(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate OpenAI credentials"""
        try:
            import openai

            api_key = credentials.get("OPENAI_API_KEY", "")
            client = openai.OpenAI(api_key=api_key)
            # Simple models list call to validate
            client.models.list()
            return True, ""
        except ImportError:
            return False, "openai package not installed"
        except Exception as e:
            return False, str(e)

    def _validate_anthropic(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate Anthropic credentials"""
        try:
            import anthropic

            api_key = credentials.get("ANTHROPIC_API_KEY", "")
            client = anthropic.Anthropic(api_key=api_key)
            # Make a minimal request
            client.messages.create(
                model=provider.test_model,
                max_tokens=1,
                messages=[{"role": "user", "content": "hi"}],
            )
            return True, ""
        except ImportError:
            return False, "anthropic package not installed"
        except Exception as e:
            return False, str(e)

    def _validate_gemini(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate Google Gemini credentials"""
        try:
            import google.generativeai as genai

            api_key = credentials.get("GEMINI_API_KEY", "")
            genai.configure(api_key=api_key)
            model = genai.GenerativeModel(provider.test_model)
            model.generate_content("hi", generation_config={"max_output_tokens": 1})
            return True, ""
        except ImportError:
            return False, "google-generativeai package not installed"
        except Exception as e:
            return False, str(e)

    def _validate_xai(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate xAI credentials using OpenAI-compatible API"""
        try:
            import openai

            api_key = credentials.get("XAI_API_KEY", "")
            client = openai.OpenAI(
                api_key=api_key,
                base_url="https://api.x.ai/v1",
            )
            client.chat.completions.create(
                model=provider.test_model,
                messages=[{"role": "user", "content": "hi"}],
                max_tokens=1,
            )
            return True, ""
        except ImportError:
            return False, "openai package not installed"
        except Exception as e:
            return False, str(e)

    def _validate_groq(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate Groq credentials using OpenAI-compatible API"""
        try:
            import openai

            api_key = credentials.get("GROQ_API_KEY", "")
            client = openai.OpenAI(
                api_key=api_key,
                base_url="https://api.groq.com/openai/v1",
            )
            client.chat.completions.create(
                model=provider.test_model,
                messages=[{"role": "user", "content": "hi"}],
                max_tokens=1,
            )
            return True, ""
        except ImportError:
            return False, "openai package not installed"
        except Exception as e:
            return False, str(e)

    def _validate_openrouter(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate OpenRouter credentials"""
        try:
            import openai

            api_key = credentials.get("OPENROUTER_API_KEY", "")
            client = openai.OpenAI(
                api_key=api_key,
                base_url="https://openrouter.ai/api/v1",
            )
            client.chat.completions.create(
                model=provider.test_model,
                messages=[{"role": "user", "content": "hi"}],
                max_tokens=1,
            )
            return True, ""
        except ImportError:
            return False, "openai package not installed"
        except Exception as e:
            return False, str(e)

    def _validate_azure(
        self, credentials: dict[str, str], provider: ProviderDefinition
    ) -> tuple[bool, str]:
        """Validate Azure OpenAI credentials"""
        try:
            import openai

            api_key = credentials.get("AZURE_OPENAI_API_KEY", "")
            endpoint = credentials.get("AZURE_OPENAI_ENDPOINT", "")

            if not endpoint:
                return False, "Azure endpoint required"

            client = openai.AzureOpenAI(
                api_key=api_key,
                azure_endpoint=endpoint,
                api_version="2024-02-15-preview",
            )
            # Try to list deployments
            client.models.list()
            return True, ""
        except ImportError:
            return False, "openai package not installed"
        except Exception as e:
            return False, str(e)

    def save_to_env(self, key: str, value: str) -> None:
        """
        Add or update key in .env file.

        Args:
            key: Environment variable name
            value: Value to set
        """
        lines: list[str] = []
        found = False

        if self.env_path.exists():
            content = self.env_path.read_text()
            for line in content.splitlines():
                # Handle lines with and without values
                if line.startswith(f"{key}="):
                    lines.append(f"{key}={value}")
                    found = True
                else:
                    lines.append(line)

        if not found:
            lines.append(f"{key}={value}")

        # Ensure trailing newline
        self.env_path.write_text("\n".join(lines) + "\n")

    def get_configured_providers(self) -> list[dict[str, Any]]:
        """
        Get list of configured providers with their status.

        Returns:
            List of dicts with provider info and status
        """
        result = []
        for provider in list_providers():
            status = self._get_provider_status(provider.name)
            result.append({
                "name": provider.name,
                "display_name": provider.display_name,
                "description": provider.description,
                "configured": status,
                "models": provider.models,
            })
        return result

    def _get_provider_status(self, provider_name: str) -> bool:
        """Check if provider has credentials configured"""
        provider = get_provider(provider_name)
        if not provider:
            return False

        # Check all required env vars
        for env_var in provider.env_vars:
            if not os.getenv(env_var):
                return False

        # Check extra vars if any
        for env_var in provider.extra_vars:
            if not os.getenv(env_var):
                return False

        return True

    def _mask_key(self, key: str) -> str:
        """Mask API key for display"""
        if len(key) <= 8:
            return "*" * len(key)
        return key[:4] + "*" * (len(key) - 8) + key[-4:]

    def show_providers(self) -> None:
        """Display configured providers in a table"""
        table = Table(title="LLM Providers", show_header=True, header_style="bold")
        table.add_column("Provider", style="cyan")
        table.add_column("Status")
        table.add_column("Description", style="dim")

        for provider in list_providers():
            status = self._get_provider_status(provider.name)
            status_text = "[green]Configured[/green]" if status else "[dim]Not configured[/dim]"
            table.add_row(provider.display_name, status_text, provider.description)

        self.console.print(table)


def connect_provider(provider_name: str | None = None) -> bool:
    """
    Convenience function to connect a provider.

    Args:
        provider_name: Optional provider name. If None, shows picker.

    Returns:
        True if connection succeeded
    """
    connector = ProviderConnector()

    if provider_name:
        return connector.configure_provider(provider_name)
    else:
        selected = connector.select_provider()
        if selected:
            return connector.configure_provider(selected)
        return False


def show_providers() -> None:
    """Convenience function to show configured providers"""
    connector = ProviderConnector()
    connector.show_providers()
