"""LLM provider abstraction using litellm."""

import logging
import os
from dataclasses import dataclass
from typing import Any

import litellm

logger = logging.getLogger(__name__)

# Configure litellm
litellm.set_verbose = False


@dataclass
class LLMConfig:
    """LLM configuration."""

    model: str
    temperature: float = 0.7
    max_tokens: int = 4096
    api_key: str | None = None
    api_base: str | None = None


# Preset configurations for common providers
PRESETS: dict[str, LLMConfig] = {
    "gemini": LLMConfig(
        model="gemini/gemini-1.5-flash",
        temperature=0.7,
        max_tokens=8192,
    ),
    "gemini-pro": LLMConfig(
        model="gemini/gemini-1.5-pro",
        temperature=0.7,
        max_tokens=8192,
    ),
    "claude": LLMConfig(
        model="anthropic/claude-3-haiku-20240307",
        temperature=0.7,
        max_tokens=4096,
    ),
    "claude-sonnet": LLMConfig(
        model="anthropic/claude-3-5-sonnet-20241022",
        temperature=0.7,
        max_tokens=8192,
    ),
    "deepseek": LLMConfig(
        model="deepseek/deepseek-chat",
        temperature=0.7,
        max_tokens=4096,
    ),
    "deepseek-coder": LLMConfig(
        model="deepseek/deepseek-coder",
        temperature=0.3,
        max_tokens=8192,
    ),
    "gpt-4o-mini": LLMConfig(
        model="gpt-4o-mini",
        temperature=0.7,
        max_tokens=4096,
    ),
    "gpt-4o": LLMConfig(
        model="gpt-4o",
        temperature=0.7,
        max_tokens=4096,
    ),
}


class LLMProvider:
    """Unified LLM provider interface."""

    def __init__(
        self,
        config: LLMConfig | str | None = None,
    ):
        """Initialize LLM provider.

        Args:
            config: LLMConfig, preset name, or None for auto-detect
        """
        if isinstance(config, str):
            if config in PRESETS:
                self.config = PRESETS[config]
            else:
                # Assume it's a model name
                self.config = LLMConfig(model=config)
        elif config is None:
            self.config = self._auto_detect_provider()
        else:
            self.config = config

    def _auto_detect_provider(self) -> LLMConfig:
        """Auto-detect available provider from environment."""
        # Check for API keys in order of preference
        if os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY"):
            logger.info("Auto-detected Google Gemini")
            return PRESETS["gemini"]
        elif os.environ.get("ANTHROPIC_API_KEY"):
            logger.info("Auto-detected Anthropic Claude")
            return PRESETS["claude"]
        elif os.environ.get("DEEPSEEK_API_KEY"):
            logger.info("Auto-detected DeepSeek")
            return PRESETS["deepseek"]
        elif os.environ.get("OPENAI_API_KEY"):
            logger.info("Auto-detected OpenAI")
            return PRESETS["gpt-4o-mini"]
        else:
            logger.warning("No API key found, defaulting to Gemini (will fail without key)")
            return PRESETS["gemini"]

    async def complete(
        self,
        messages: list[dict[str, str]],
        **kwargs: Any,
    ) -> str:
        """Get completion from LLM.

        Args:
            messages: List of message dicts with 'role' and 'content'
            **kwargs: Override config options

        Returns:
            Generated text
        """
        response = await litellm.acompletion(
            model=kwargs.get("model", self.config.model),
            messages=messages,
            temperature=kwargs.get("temperature", self.config.temperature),
            max_tokens=kwargs.get("max_tokens", self.config.max_tokens),
            api_key=kwargs.get("api_key", self.config.api_key),
            api_base=kwargs.get("api_base", self.config.api_base),
        )

        return response.choices[0].message.content

    async def generate_code(
        self,
        prompt: str,
        system_prompt: str | None = None,
        examples: list[str] | None = None,
        **kwargs: Any,
    ) -> str:
        """Generate code with optional examples for context.

        Args:
            prompt: User prompt describing what to generate
            system_prompt: Optional system instructions
            examples: Optional list of example code for context
            **kwargs: Override config options

        Returns:
            Generated code
        """
        messages = []

        if system_prompt:
            messages.append({"role": "system", "content": system_prompt})

        # Add examples as context
        if examples:
            example_text = "\n\n---\n\n".join(examples[:3])  # Limit examples
            messages.append(
                {
                    "role": "user",
                    "content": f"Here are some example ManimGL scenes for reference:\n\n{example_text}",
                }
            )
            messages.append(
                {
                    "role": "assistant",
                    "content": "I understand. I'll use these examples as reference for the code style and patterns.",
                }
            )

        messages.append({"role": "user", "content": prompt})

        # Use lower temperature for code generation
        kwargs.setdefault("temperature", 0.3)

        return await self.complete(messages, **kwargs)


# Global provider instance
_provider: LLMProvider | None = None


def get_provider(config: LLMConfig | str | None = None) -> LLMProvider:
    """Get or create global LLM provider.

    Args:
        config: Optional config to initialize with

    Returns:
        LLM provider instance
    """
    global _provider
    if _provider is None or config is not None:
        _provider = LLMProvider(config)
    return _provider
