"""LLM Registry and factory for Fluxibly.

Provides auto-detection of LLM provider from model name and
a factory function to create the appropriate LLM implementation.
"""

from __future__ import annotations

from fluxibly.llm.base import BaseLLM, LLMConfig

_llm_registry: dict[str, type[BaseLLM]] = {}


def register_llm(name: str, cls: type[BaseLLM]) -> None:
    """Register an LLM implementation.

    Args:
        name: Provider name (e.g. "openai", "anthropic", "gemini")
        cls: BaseLLM subclass
    """
    _llm_registry[name] = cls


def create_llm(config: LLMConfig) -> BaseLLM:
    """Factory function to create LLM from config.

    Uses ``config.additional_params["framework"]`` if set explicitly,
    otherwise auto-detects from model name.

    Args:
        config: LLMConfig with model name.

    Returns:
        BaseLLM instance for the appropriate provider.

    Raises:
        ValueError: If provider cannot be determined or is not registered.
    """
    framework = config.additional_params.get("framework", "auto")

    if framework != "auto":
        if framework not in _llm_registry:
            available = list(_llm_registry.keys())
            raise ValueError(
                f"Unknown LLM framework: {framework}. Available: {available}"
            )
        return _llm_registry[framework](config)

    # Auto-detect from model name
    model = config.model.lower()

    if any(p in model for p in ("gpt", "o1", "o3", "o4")) or model.startswith(
        "openai/"
    ):
        provider = "openai"
    elif "claude" in model or model.startswith("anthropic/"):
        provider = "anthropic"
    elif "gemini" in model or model.startswith("google/"):
        provider = "gemini"
    else:
        # Default to litellm for unknown providers
        import logging as _logging

        _logging.getLogger(__name__).warning(
            "Unknown provider for model '%s' — falling back to litellm. "
            "Set framework explicitly if this is unintended.",
            config.model,
        )
        provider = "litellm"

    if provider not in _llm_registry:
        available = list(_llm_registry.keys())
        raise ValueError(
            f"Provider '{provider}' (auto-detected from model '{config.model}') "
            f"is not registered. Available: {available}. "
            f"Install the provider package or set framework explicitly."
        )

    return _llm_registry[provider](config)


def list_providers() -> list[str]:
    """List all registered LLM providers."""
    return list(_llm_registry.keys())


def _auto_register() -> None:
    """Auto-register available LLM implementations.

    Called on module import. Only registers providers whose
    dependencies are installed.
    """
    # OpenAI
    try:
        from fluxibly.llm.openai_llm import OpenAILLM

        register_llm("openai", OpenAILLM)
    except ImportError:
        pass

    # Anthropic
    try:
        from fluxibly.llm.anthropic_llm import AnthropicLLM

        register_llm("anthropic", AnthropicLLM)
    except ImportError:
        pass

    # Gemini
    try:
        from fluxibly.llm.gemini_llm import GeminiLLM

        register_llm("gemini", GeminiLLM)
    except ImportError:
        pass

    # LangChain
    try:
        from fluxibly.llm.langchain_llm import LangChainLLM

        register_llm("langchain", LangChainLLM)
    except ImportError:
        pass

    # LiteLLM
    try:
        from fluxibly.llm.litellm_llm import LiteLLM

        register_llm("litellm", LiteLLM)
    except ImportError:
        pass


_auto_register()
