"""
providers/anthropic_provider.py — Anthropic Claude provider.

Uses the official `anthropic` Python SDK.
Tool schemas are already in Anthropic's native format, so no conversion needed.
Messages are converted from the OpenAI-style format used internally by aide.
"""
from __future__ import annotations

import json
import logging

import anthropic

from .base import AIProvider, ProviderResponse, ToolCallResult, UsageStats

logger = logging.getLogger(__name__)

DEFAULT_MODEL = "claude-sonnet-5"


class AnthropicProvider(AIProvider):
    def __init__(self, api_key: str) -> None:
        self._client = anthropic.Anthropic(api_key=api_key)
        self._async_client = anthropic.AsyncAnthropic(api_key=api_key)

    @property
    def name(self) -> str:
        return "Anthropic"

    @property
    def default_model(self) -> str:
        return DEFAULT_MODEL

    # ── Public interface ──────────────────────────────────────────────────────

    def chat(
        self,
        messages: list[dict],
        tools: list[dict] | None = None,
        system: str = "",
        model: str = "",
        max_tokens: int = 4096,
    ) -> ProviderResponse:
        params = self._build_params(messages, tools, system, model, max_tokens)
        try:
            response = self._client.messages.create(**params)
            return self._parse_response(response)
        except Exception as e:
            logger.error(f"Anthropic chat error: {e}")
            return ProviderResponse(text=f"Error: {e}", finish_reason="error")

    async def chat_async(
        self,
        messages: list[dict],
        tools: list[dict] | None = None,
        system: str = "",
        model: str = "",
        max_tokens: int = 4096,
    ) -> ProviderResponse:
        params = self._build_params(messages, tools, system, model, max_tokens)
        try:
            response = await self._async_client.messages.create(**params)
            return self._parse_response(response)
        except Exception as e:
            logger.error(f"Anthropic async chat error: {e}")
            return ProviderResponse(text=f"Error: {e}", finish_reason="error")

    # ── Internal helpers ──────────────────────────────────────────────────────

    def _build_params(
        self,
        messages: list[dict],
        tools: list[dict] | None,
        system: str,
        model: str,
        max_tokens: int,
    ) -> dict:
        anthropic_messages = self._convert_messages(messages)
        params: dict = {
            "model": model or self.default_model,
            "messages": anthropic_messages,
            "max_tokens": max_tokens,
        }
        if system:
            # Wrap in a content block with cache_control so the system prompt is
            # cached across repeated calls (e.g. email handling runs). Cached tokens
            # cost ~10% of normal input price after the first write.
            params["system"] = [
                {"type": "text", "text": system, "cache_control": {"type": "ephemeral"}}
            ]
        if tools:
            # aide tool schemas ARE Anthropic format — pass through directly
            params["tools"] = tools
            params["tool_choice"] = {"type": "auto"}
        return params

    def _convert_messages(self, messages: list[dict]) -> list[dict]:
        """
        Convert aide's internal message list to Anthropic format.

        aide uses an OpenAI-style internal format:
          {"role": "user", "content": "..."}
          {"role": "assistant", "content": "...", "tool_calls": [...]}
          {"role": "tool", "tool_call_id": "...", "content": "..."}

        Anthropic requires:
          - tool calls embedded in content blocks (tool_use type)
          - tool results as user messages with tool_result content blocks
        """
        result: list[dict] = []
        i = 0
        while i < len(messages):
            msg = messages[i]
            role = msg["role"]

            if role == "system":
                i += 1
                continue  # Already handled via system= param

            if role == "assistant" and msg.get("tool_calls"):
                # Convert assistant tool calls to Anthropic content blocks
                blocks: list[dict] = []
                if msg.get("content"):
                    blocks.append({"type": "text", "text": msg["content"]})
                for tc in msg["tool_calls"]:
                    blocks.append({
                        "type": "tool_use",
                        "id": tc["id"],
                        "name": tc["name"],
                        "input": tc["arguments"],
                    })
                result.append({"role": "assistant", "content": blocks})

            elif role == "tool":
                # Group consecutive tool results into one user message
                tool_results: list[dict] = []
                while i < len(messages) and messages[i]["role"] == "tool":
                    t = messages[i]
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": t["tool_call_id"],
                        "content": t["content"],
                    })
                    i += 1
                result.append({"role": "user", "content": tool_results})
                continue  # i already advanced

            else:
                # content may be a string (plain text) or a list of blocks (multimodal)
                result.append({"role": role, "content": msg.get("content", "")})

            i += 1

        return result

    def _parse_response(self, response) -> ProviderResponse:
        text = ""
        tool_calls: list[ToolCallResult] = []

        for block in response.content:
            if block.type == "text":
                text += block.text
            elif block.type == "tool_use":
                tool_calls.append(ToolCallResult(
                    id=block.id,
                    name=block.name,
                    arguments=block.input,
                ))

        if response.usage:
            cache_read = getattr(response.usage, "cache_read_input_tokens", 0) or 0
            cache_write = getattr(response.usage, "cache_creation_input_tokens", 0) or 0
            usage = UsageStats(
                input_tokens=response.usage.input_tokens,
                output_tokens=response.usage.output_tokens,
                cache_read_tokens=cache_read,
                cache_write_tokens=cache_write,
            )
        else:
            usage = UsageStats()

        finish_reason = response.stop_reason or "stop"
        if tool_calls:
            finish_reason = "tool_use"

        return ProviderResponse(
            text=text or None,
            tool_calls=tool_calls,
            usage=usage,
            finish_reason=finish_reason,
            model=response.model,
        )
