"""Base provider interface and auto-detection logic for agentbudget."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any


@dataclass
class UsageData:
    """Extracted usage data from an LLM API response.

    Attributes:
        model: The model name as reported by the API.
        input_tokens: Number of input/prompt tokens consumed.
        output_tokens: Number of output/completion tokens consumed.
        provider: Provider name string ("openai" or "anthropic").
    """

    model: str
    input_tokens: int
    output_tokens: int
    provider: str


class BaseProvider(ABC):
    """Abstract base class for LLM provider adapters.

    Subclasses implement extract_usage() to parse provider-specific
    API response objects and return a normalized UsageData instance.
    """

    @abstractmethod
    def extract_usage(self, response: Any) -> UsageData:
        """Extract token usage and model information from an API response.

        Args:
            response: The raw API response object from the provider SDK.

        Returns:
            A UsageData instance with model, token counts, and provider name.

        Raises:
            ValueError: If the response object cannot be parsed.
        """
        ...


def detect_provider(response: Any) -> "BaseProvider":
    """Auto-detect the appropriate provider from an API response object.

    Detection order:
    1. isinstance check for openai.types.chat.ChatCompletion → OpenAIProvider
    2. isinstance check for google.generativeai.GenerateContentResponse → GeminiProvider
    3. isinstance check for google.genai.types.GenerateContentResponse → GeminiProvider
    4. Check if response.usage has input_tokens attribute → AnthropicProvider
    5. Check if response.usage_metadata has prompt_token_count → GeminiProvider
    6. Fallback: check response.model prefix
       ("gpt-", "o1", "o3" → OpenAI; "claude-" → Anthropic; "gemini-" → Gemini)
    7. Raise ValueError if no match.

    Args:
        response: The raw API response object from any supported provider.

    Returns:
        An instantiated provider adapter for the detected provider.

    Raises:
        ValueError: If the provider cannot be detected from the response.
    """
    # Step 1: isinstance check for OpenAI ChatCompletion
    try:
        import openai.types.chat
        if isinstance(response, openai.types.chat.ChatCompletion):
            from agentbudget.providers.openai import OpenAIProvider
            return OpenAIProvider()
    except ImportError:
        pass

    # Step 2: isinstance check for google-generativeai SDK
    try:
        import google.generativeai as genai
        if isinstance(response, genai.types.GenerateContentResponse):
            from agentbudget.providers.gemini import GeminiProvider
            return GeminiProvider()
    except (ImportError, AttributeError):
        pass

    # Step 3: isinstance check for google-genai SDK
    try:
        import google.genai.types
        if isinstance(response, google.genai.types.GenerateContentResponse):
            from agentbudget.providers.gemini import GeminiProvider
            return GeminiProvider()
    except (ImportError, AttributeError):
        pass

    # Step 4: Check for Anthropic-style usage (input_tokens attribute)
    if hasattr(response, "usage") and hasattr(response.usage, "input_tokens"):
        from agentbudget.providers.anthropic import AnthropicProvider
        return AnthropicProvider()

    # Step 5: Check for Gemini-style usage_metadata
    if (
        hasattr(response, "usage_metadata")
        and response.usage_metadata is not None
        and hasattr(response.usage_metadata, "prompt_token_count")
    ):
        from agentbudget.providers.gemini import GeminiProvider
        return GeminiProvider()

    # Step 6: Fallback by model name prefix
    model = getattr(response, "model", "")
    if isinstance(model, str):
        if model.startswith(("gpt-", "o1", "o3")):
            from agentbudget.providers.openai import OpenAIProvider
            return OpenAIProvider()
        if model.startswith("claude-"):
            from agentbudget.providers.anthropic import AnthropicProvider
            return AnthropicProvider()
        if model.startswith("gemini-"):
            from agentbudget.providers.gemini import GeminiProvider
            return GeminiProvider()

    raise ValueError("Cannot detect provider from response object.")
