import os
import anthropic
from anthropic import AsyncAnthropic

from .base import LLMProvider, ContextLengthExceededError, ProviderAuthError, ProviderConnectionError

# Safe default for max output tokens; configurable in Phase 3
_DEFAULT_MAX_OUTPUT_TOKENS = 8192

class AnthropicProvider(LLMProvider):
    """LLM provider implementation for Anthropic Claude models."""
    _client: AsyncAnthropic | None = None

    def _get_client(self) -> AsyncAnthropic:
        if AnthropicProvider._client is None:
            if not (api_key := os.environ.get('TELLMGRAMBOT_ANTHROPIC_API_KEY')):
                raise ProviderAuthError("Anthropic API key not set. Provide TELLMGRAMBOT_ANTHROPIC_API_KEY.")
            AnthropicProvider._client = AsyncAnthropic(api_key=api_key)
        return AnthropicProvider._client

    async def complete(self, model: str, messages: list[dict], tools: list[dict] | None = None) -> "str | ToolCall":
        from .base import ToolCall
        import json
        client = self._get_client()

        # Anthropic takes system prompt as a separate parameter, not in the messages list
        system = None
        chat_messages = []
        for msg in messages:
            if msg['role'] == 'system':
                system = msg['content']
            else:
                chat_messages.append(msg)

        kwargs = {
            'model': model,
            'max_tokens': _DEFAULT_MAX_OUTPUT_TOKENS,
            'messages': chat_messages,
        }
        if system:
            kwargs['system'] = system
        if tools:
            kwargs['tools'] = [
                {"name": t["name"], "description": t["description"], "input_schema": t["parameters"]}
                for t in tools
            ]

        try:
            response = await client.messages.create(**kwargs)
            if response.stop_reason == 'tool_use':
                for block in response.content:
                    if block.type == 'tool_use':
                        return ToolCall(
                            name=block.name,
                            arguments=block.input if isinstance(block.input, dict) else json.loads(block.input),
                            tool_call_id=block.id,
                        )
            if not response.content or response.content[0].type != 'text':
                return ""
            return response.content[0].text
        except anthropic.AuthenticationError as e:
            raise ProviderAuthError(f"Anthropic authentication failed: {e}") from e
        except anthropic.BadRequestError as e:
            if 'too long' in str(e).lower() or 'context' in str(e).lower():
                raise ContextLengthExceededError() from e
            raise
        except anthropic.APIConnectionError as e:
            raise ProviderConnectionError(f"Anthropic connection error: {e}") from e

    async def count_tokens(self, model: str, messages: list[dict]) -> int:
        """
        Count tokens in messages for a Claude model using the Anthropic SDK.

        Uses the Anthropic beta token counting API (token-counting-2024-11-01), which accounts
        for the full context including system prompt. System messages are extracted from the
        messages list and passed separately per the Anthropic API design.

        Returns 0 immediately if no chat messages remain after system extraction (e.g., new user
        first message or immediately after /forget). This avoids a 400 "messages: at least one
        message is required" error from the Anthropic token counting API.

        Args:
            model: The Anthropic model name (e.g., 'claude-sonnet-4-6', 'claude-haiku-4-5').
            messages: List of message dictionaries with 'role' and 'content' keys. May include
                      a 'system' role message.

        Returns:
            The input token count for the messages, as reported by the Anthropic API, or 0 if
            chat_messages is empty after system extraction.
        """
        client = self._get_client()
        system = None
        chat_messages = []
        for msg in messages:
            if msg['role'] == 'system':
                system = msg['content']
            else:
                chat_messages.append(msg)
        if not chat_messages:
            return 0
        kwargs = {'model': model, 'messages': chat_messages}
        if system:
            kwargs['system'] = system
        response = await client.beta.messages.count_tokens(**kwargs, betas=["token-counting-2024-11-01"])
        return response.input_tokens
