from __future__ import annotations

from typing import Any

from .base import (
    BaseProvider,
    JsonDict,
    ProviderError,
    build_openai_messages,
    build_openai_tools,
    ensure_trailing_slashless,
    extract_openai_model_names,
    normalise_openai_response,
)


class OpenAICompatibleProvider(BaseProvider):
    provider_type = "openai_compatible"

    def __init__(
        self,
        *,
        api_key: str,
        base_url: str,
        provider_name: str = "openai_compatible",
        timeout: float = 60.0,
        extra_headers: dict[str, str] | None = None,
        http_client=None,
    ) -> None:
        super().__init__(provider_name=provider_name, timeout=timeout, http_client=http_client)
        self.api_key = api_key
        self.base_url = ensure_trailing_slashless(base_url)
        self.extra_headers = extra_headers or {}

    def _ensure_configured(self) -> None:
        if not self.api_key:
            raise ProviderError(f"{self.provider_name} requires an API key. Configure it via Telegram /settings or /apikey <provider> <key>.")

    def _headers(self) -> dict[str, str]:
        self._ensure_configured()
        headers = {"Authorization": f"Bearer {self.api_key}"}
        headers.update(self.extra_headers)
        return headers

    async def generate(
        self,
        messages: list[JsonDict],
        tools: list[JsonDict] | None,
        model: str,
        system_prompt: str | None = None,
        temperature: float | None = None,
    ) -> JsonDict:
        payload: JsonDict = {
            "model": model,
            "messages": build_openai_messages(messages, system_prompt=system_prompt),
        }
        prepared_tools = build_openai_tools(tools)
        if prepared_tools:
            payload["tools"] = prepared_tools
            payload["tool_choice"] = "auto"
        if temperature is not None:
            payload["temperature"] = temperature

        response = await self.http.post_json(
            f"{self.base_url}/chat/completions",
            payload=payload,
            headers=self._headers(),
        )
        return normalise_openai_response(response.payload, fallback_model=model, provider_name=self.provider_name)

    async def list_models(self) -> list[str]:
        response = await self.http.get_json(f"{self.base_url}/models", headers=self._headers())
        return extract_openai_model_names(response.payload)
