"""
Anthropic Claude LLM Adapter

Integrates with the Anthropic Messages API for streaming LLM responses.
Implements the LlmAdapter interface — returns streaming tokens as AsyncIterable.

Model Reference (2026-03):
  claude-opus-4-6        — Most capable, best reasoning and instruction following
  claude-sonnet-4-6      — Best balance of speed and quality (recommended for voice)
  claude-haiku-4-5-20251001 — Fastest, lowest cost (best latency for voice bots)

Latency targets (voice pipeline, TTFT = time to first token):
  claude-haiku-4-5    — ~80ms TTFT  (optimal for real-time voice)
  claude-sonnet-4-6   — ~120ms TTFT (recommended for complex voice agents)
  claude-opus-4-6     — ~200ms TTFT (use only for high-complexity tasks)

API Reference: https://docs.anthropic.com/en/api/messages
"""

from __future__ import annotations

from typing import AsyncIterable, AsyncIterator

from dvgateway.types import LlmAdapter, LlmOptions, Message


class AnthropicAdapter(LlmAdapter):
    """Anthropic Claude LLM adapter implementing the LlmAdapter interface."""

    def __init__(
        self,
        api_key: str = "",
        model: str = "claude-sonnet-4-6",
        system_prompt: str = "You are a helpful voice assistant. Keep answers concise and conversational, suitable for text-to-speech.",
        max_tokens: int = 1024,
        temperature: float = 0.7,
        top_p: float | None = None,
        stop_sequences: list[str] | None = None,
    ) -> None:
        self._api_key = api_key
        self._model = model
        self.system_prompt = system_prompt
        self._max_tokens = max_tokens
        self._temperature = temperature
        self._top_p = top_p
        self._stop_sequences = stop_sequences or []

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

        client = AsyncAnthropic(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_prompt = (options.system_prompt if options else None) or self.system_prompt

        # Separate system messages from conversation
        user_messages = [m for m in messages if m.role != "system"]
        system_content = next((m.content for m in messages if m.role == "system"), system_prompt)

        anthropic_messages = [
            {"role": m.role, "content": m.content}
            for m in user_messages
        ]

        params: dict[str, object] = {
            "model": model,
            "max_tokens": max_tokens,
            "system": system_content,
            "messages": anthropic_messages,
        }

        if temperature is not None:
            params["temperature"] = temperature
        if self._top_p is not None:
            params["top_p"] = self._top_p
        if self._stop_sequences:
            params["stop_sequences"] = self._stop_sequences

        async with client.messages.stream(**params) as stream:  # type: ignore[arg-type]
            async for event in stream:
                if hasattr(event, "type") and event.type == "content_block_delta":
                    if hasattr(event.delta, "text"):
                        yield event.delta.text

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