"""Base provider interface."""

import os
from abc import ABC, abstractmethod
from collections.abc import AsyncGenerator
from dataclasses import dataclass

import httpx
import structlog

from robot_resources.proxy.models import (
    ChatCompletionRequest,
    ChatCompletionResponse,
    Usage,
)

logger = structlog.get_logger()


@dataclass
class ProviderConfig:
    """Configuration for a provider."""

    api_key: str
    base_url: str | None = None
    timeout: float = 60.0

    @classmethod
    def from_env(cls, env_var: str, base_url: str | None = None) -> "ProviderConfig":
        """Create config from environment variable."""
        api_key = os.environ.get(env_var)
        if not api_key:
            raise ValueError(f"Environment variable {env_var} not set")
        return cls(api_key=api_key, base_url=base_url)


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

    def __init__(self, config: ProviderConfig):
        self.config = config
        self._last_stream_usage: Usage | None = None

    @property
    @abstractmethod
    def name(self) -> str:
        """Provider name."""
        ...

    @property
    @abstractmethod
    def base_url(self) -> str:
        """Base URL for API."""
        ...

    @abstractmethod
    def format_request(self, request: ChatCompletionRequest) -> dict:
        """Format request for this provider's API."""
        ...

    @abstractmethod
    def parse_response(self, response: dict) -> ChatCompletionResponse:
        """Parse provider response to standard format."""
        ...

    @abstractmethod
    def map_model(self, model: str) -> str:
        """Map model name to provider-specific name."""
        ...

    def _create_client(self, api_key: str | None = None) -> httpx.AsyncClient:
        """Create a new HTTP client for a single request."""
        return httpx.AsyncClient(
            timeout=self.config.timeout,
            headers=self._get_headers(api_key=api_key),
        )

    @abstractmethod
    def _get_headers(self, *, api_key: str | None = None) -> dict:
        """Get headers for API requests.

        When *api_key* is provided it takes precedence over
        ``self.config.api_key`` (transparent-proxy passthrough).
        """
        ...

    async def _post(self, endpoint: str, data: dict, *, api_key: str | None = None) -> dict:
        """Make POST request to provider API."""
        url = f"{self.config.base_url or self.base_url}{endpoint}"
        logger.info("provider_request", provider=self.name, endpoint=endpoint)

        async with self._create_client(api_key=api_key) as client:
            response = await client.post(url, json=data)
            response.raise_for_status()
            return response.json()

    async def complete(
        self, request: ChatCompletionRequest, *, api_key: str | None = None,
    ) -> ChatCompletionResponse:
        """Execute a chat completion request."""
        formatted = self.format_request(request)
        response = await self._post(self._get_completion_endpoint(), formatted, api_key=api_key)
        return self.parse_response(response)

    @abstractmethod
    def _get_completion_endpoint(self) -> str:
        """Get the completion endpoint path."""
        ...

    async def stream(
        self, request: ChatCompletionRequest, *, api_key: str | None = None,
    ) -> AsyncGenerator[str, None]:
        """Stream a chat completion request."""
        # Default implementation - override in subclasses
        raise NotImplementedError("Streaming not implemented for this provider")
        yield  # pragma: no cover  — makes this a proper async generator for type checking
