"""
Anthropic LLM Provider Adapter

Adapter for Anthropic's Claude models.

v2.0: Includes event emission for tool calls and real-time visibility.
"""

import time
from typing import Callable

from anthropic import AsyncAnthropic, APIError, RateLimitError, APIConnectionError

from honeydb_ai.domain.exceptions import (
    LLMAPIError,
    LLMProviderError,
    LLMRateLimitError,
    LLMRequestCancelledError,
)
from honeydb_ai.domain.models import Event, LLMResponse, Message
from honeydb_ai.infrastructure.llm_adapters.base import BaseLLMAdapter


class AnthropicAdapter(BaseLLMAdapter):
    """
    Anthropic provider adapter.

    Supports Anthropic Claude 4 models (e.g., Opus, Sonnet, Haiku).
    See `get_available_models` for the current list of supported model IDs.
    """

    def __init__(self, api_key: str):
        """
        Initialize Anthropic adapter.

        Args:
            api_key: Anthropic API key
        """
        super().__init__(api_key)
        self.client = AsyncAnthropic(api_key=api_key)

    async def send_message(
        self,
        messages: list[Message],
        model: str | None = None,
        temperature: float | None = None,
        max_tokens: int | None = None,
        stream: bool = False,
        event_callback: Callable[[Event], None] | None = None,
    ) -> LLMResponse:
        """
        Send messages to Anthropic API.

        v2.0: Emits events for request lifecycle and tool calls.

        Args:
            messages: List of messages in the conversation
            model: Model identifier (e.g., "claude-opus-4-6")
            temperature: Sampling temperature (0.0-1.0)
            max_tokens: Maximum tokens in response
            stream: Whether to stream response (Phase 1: False)
            event_callback: Optional callback for events

        Returns:
            LLMResponse with the assistant's response

        Raises:
            LLMProviderError: If the provider returns an error
            LLMAPIError: If the API call fails
            LLMRateLimitError: If rate limit is exceeded
            LLMRequestCancelledError: If user cancels the request
        """
        model = model or "claude-sonnet-4-6"
        start_time = time.time()

        # Emit request started event
        self._emit_request_started(event_callback, model, len(messages))

        # Convert messages to Anthropic format
        api_messages = self._messages_to_dict(messages)

        # Extract system message if present (Anthropic requires separate system parameter)
        system_message = None
        filtered_messages = []
        for msg in api_messages:
            if msg["role"] == "system":
                system_message = msg["content"]
            else:
                filtered_messages.append(msg)

        # Build request parameters
        params = {
            "model": model,
            "messages": filtered_messages,
            "max_tokens": max_tokens or 4096,  # Anthropic requires max_tokens
        }

        if system_message:
            params["system"] = system_message

        if temperature is not None:
            params["temperature"] = temperature

        try:
            # Make API call
            response = await self.client.messages.create(**params)

            # Extract response content
            content = ""
            if response.content:
                for block in response.content:
                    if hasattr(block, 'text'):
                        content += block.text
                    elif hasattr(block, 'tool_use'):
                        # Tool call detected
                        self._emit_tool_call(
                            event_callback,
                            block.tool_use.name,
                            {"args": block.tool_use.input}
                        )

            finish_reason = response.stop_reason

            # Calculate duration
            duration_ms = (time.time() - start_time) * 1000

            # Emit response received event
            self._emit_response_received(event_callback, finish_reason, duration_ms)

            # Log (metadata only)
            self.logger.info(
                f"Anthropic request completed | Model: {model} | "
                f"Duration: {duration_ms:.0f}ms | Finish: {finish_reason}"
            )

            # Build response
            usage = {}
            if hasattr(response, 'usage') and response.usage:
                usage = {
                    "input_tokens": response.usage.input_tokens,
                    "output_tokens": response.usage.output_tokens,
                }

            return LLMResponse(
                content=content,
                model=model,
                finish_reason=finish_reason,
                usage=usage,
                metadata={
                    "response_id": response.id,
                    "model": response.model,
                }
            )

        except RateLimitError as e:
            self.logger.warning(f"Anthropic rate limit exceeded: {e}")
            raise LLMRateLimitError("Anthropic", None)

        except APIConnectionError as e:
            self.logger.error(f"Anthropic connection error: {e}")
            raise LLMAPIError("Anthropic", 0, f"Connection error: {e}")

        except APIError as e:
            self.logger.error(f"Anthropic API error: {e}")
            status_code = getattr(e, 'status_code', 0)
            raise LLMAPIError("Anthropic", status_code, str(e))

        except KeyboardInterrupt:
            self.logger.info("Anthropic request cancelled by user")
            raise LLMRequestCancelledError()

        except Exception as e:
            self.logger.error(f"Unexpected Anthropic error: {e}")
            raise LLMProviderError("Anthropic", str(e))

    def validate_api_key(self) -> bool:
        """
        Validate that the API key is configured.

        Returns:
            True if API key is present and non-empty
        """
        return bool(self.api_key and len(self.api_key.strip()) > 0)

    def get_provider_name(self) -> str:
        """Get the provider name."""
        return "anthropic"

    def get_available_models(self) -> list[str]:
        """
        Get list of available Anthropic models.

        Returns:
            List of model identifiers
        """
        return [
            "claude-opus-4-6",
            "claude-sonnet-4-6",
            "claude-haiku-4-5-20251001",
        ]
