"""Provider adapters used by gateway runtimes.

OSS version - contains generic provider adapters without EB1 orchestration.
"""

from __future__ import annotations

from typing import Any, Generator

from ember.models.providers.anthropic_responses import AnthropicProvider
from ember.models.providers.azure_openai import AzureOpenAIProvider
from ember.models.providers.base import BaseProvider
from ember.models.providers.google import GoogleProvider
from ember.models.providers.openai import OpenAIProvider
from ember.models.schemas import ChatResponse, UsageStats


def _response_to_tuple(response: ChatResponse) -> tuple[str, int, int, dict | None]:
    """Convert a ChatResponse to the legacy tuple format."""
    usage = response.usage or UsageStats()
    raw_metadata = response.raw_output

    if isinstance(raw_metadata, dict):
        metadata: dict | None = dict(raw_metadata)
    elif raw_metadata is None:
        metadata = None
    elif hasattr(raw_metadata, "model_dump"):
        metadata = raw_metadata.model_dump()
    elif hasattr(raw_metadata, "to_dict"):
        metadata = raw_metadata.to_dict()
    else:
        metadata = {"_type": type(raw_metadata).__name__}

    return response.data, usage.prompt_tokens, usage.completion_tokens, metadata


class ProviderAdapter:
    """Minimal interface implemented by all provider adapters."""

    name: str = "abstract"
    supports_streaming: bool = False

    def complete(
        self,
        model: str,
        prompt_text: str,
        temperature: float = 0.0,
        **kwargs: Any,
    ) -> tuple[str, int, int, object | None]:
        raise NotImplementedError

    def stream_complete(
        self,
        model: str,
        prompt_text: str,
        temperature: float = 0.0,
        **kwargs: Any,
    ) -> Generator[str, None, ChatResponse]:
        raise NotImplementedError


class ProviderClientAdapter(ProviderAdapter):
    """Adapter that wraps a ``BaseProvider`` implementation."""

    def __init__(self, name: str, provider_cls: type[BaseProvider]) -> None:
        self.name = name
        self._provider_cls = provider_cls
        self.supports_streaming = getattr(provider_cls, "supports_streaming", False)
        self._provider_cache: dict[str, BaseProvider] = {}

    def _create_provider(self, api_key_override: str | None = None) -> BaseProvider:
        """Cache providers by api_key override."""
        cache_key = api_key_override or "__default__"
        provider = self._provider_cache.get(cache_key)
        if provider is None:
            if api_key_override is None:
                provider = self._provider_cls()
            else:
                provider = self._provider_cls(api_key_override)
            self._provider_cache[cache_key] = provider
        return provider

    def complete(
        self,
        model: str,
        prompt_text: str,
        temperature: float = 0.0,
        **kwargs: Any,
    ) -> tuple[str, int, int, object | None]:
        params = dict(kwargs)
        responses_payload = params.pop("responses_payload", None)
        api_key_override = params.pop("api_key_override", None)
        provider = self._create_provider(api_key_override)

        if responses_payload is not None and getattr(
            provider, "supports_responses_api", False
        ):
            payload = dict(responses_payload)
            payload.setdefault("model", model)
            response = provider.complete_responses_payload(payload)
            return _response_to_tuple(response)

        response = provider.complete(
            prompt_text,
            model,
            temperature=temperature,
            **params,
        )
        return _response_to_tuple(response)

    def stream_complete(
        self,
        model: str,
        prompt_text: str,
        temperature: float = 0.0,
        **kwargs: Any,
    ) -> Generator[str, None, ChatResponse]:
        params = dict(kwargs)
        responses_payload = params.pop("responses_payload", None)
        api_key_override = params.pop("api_key_override", None)
        provider = self._create_provider(api_key_override)

        if responses_payload is not None and getattr(
            provider, "supports_responses_api", False
        ):
            payload = dict(responses_payload)
            payload.setdefault("model", model)
            return provider.stream_responses_payload(payload)

        return provider.stream_complete(
            prompt_text,
            model,
            temperature=temperature,
            **params,
        )


class OpenAIAdapter(ProviderClientAdapter):
    """Adapter that routes calls to OpenAI."""

    def __init__(self, provider_cls: type[BaseProvider] | None = None) -> None:
        super().__init__("openai", provider_cls or OpenAIProvider)
        self.supports_streaming = True


class AnthropicAdapter(ProviderClientAdapter):
    """Adapter wrapping the Anthropic provider."""

    def __init__(self) -> None:
        super().__init__("anthropic", AnthropicProvider)
        self.supports_streaming = False


class GoogleAdapter(ProviderClientAdapter):
    """Adapter wrapping Google Gemini provider."""

    def __init__(self) -> None:
        super().__init__("google", GoogleProvider)
        self.supports_streaming = True


class AzureOpenAIAdapter(ProviderClientAdapter):
    """Adapter wrapping Azure OpenAI."""

    def __init__(self) -> None:
        super().__init__("azure_openai", AzureOpenAIProvider)
        self.supports_streaming = True


def default_adapters() -> dict[str, ProviderAdapter]:
    """Instantiate the default provider adapters used by the gateway."""
    return {
        "openai": OpenAIAdapter(),
        "anthropic": AnthropicAdapter(),
        "google": GoogleAdapter(),
        "azure_openai": AzureOpenAIAdapter(),
    }


__all__ = [
    "ProviderAdapter",
    "ProviderClientAdapter",
    "OpenAIAdapter",
    "AnthropicAdapter",
    "GoogleAdapter",
    "AzureOpenAIAdapter",
    "default_adapters",
]
