"""Anthropic model implementation.

This module provides the Anthropic model implementation using the
Anthropic Python SDK.
"""

import logging
import os
from typing import Any, Dict, List, Optional

from agent_factory.models.base import (
    BaseModel,
    Message,
    ModelError,
    ModelNotAvailableError,
    ModelRateLimitError,
    ModelResponse,
    StopReason,
    ToolDefinition,
    ToolUse,
)

logger = logging.getLogger(__name__)


class AnthropicModel(BaseModel):
    """Anthropic model implementation.

    Uses the Anthropic Python SDK for direct API access.

    Example:
        model = AnthropicModel(
            model_id="claude-3-5-sonnet-20241022",
            api_key="sk-ant-...",  # Or use ANTHROPIC_API_KEY env var
        )
        response = model.converse(messages, system_prompt)
    """

    def __init__(
        self,
        model_id: str = "claude-3-5-sonnet-20241022",
        api_key: Optional[str] = None,
        client: Optional[Any] = None,
    ):
        """Initialize Anthropic model.

        Args:
            model_id: Anthropic model ID
            api_key: API key (defaults to ANTHROPIC_API_KEY env var)
            client: Optional pre-configured Anthropic client
        """
        self._model_id = model_id
        self._api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
        self._client = client

    def _get_client(self):
        """Get or create the Anthropic client."""
        if self._client is None:
            if not self._api_key:
                raise ModelNotAvailableError(
                    "ANTHROPIC_API_KEY not set",
                    model_id=self._model_id,
                )
            try:
                import anthropic

                self._client = anthropic.Anthropic(api_key=self._api_key)
            except ImportError:
                raise ModelNotAvailableError(
                    "anthropic is required. "
                    "Install with: pip install agent-factory-core[anthropic]",
                    model_id=self._model_id,
                )
        return self._client

    def converse(
        self,
        messages: List[Message],
        system_prompt: str,
        tools: Optional[List[ToolDefinition]] = None,
        max_tokens: int = 4096,
        temperature: float = 0.7,
    ) -> ModelResponse:
        """Send messages to Anthropic and get a response."""
        client = self._get_client()

        # Convert messages to Anthropic format
        anthropic_messages = self._convert_messages(messages)

        # Build request
        request: Dict[str, Any] = {
            "model": self._model_id,
            "messages": anthropic_messages,
            "system": system_prompt,
            "max_tokens": max_tokens,
            "temperature": temperature,
        }

        # Add tools if provided
        if tools:
            request["tools"] = [self._convert_tool(tool) for tool in tools]

        try:
            response = client.messages.create(**request)
            return self._parse_response(response)

        except Exception as e:
            error_str = str(e).lower()
            if "rate" in error_str or "429" in error_str:
                raise ModelRateLimitError(
                    f"Anthropic rate limited: {e}",
                    model_id=self._model_id,
                    original_error=e,
                )
            is_transient = "timeout" in error_str or "connection" in error_str
            raise ModelError(
                f"Anthropic error: {e}",
                model_id=self._model_id,
                is_transient=is_transient,
                original_error=e,
            )

    def _convert_messages(self, messages: List[Message]) -> List[Dict[str, Any]]:
        """Convert messages to Anthropic format."""
        result = []
        for msg in messages:
            if msg.tool_result:
                result.append(
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "tool_result",
                                "tool_use_id": msg.tool_result.get("toolUseId", ""),
                                "content": str(msg.tool_result.get("content", "")),
                            }
                        ],
                    }
                )
            elif msg.tool_use:
                result.append(
                    {
                        "role": "assistant",
                        "content": [
                            {
                                "type": "tool_use",
                                "id": msg.tool_use.tool_use_id,
                                "name": msg.tool_use.name,
                                "input": msg.tool_use.input,
                            }
                        ],
                    }
                )
            else:
                result.append(
                    {
                        "role": msg.role,
                        "content": msg.content,
                    }
                )
        return result

    def _convert_tool(self, tool: ToolDefinition) -> Dict[str, Any]:
        """Convert tool definition to Anthropic format."""
        return {
            "name": tool.name,
            "description": tool.description,
            "input_schema": tool.input_schema,
        }

    def _parse_response(self, response: Any) -> ModelResponse:
        """Parse Anthropic response into ModelResponse."""
        content_items = response.content
        text_parts = []
        tool_uses = []

        for item in content_items:
            if item.type == "text":
                text_parts.append(item.text)
            elif item.type == "tool_use":
                tool_uses.append(
                    ToolUse(
                        tool_use_id=item.id,
                        name=item.name,
                        input=item.input,
                    )
                )

        # Map stop reason
        stop_reason_map = {
            "end_turn": StopReason.END_TURN,
            "tool_use": StopReason.TOOL_USE,
            "max_tokens": StopReason.MAX_TOKENS,
            "stop_sequence": StopReason.STOP_SEQUENCE,
        }
        stop_reason = stop_reason_map.get(response.stop_reason, StopReason.END_TURN)

        return ModelResponse(
            content="\n".join(text_parts),
            stop_reason=stop_reason,
            tool_uses=tool_uses,
            usage={
                "input_tokens": response.usage.input_tokens,
                "output_tokens": response.usage.output_tokens,
            },
            model_id=self._model_id,
            raw_response=response.model_dump() if hasattr(response, "model_dump") else None,
        )

    def is_available(self) -> bool:
        """Check if Anthropic is available."""
        try:
            self._get_client()
            return True
        except Exception:
            return False

    @property
    def model_id(self) -> str:
        """Get the model identifier."""
        return self._model_id
