"""Provider capability registry -- OSS build.

Single source of truth for provider metadata, without Takumi/EB1 entries.

Design principles:
- Model equivalence as first-class concept
- Data-oriented: Provider x Model matrix
- No magic: Explicit, greppable definitions
- Extensible: Support custom provider registration

Examples:
    >>> from .provider_registry import get_providers_for_model
    >>> providers = get_providers_for_model("gpt-4")
    >>> print(providers)  # ["openai", "azure_openai"]
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import FrozenSet


@dataclass(frozen=True)
class ProviderCapability:
    """Provider capability definition.

    Attributes:
        name: Provider identifier (e.g., "openai", "azure_openai").
        models: Canonical model IDs this provider can serve.
        streaming: Whether provider supports streaming responses.
        json_strict: Whether provider supports strict JSON mode.
        tools: Whether provider supports tool/function calling.
        vision: Whether provider supports vision/multimodal inputs.
        responses: Whether provider natively supports the Responses API.
        preference_score: Routing preference (1.0 = highest, lower = fallback).
            Used by router to select among equivalent providers.
        notes: Optional documentation or configuration hints.
    """

    name: str
    models: FrozenSet[str]
    streaming: bool = True
    json_strict: bool = False
    tools: bool = False
    vision: bool = False
    responses: bool = True
    preference_score: float = 1.0
    notes: str = ""


# Core provider registry - canonical source of truth
# Organized by provider family for maintainability
_CORE_PROVIDERS = [
    # OpenAI first-party
    ProviderCapability(
        name="openai",
        models=frozenset(
            {
                "gpt-4",
                "gpt-4-turbo",
                "gpt-4-turbo-preview",
                "gpt-4-0125-preview",
                "gpt-4-1106-preview",
                "gpt-5",
                "gpt-5-mini",
                "gpt-5-nano",
                "gpt-5-chat-latest",
                "gpt-5-codex",
                "o1",
                "o1-mini",
                "o1-preview",
            }
        ),
        streaming=True,
        json_strict=True,
        tools=True,
        vision=True,
        responses=True,
        preference_score=1.0,
        notes="OpenAI first-party API - highest preference",
    ),
    # Azure OpenAI - same models, slightly lower preference
    ProviderCapability(
        name="azure_openai",
        models=frozenset(
            {
                "gpt-4",
                "gpt-4-turbo",
                "gpt-4-turbo-preview",
                "gpt-4-0125-preview",
                "gpt-4-1106-preview",
                "gpt-5",
                "gpt-5-mini",
                "gpt-5-nano",
            }
        ),
        streaming=True,
        json_strict=True,
        tools=True,
        vision=True,
        responses=True,
        preference_score=0.95,
        notes="Azure OpenAI deployments - pooled with OpenAI first-party",
    ),
    # Anthropic Claude
    ProviderCapability(
        name="anthropic",
        models=frozenset(
            {
                "claude-3-opus-20240229",
                "claude-3-sonnet-20240229",
                "claude-3-haiku-20240307",
                "claude-3-5-sonnet-20240620",
                "claude-3-5-sonnet-20241022",
                "claude-opus-4-20250514",
                "claude-opus-4-1-20250805",
                "claude-sonnet-4-20250514",
            }
        ),
        streaming=True,
        json_strict=True,
        tools=False,
        vision=False,
        responses=True,
        preference_score=0.9,
        notes="Anthropic Claude models",
    ),
    # Google Gemini
    ProviderCapability(
        name="google",
        models=frozenset(
            {
                "gemini-pro",
                "gemini-pro-vision",
                "gemini-1.5-pro",
                "gemini-1.5-flash",
                "gemini-2.0-flash-exp",
                "gemini-2.5-pro",
                "gemini-2.5-flash",
                "gemini-2.5-flash-lite",
            }
        ),
        streaming=True,
        json_strict=False,
        tools=False,
        vision=True,
        responses=True,
        preference_score=0.85,
        notes="Google Gemini models - streaming may require env override",
    ),
]


# Runtime registry allows custom provider registration
_custom_providers: dict[str, ProviderCapability] = {}


def register_provider(provider: ProviderCapability) -> None:
    """Register a custom provider at runtime.

    Args:
        provider: Provider capability definition.

    Raises:
        ValueError: If provider name conflicts with core provider.

    Examples:
        >>> custom = ProviderCapability(
        ...     name="bedrock",
        ...     models=frozenset({"claude-3-opus"}),
        ...     streaming=True,
        ... )
        >>> register_provider(custom)
    """
    if any(p.name == provider.name for p in _CORE_PROVIDERS):
        raise ValueError(
            f"Cannot override core provider '{provider.name}'. "
            f"Use a different name or configure via settings."
        )
    _custom_providers[provider.name] = provider


def unregister_provider(name: str) -> bool:
    """Unregister a custom provider.

    Args:
        name: Provider name to remove.

    Returns:
        True if provider was removed, False if not found.
    """
    if name in _custom_providers:
        del _custom_providers[name]
        return True
    return False


def list_providers() -> list[ProviderCapability]:
    """Return all registered providers (core + custom).

    Returns:
        List of provider capabilities, with custom providers appended.
    """
    return list(_CORE_PROVIDERS) + list(_custom_providers.values())


def get_provider(name: str) -> ProviderCapability | None:
    """Get provider capability by name.

    Args:
        name: Provider identifier.

    Returns:
        Provider capability if found, None otherwise.
    """
    for provider in _CORE_PROVIDERS:
        if provider.name == name:
            return provider
    return _custom_providers.get(name)


def get_providers_for_model(model: str) -> list[str]:
    """Return provider names that can serve the given model.

    This is the key function enabling model-centric capacity pooling.
    Multiple providers can serve the same model (e.g., OpenAI + Azure OpenAI
    both serve gpt-4), and this function returns all of them.

    Args:
        model: Canonical model identifier (e.g., "gpt-4", "claude-opus-4").

    Returns:
        List of provider names, sorted by preference_score (descending).

    Examples:
        >>> providers = get_providers_for_model("gpt-4")
        >>> print(providers)  # ["openai", "azure_openai"]
    """
    model_lower = (model or "").strip().lower()

    candidates = [
        (p.name, p.preference_score)
        for p in list_providers()
        if model_lower in {m.lower() for m in p.models}
    ]

    candidates.sort(key=lambda x: x[1], reverse=True)
    return [name for name, _ in candidates]


def get_capability_map() -> dict[str, dict[str, bool]]:
    """Return capability map for all providers.

    Used by gateway capabilities layer for backward compatibility.

    Returns:
        Dict mapping provider name to capability flags.
    """
    result = {}
    for provider in list_providers():
        result[provider.name] = {
            "supports_streaming": provider.streaming,
            "json_strict": provider.json_strict,
            "tools": provider.tools,
            "vision": provider.vision,
            "supports_responses_api": provider.responses,
        }
    return result


def get_routing_preferences() -> dict[str, float]:
    """Return routing preference scores for all providers.

    Used by router to select among equivalent providers.

    Returns:
        Dict mapping provider name to preference score.
    """
    return {p.name: p.preference_score for p in list_providers()}


__all__ = [
    "ProviderCapability",
    "register_provider",
    "unregister_provider",
    "list_providers",
    "get_provider",
    "get_providers_for_model",
    "get_capability_map",
    "get_routing_preferences",
]
