"""
LLM factory for creating LLM instances from configuration.

Provides a simple way to instantiate LLM providers based on
configuration without hard-coding implementation details.
"""

import os
from typing import List, Optional

from mini_videorag.core.llm import BaseLLM
from mini_videorag.llm.litellm_provider import LiteLLMLLM
import logging

logger = logging.getLogger(__name__)


class LLMFactory:
    """
    Factory for creating LLM instances from configuration.

    Supports environment variable configuration for easy deployment
    across different environments (local, staging, production).

    Examples:
        # From environment variables
        llm = LLMFactory.from_env()

        # From explicit config
        llm = LLMFactory.create(
            provider="litellm",
            model="gpt-4",
            temperature=0.7
        )

        # With fallbacks
        llm = LLMFactory.create(
            provider="litellm",
            model="gpt-4",
            fallbacks=["gpt-3.5-turbo", "claude-3-sonnet"]
        )

    Environment Variables:
        LLM_PROVIDER: Type of LLM ("litellm" is default and recommended)
        LLM_MODEL: Model identifier (e.g., "gpt-4", "claude-3-opus")
        LLM_FALLBACK_MODELS: Comma-separated fallback models
        LLM_TEMPERATURE: Default temperature (0.0-1.0)
        LLM_MAX_TOKENS: Default max tokens

        Provider-specific API keys:
        OPENAI_API_KEY: For OpenAI models
        ANTHROPIC_API_KEY: For Anthropic models
        COHERE_API_KEY: For Cohere models
        (etc. - see LiteLLM docs for full list)
    """

    @staticmethod
    def create(
        provider: str = "litellm",
        model: str = "gpt-3.5-turbo",
        fallbacks: Optional[List[str]] = None,
        temperature: Optional[float] = None,
        max_tokens: Optional[int] = None,
        **kwargs,
    ) -> BaseLLM:
        """
        Create an LLM instance.

        Args:
            provider: Type of LLM provider ("litellm" is the only current option)
            model: Model identifier (e.g., "gpt-4", "claude-3-opus-20240229")
            fallbacks: Optional list of fallback models
            temperature: Default sampling temperature
            max_tokens: Default maximum tokens
            **kwargs: Additional provider-specific parameters

        Returns:
            Configured LLM instance

        Raises:
            ValueError: If provider is not supported
        """
        provider = provider.lower()

        if provider == "litellm":
            return LiteLLMLLM(
                model=model,
                fallbacks=fallbacks,
                temperature=temperature,
                max_tokens=max_tokens,
                **kwargs,
            )
        raise ValueError(f"Unsupported LLM provider: {provider}. " f"Currently supported: litellm")

    @staticmethod
    def from_env() -> BaseLLM:
        """
        Create an LLM instance from environment variables.

        This is the recommended way to configure LLMs in production.
        as it follows 12-factor app principles.

        Environment Variable Examples:
            # OpenAI GPT-4
            LLM_PROVIDER=litellm
            LLM_MODEL=gpt-4
            OPENAI_API_KEY=sk-...

            # Anthropic Claude with fallback
            LLM_PROVIDER=litellm
            LLM_MODEL=claude-3-opus-20240229
            LLM_FALLBACK_MODELS=claude-3-sonnet-20240229,gpt-4
            ANTHROPIC_API_KEY=...

            # Local Ollama (development)
            LLM_PROVIDER=litellm
            LLM_MODEL=ollama/llama3

            # With parameters
            LLM_PROVIDER=litellm
            LLM_MODEL=gpt-4
            LLM_TEMPERATURE=0.7
            LLM_MAX_TOKENS=2000

        Returns:
            Configured LLM instance

        Raises:
            ValueError: If required environment variables are missing
        """
        provider = os.getenv("LLM_PROVIDER", "litellm")
        model = os.getenv("LLM_MODEL")

        if not model:
            raise ValueError(
                "LLM_MODEL environment variable is required. "
                "Examples:\n"
                "  export LLM_MODEL=gpt-4                    # OpenAI\n"
                "  export LLM_MODEL=claude-3-opus-20240229   # Anthropic\n"
                "  export LLM_MODEL=ollama/llama3            # Local Ollama\n"
            )

        fallbacks = None
        fallback_str = os.getenv("LLM_FALLBACK_MODELS")
        if fallback_str:
            fallbacks = [m.strip() for m in fallback_str.split(",")]

        temperature = None
        temperature_str = os.getenv("LLM_TEMPERATURE")
        if temperature_str:
            try:
                temperature = float(temperature_str)
            except ValueError:
                logger.warning(f"Invalid LLM_TEMPERATURE value: {temperature_str}, ignoring")

        max_tokens = None
        max_tokens_str = os.getenv("LLM_MAX_TOKENS")
        if max_tokens_str:
            try:
                max_tokens = int(max_tokens_str)
            except ValueError:
                logger.warning(f"Invalid LLM_MAX_TOKENS value: {max_tokens_str}, ignoring")

        logger.info(
            f"Creating LLM from environment: provider={provider}, "
            f"model={model}, fallbacks={fallbacks}, "
            f"temperature={temperature}, max_tokens={max_tokens}"
        )

        return LLMFactory.create(
            provider=provider,
            model=model,
            fallbacks=fallbacks,
            temperature=temperature,
            max_tokens=max_tokens,
        )
