from __future__ import annotations

import json
import os
from typing import Any

import anthropic

from nexus.protocols.providers.base import AbstractProvider, LLMRequest, LLMResponse, ToolCall
from nexus.resilience.errors import ProviderError


class AnthropicProvider(AbstractProvider):
    """Anthropic SDK-backed provider."""

    def __init__(self, model: str = "claude-opus-4-6", api_key: str | None = None) -> None:
        self._model = model
        self._api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
        self._client = anthropic.AsyncAnthropic(api_key=self._api_key)

    async def complete(self, request: LLMRequest) -> LLMResponse:
        """Run Anthropic messages API and normalize output."""
        tools = [
            {
                "name": t.name,
                "description": t.description,
                "input_schema": t.parameters,
            }
            for t in request.tools
        ]
        msgs = [{"role": m.role, "content": m.content} for m in request.messages if m.role != "system"]

        try:
            resp = await self._client.messages.create(
                model=self._model,
                messages=msgs,
                system=request.system,
                max_tokens=request.max_tokens,
                temperature=request.temperature,
                tools=tools or None,
                stop_sequences=request.stop_sequences or None,
            )
        except Exception as exc:
            raise ProviderError(f"Anthropic call failed: {exc}", provider_name="anthropic") from exc

        content_parts: list[str] = []
        calls: list[ToolCall] = []
        for block in resp.content:
            btype = getattr(block, "type", "")
            if btype == "text":
                content_parts.append(getattr(block, "text", ""))
            elif btype == "tool_use":
                calls.append(
                    ToolCall(
                        tool_call_id=getattr(block, "id", ""),
                        tool_name=getattr(block, "name", ""),
                        arguments=dict(getattr(block, "input", {}) or {}),
                    )
                )

        usage = resp.usage
        return LLMResponse(
            content="\n".join(content_parts).strip(),
            tool_calls=calls,
            input_tokens=int(getattr(usage, "input_tokens", 0) or 0),
            output_tokens=int(getattr(usage, "output_tokens", 0) or 0),
            model=self._model,
            finish_reason=str(getattr(resp, "stop_reason", "stop") or "stop"),
            raw=json.loads(resp.model_dump_json()),
        )

    def count_tokens(self, text: str) -> int:
        """Approximate token count as utf-8 bytes divided by four."""
        return len(text.encode("utf-8")) // 4

    @property
    def model_name(self) -> str:
        return self._model

    @property
    def provider_name(self) -> str:
        return "anthropic"

    @property
    def context_window(self) -> int:
        return {
            "claude-3-opus": 200000,
            "claude-3-5-sonnet": 200000,
            "claude-opus-4-6": 200000,
            "claude-sonnet-4-6": 200000,
        }.get(self._model, 200000)
