"""Anthropic Claude adapter (tool use)."""

from __future__ import annotations

import httpx
import json
import logging
from typing import Any

from mcpbridge.adapters.base import BaseLLMAdapter, ToolCall, ToolResult
from mcpbridge.config import LLMConfig
from mcpbridge.exceptions import ToolsNotSupportedError
from mcpbridge.utils.token_counter import estimate_messages_tokens

logger = logging.getLogger("mcpbridge")


class AnthropicAdapter(BaseLLMAdapter):
    """Adapter for Anthropic Claude via the Messages API."""

    def __init__(self, llm_config: LLMConfig) -> None:
        try:
            import anthropic as _anthropic  # noqa: F401
        except ImportError as e:
            raise ImportError("Missing `anthropic` dependency. Install with: pip install mcpbridge[anthropic]") from e

        self._provider = "anthropic"
        self._model = llm_config.model
        self._api_key = llm_config.api_key
        self._timeout = int(llm_config.timeout)

        self._temperature = float(llm_config.temperature)
        self._max_tokens = int(llm_config.max_tokens)
        self._top_p = llm_config.top_p
        self._stop_sequences = llm_config.stop_sequences
        self._thinking = llm_config.thinking

    @property
    def provider_name(self) -> str:
        return self._provider

    @property
    def supports_parallel_tool_calls(self) -> bool:
        return True

    @property
    def supports_system_prompt(self) -> bool:
        return True

    def _normalize_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
        """Convert our generic message representation into Anthropic content blocks."""
        out: list[dict[str, Any]] = []
        for m in messages:
            role = m.get("role")
            if role not in {"user", "assistant"}:
                # Tool results are represented as role=user with tool_result blocks.
                role = "user"
            content = m.get("content")
            if isinstance(content, str):
                content_blocks = [{"type": "text", "text": content}]
            elif isinstance(content, list):
                # Assume already in Anthropic content-block shape.
                content_blocks = content
            else:
                content_blocks = [{"type": "text", "text": str(content or "")}]
            out.append({"role": role, "content": content_blocks})
        return out

    async def chat(
        self,
        messages: list[dict[str, Any]],
        tools: list[dict[str, Any]],
        system: str | None = None,
        **kwargs: Any,
    ) -> Any:
        headers = {
            "x-api-key": self._api_key or "",
            "anthropic-version": "2023-06-01",
            "content-type": "application/json",
        }

        payload: dict[str, Any] = {
            "model": self._model,
            "max_tokens": self._max_tokens,
            "temperature": self._temperature,
            "messages": self._normalize_messages(messages),
        }
        if system:
            payload["system"] = system
        if self._top_p is not None:
            payload["top_p"] = self._top_p
        if tools:
            payload["tools"] = tools
        if self._stop_sequences:
            payload["stop_sequences"] = self._stop_sequences
        if self._thinking and getattr(self._thinking, "enabled", False):
            payload["thinking"] = {"type": "enabled", "budget_tokens": int(getattr(self._thinking, "budget_tokens", 1024))}

        url = "https://api.anthropic.com/v1/messages"
        async with httpx.AsyncClient(timeout=self._timeout) as client:
            resp = await client.post(url, headers=headers, json=payload)
            if resp.status_code >= 400:
                raise httpx.HTTPStatusError("Anthropic chat failed", request=resp.request, response=resp)
            return resp.json()

    def extract_tool_calls(self, response: Any) -> list[ToolCall]:
        content = []
        if isinstance(response, dict):
            content = response.get("content") or []
        tool_calls: list[ToolCall] = []
        for block in content:
            if not isinstance(block, dict):
                continue
            if block.get("type") == "tool_use":
                tool_use_id = str(block.get("id") or "")
                name = str(block.get("name") or "")
                args = block.get("input") or {}
                if isinstance(args, str):
                    try:
                        args = json.loads(args)
                    except Exception:
                        args = {}
                if not isinstance(args, dict):
                    args = {}
                if tool_use_id and name:
                    tool_calls.append(ToolCall(id=tool_use_id, name=name, arguments=args))
        return tool_calls

    def extract_text(self, response: Any) -> str:
        if not isinstance(response, dict):
            return ""
        content = response.get("content") or []
        texts: list[str] = []
        for block in content:
            if isinstance(block, dict) and block.get("type") == "text":
                text = block.get("text")
                if isinstance(text, str):
                    texts.append(text)
        return "".join(texts).strip()

    def is_done(self, response: Any) -> bool:
        return len(self.extract_tool_calls(response)) == 0

    def append_tool_results(self, messages: list[dict[str, Any]], response: Any, tool_results: list[ToolResult]) -> list[dict[str, Any]]:
        """Append assistant tool_use message + user tool_result blocks."""
        if not isinstance(response, dict):
            return messages

        # Preserve the assistant's content blocks that triggered the tool calls.
        assistant_content = response.get("content") or []
        if assistant_content:
            messages.append({"role": "assistant", "content": assistant_content})

        tool_result_blocks: list[dict[str, Any]] = []
        for tr in tool_results:
            tool_result_blocks.append(
                {"type": "tool_result", "tool_use_id": tr.tool_call_id, "content": tr.content}
            )

        if tool_result_blocks:
            messages.append({"role": "user", "content": tool_result_blocks})
        return messages

    def count_tokens(self, messages: list[dict[str, Any]], system: str) -> int:
        # Best-effort.
        return estimate_messages_tokens(messages, system, provider=self._provider)

