"""AI provider factory — registry-based O(1) lookup.

Usage:
    from ai import get_provider
    from ai.provider_config import ProviderConfig

    config = ProviderConfig(provider_id="groq", api_key="sk-...")
    provider = get_provider(config)
    result = await provider.classify(text, topics, system_prompt)
"""
from __future__ import annotations

from ai.base import AIProvider
from ai.anthropic_provider import AnthropicProvider
from ai.openai_provider import OpenAIProvider
from ai.generic_openai_provider import GenericOpenAIProvider
from ai.provider_config import ProviderConfig, PROVIDER_DEFAULTS, SUPPORTS_JSON_MODE


# Registry: maps provider_id → provider class
# "openai" uses OpenAIProvider (no response_format override needed — plain OpenAI)
# "anthropic" uses AnthropicProvider (native output_config, Plan 03 widened ctor accepts context_chars+base_url)
# All 8 OpenAI-compat vendors use GenericOpenAIProvider (D-16/D-17/D-18)
_REGISTRY: dict[str, type[AIProvider]] = {
    "openai": OpenAIProvider,
    "anthropic": AnthropicProvider,
    "gemini": GenericOpenAIProvider,
    "groq": GenericOpenAIProvider,
    "xai": GenericOpenAIProvider,
    "deepseek": GenericOpenAIProvider,
    "openrouter": GenericOpenAIProvider,
    "mistral": GenericOpenAIProvider,
    "ollama": GenericOpenAIProvider,
    "lmstudio": GenericOpenAIProvider,
}


def get_provider(config: ProviderConfig) -> AIProvider:
    """Instantiate and return an AI provider for the given ProviderConfig.

    Resolves defaults from PROVIDER_DEFAULTS when config fields are absent,
    normalises an empty api_key to "not-needed" (OpenAI SDK 2.34+ rejects ""),
    and sets supports_json_mode from the SUPPORTS_JSON_MODE lookup for
    GenericOpenAIProvider instances.

    Args:
        config: A ProviderConfig with at minimum provider_id set.

    Returns:
        A fully-constructed AIProvider instance.

    Raises:
        ValueError: If config.provider_id is not in the registry.
    """
    cls = _REGISTRY.get(config.provider_id)
    if cls is None:
        raise ValueError(f"Unknown AI provider: {config.provider_id!r}")

    defaults = PROVIDER_DEFAULTS[config.provider_id]

    # Resolve effective values — config fields take precedence over defaults
    effective_api_key = config.api_key or "not-needed"
    effective_model = config.model or defaults["model"]
    effective_base_url = config.base_url if config.base_url is not None else defaults["base_url"]
    effective_context_chars = config.context_chars or defaults["context_chars"]

    if config.provider_id == "anthropic":
        # AnthropicProvider accepts context_chars and base_url (Plan 03 widened ctor).
        # base_url is accepted for uniform factory signature but unused by the SDK.
        return cls(
            api_key=effective_api_key,
            model=effective_model,
            context_chars=effective_context_chars,
            base_url=effective_base_url,
        )
    elif cls is GenericOpenAIProvider:
        return cls(
            api_key=effective_api_key,
            model=effective_model,
            base_url=effective_base_url,
            context_chars=effective_context_chars,
            supports_json_mode=SUPPORTS_JSON_MODE[config.provider_id],
        )
    else:
        # OpenAIProvider
        return cls(
            api_key=effective_api_key,
            model=effective_model,
            base_url=effective_base_url,
            context_chars=effective_context_chars,
        )
