"""
OpenAI LLM Adapter

Integrates with OpenAI Chat Completions API for streaming responses.
Can be used as a primary or fallback LLM.

Model Reference (2026-03):
  gpt-4o-mini        — Fastest, cost-effective (default for voice)
  gpt-4o             — Best overall quality, multimodal capable

Latency targets (voice pipeline, TTFT = time to first token):
  gpt-4o-mini — ~100ms TTFT (recommended for real-time voice)
  gpt-4o      — ~150ms TTFT

API Reference: https://platform.openai.com/docs/api-reference/chat
"""

from __future__ import annotations

from typing import AsyncIterable, AsyncIterator

from dvgateway.types import LlmAdapter, LlmOptions, Message


class OpenAILlmAdapter(LlmAdapter):
    """OpenAI LLM adapter implementing the LlmAdapter interface."""

    def __init__(
        self,
        api_key: str = "",
        model: str = "gpt-4o-mini",
        system_prompt: str = "You are a helpful voice assistant. Keep answers concise and conversational.",
        max_tokens: int = 1024,
        temperature: float = 0.7,
        presence_penalty: float = 0.0,
        frequency_penalty: float = 0.0,
    ) -> None:
        self._api_key = api_key
        self._model = model
        self.system_prompt = system_prompt
        self._max_tokens = max_tokens
        self._temperature = temperature
        self._presence_penalty = presence_penalty
        self._frequency_penalty = frequency_penalty

    async def _chat_impl(
        self,
        messages: list[Message],
        options: LlmOptions | None = None,
    ) -> AsyncIterator[str]:
        try:
            from openai import AsyncOpenAI
        except ImportError as e:
            raise ImportError("Install openai: pip install openai") from e

        client = AsyncOpenAI(api_key=self._api_key)

        model = (options.model if options else None) or self._model
        max_tokens = (options.max_tokens if options else None) or self._max_tokens
        temperature = (options.temperature if options else None) or self._temperature
        system = next(
            (m.content for m in messages if m.role == "system"),
            (options.system_prompt if options else None) or self.system_prompt,
        )

        chat_messages = [
            {"role": "system", "content": system},
            *[
                {"role": m.role, "content": m.content}
                for m in messages if m.role != "system"
            ],
        ]

        stream = await client.chat.completions.create(
            model=model,
            max_tokens=max_tokens,
            temperature=temperature,
            presence_penalty=self._presence_penalty,
            frequency_penalty=self._frequency_penalty,
            messages=chat_messages,  # type: ignore[arg-type]
            stream=True,
        )

        async for chunk in stream:
            delta = chunk.choices[0].delta.content if chunk.choices else None
            if delta:
                yield delta

    def chat(self, messages: list[Message], options: LlmOptions | None = None) -> AsyncIterable[str]:
        return self._chat_impl(messages, options)
