"""LLM client protocol and factory for dependency injection.

This module defines the LLMClient protocol that abstracts LLM API calls,
enabling dependency injection for testing and decoupling from modelrelay.
"""

from __future__ import annotations

from typing import Any, Protocol, TYPE_CHECKING

if TYPE_CHECKING:
    from .modelrelay import TokenUsage, BatchResponse


class LLMClient(Protocol):
    """Protocol for LLM API clients.

    This abstraction enables:
    - Dependency injection for testing
    - Decoupling from specific LLM providers
    - Explicit dependencies instead of late imports
    """

    def responses_create(
        self,
        messages: list[dict[str, Any]],
        model: str,
        max_tokens: int = 4096,
        temperature: float = 0.0,
        return_usage: bool = False,
    ) -> str | tuple[str, "TokenUsage"]:
        """Create a response from a list of messages.

        Args:
            messages: List of message dicts with 'role' and 'content'
            model: Model identifier
            max_tokens: Maximum output tokens
            temperature: Sampling temperature
            return_usage: If True, return (response, TokenUsage) tuple

        Returns:
            Response text, or (response, TokenUsage) if return_usage=True
        """
        ...

    def batch_responses_typed(
        self,
        requests: list[dict[str, Any]],
        options: dict[str, Any] | None = None,
    ) -> "BatchResponse":
        """Make a batch responses request.

        Args:
            requests: List of request dicts
            options: Optional batch options

        Returns:
            BatchResponse with results and usage
        """
        ...

    def get_model_context_window(self, model_id: str) -> int | None:
        """Return the max context window (tokens) for a model if known.

        Args:
            model_id: Model identifier

        Returns:
            Context window size in tokens, or None if unknown
        """
        ...

    def get_model_max_output_tokens(self, model_id: str) -> int | None:
        """Return the max output tokens for a model if known.

        Args:
            model_id: Model identifier

        Returns:
            Max output tokens, or None if unknown
        """
        ...


class ModelRelayLLMClient:
    """LLMClient implementation using ModelRelay.

    This is the default production implementation that wraps
    the ModelRelayClient for use with LlmOrchestrator.
    """

    def __init__(self, client: Any | None = None) -> None:
        """Initialize with optional ModelRelayClient instance.

        Args:
            client: ModelRelayClient instance. If None, uses default client.
        """
        self._client = client

    def _get_client(self) -> Any:
        """Get or create the ModelRelayClient."""
        if self._client is None:
            from .modelrelay import ModelRelayClient
            self._client = ModelRelayClient()
        return self._client

    def responses_create(
        self,
        messages: list[dict[str, Any]],
        model: str,
        max_tokens: int = 4096,
        temperature: float = 0.0,
        return_usage: bool = False,
    ) -> str | tuple[str, "TokenUsage"]:
        """Create a response via ModelRelay."""
        return self._get_client().responses_create(
            messages=messages,
            model=model,
            max_tokens=max_tokens,
            temperature=temperature,
            return_usage=return_usage,
        )

    def batch_responses_typed(
        self,
        requests: list[dict[str, Any]],
        options: dict[str, Any] | None = None,
    ) -> "BatchResponse":
        """Make a batch responses request via ModelRelay."""
        return self._get_client().batch_responses_typed(
            requests=requests,
            options=options,
        )

    def get_model_context_window(self, model_id: str) -> int | None:
        """Return the max context window (tokens) for a model if known."""
        return self._get_client().get_model_context_window(model_id)

    def get_model_max_output_tokens(self, model_id: str) -> int | None:
        """Return the max output tokens for a model if known."""
        return self._get_client().get_model_max_output_tokens(model_id)


def create_default_llm_client() -> LLMClient:
    """Factory function to create the default LLM client.

    Returns:
        ModelRelayLLMClient instance
    """
    return ModelRelayLLMClient()


def create_llm_client_with_credentials(
    api_key: str | None = None,
    customer_token: str | None = None,
) -> LLMClient:
    """Factory function to create an LLM client with explicit credentials.

    This avoids relying on environment variables and enables explicit dependency injection.

    Args:
        api_key: ModelRelay API key
        customer_token: ModelRelay customer token (takes precedence over api_key)

    Returns:
        ModelRelayLLMClient instance with configured credentials
    """
    from .modelrelay import ModelRelayClient
    client = ModelRelayClient(api_key=api_key, customer_token=customer_token)
    return ModelRelayLLMClient(client)
