"""
Anthropic provider adapter.

Translates between the unified OpenAI-compatible format and the
Anthropic Messages API format. Key differences:

- System prompt is a top-level field, not a message
- Response uses content[].text blocks
- Auth header: x-api-key instead of Authorization: Bearer
- Streaming events: message_start / content_block_delta / message_stop
"""

from __future__ import annotations

import json
from typing import Any, Dict, List, Tuple

from proxy.adapters.base import BaseProviderAdapter
from proxy.pricing import ANTHROPIC_PRICING, ZERO_PRICING
from proxy.schemas import (
    ChatCompletionChunk,
    ChatCompletionRequest,
    ChatCompletionResponse,
    ChatMessage,
    Choice,
    ChoiceMessage,
    StreamChoice,
    StreamDelta,
    TokenPricing,
    Usage,
)


class AnthropicAdapter(BaseProviderAdapter):
    """Adapter for the Anthropic Messages API.

    Handles bidirectional translation between OpenAI format and
    Anthropic's Messages format.
    """

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

    # ----- request translation -----

    def translate_request(
        self,
        request: ChatCompletionRequest,
        api_key: str,
    ) -> Tuple[str, Dict[str, str], Dict[str, Any]]:
        url = f"{self.base_url}/v1/messages"

        headers = {
            "Content-Type": "application/json",
            "x-api-key": api_key,
            "anthropic-version": "2023-06-01",
        }

        # Extract system prompt (OpenAI puts it as first message with role="system")
        system_text = None
        messages: List[Dict[str, str]] = []
        for msg in request.messages:
            if msg.role == "system":
                system_text = msg.content
            else:
                messages.append(
                    {
                        "role": msg.role,
                        "content": msg.content or "",
                    }
                )

        body: Dict[str, Any] = {
            "model": request.model,
            "messages": messages,
            "max_tokens": request.max_tokens or request.max_completion_tokens or 4096,
        }

        if system_text:
            body["system"] = system_text

        if request.temperature is not None:
            body["temperature"] = request.temperature
        if request.top_p is not None:
            body["top_p"] = request.top_p
        if request.stop is not None:
            body["stop_sequences"] = (
                request.stop if isinstance(request.stop, list) else [request.stop]
            )
        if request.stream:
            body["stream"] = True

        return url, headers, body

    # ----- response translation -----

    def translate_response(
        self,
        status_code: int,
        headers: Dict[str, str],
        body: Dict[str, Any],
    ) -> ChatCompletionResponse:
        # Anthropic response structure:
        # { "id": "...", "type": "message", "role": "assistant",
        #   "content": [{"type": "text", "text": "..."}],
        #   "usage": {"input_tokens": N, "output_tokens": M} }

        content_blocks = body.get("content", [])
        text_parts = []
        for block in content_blocks:
            if block.get("type") == "text":
                text_parts.append(block.get("text", ""))

        content = "\n".join(text_parts) if text_parts else None

        usage = body.get("usage", {})
        input_tokens = usage.get("input_tokens", 0)
        output_tokens = usage.get("output_tokens", 0)

        stop_reason = body.get("stop_reason", "end_turn")
        finish_reason_map = {
            "end_turn": "stop",
            "max_tokens": "length",
            "stop_sequence": "stop",
            "tool_use": "tool_calls",
        }

        return ChatCompletionResponse(
            id=body.get("id", ""),
            object="chat.completion",
            created=0,
            model=body.get("model", ""),
            choices=[
                Choice(
                    index=0,
                    message=ChoiceMessage(
                        role="assistant",
                        content=content,
                    ),
                    finish_reason=finish_reason_map.get(stop_reason, "stop"),
                )
            ],
            usage=Usage(
                prompt_tokens=input_tokens,
                completion_tokens=output_tokens,
                total_tokens=input_tokens + output_tokens,
            ),
        )

    # ----- streaming -----

    def parse_stream_chunk(self, raw_line: str) -> ChatCompletionChunk | None:
        line = raw_line.strip()
        if not line:
            return None

        try:
            data = json.loads(line)
        except json.JSONDecodeError:
            return None

        event_type = data.get("type", "")

        if event_type == "content_block_delta":
            delta = data.get("delta", {})
            if delta.get("type") == "text_delta":
                text = delta.get("text", "")
                return ChatCompletionChunk(
                    id=data.get("message", {}).get("id", ""),
                    model="",
                    choices=[
                        StreamChoice(
                            index=data.get("index", 0),
                            delta=StreamDelta(content=text),
                            finish_reason=None,
                        )
                    ],
                )

        elif event_type == "message_start":
            msg = data.get("message", {})
            return ChatCompletionChunk(
                id=msg.get("id", ""),
                model=msg.get("model", ""),
                choices=[
                    StreamChoice(
                        index=0,
                        delta=StreamDelta(role="assistant"),
                        finish_reason=None,
                    )
                ],
            )

        elif event_type == "message_delta":
            stop_reason = data.get("delta", {}).get("stop_reason")
            if stop_reason:
                return ChatCompletionChunk(
                    choices=[
                        StreamChoice(
                            index=0,
                            delta=StreamDelta(),
                            finish_reason=(
                                "stop" if stop_reason == "end_turn" else stop_reason
                            ),
                        )
                    ],
                )

        elif event_type == "message_stop":
            return None  # End of stream

        return None

    # ----- pricing -----

    def get_pricing(self, model: str) -> TokenPricing:
        return ANTHROPIC_PRICING.get(model, ZERO_PRICING)
