"""Tests for AnthropicProvider — mock-based, no real API calls."""

from __future__ import annotations

from dataclasses import dataclass
from unittest.mock import AsyncMock, MagicMock

from conftest import run_async

from arandu.protocols import LLMResult

# ---------------------------------------------------------------------------
# Mock helpers
# ---------------------------------------------------------------------------


@dataclass
class MockTextBlock:
    text: str
    type: str = "text"


@dataclass
class MockUsage:
    input_tokens: int = 0
    output_tokens: int = 0


def _make_mock_response(text: str, input_tokens: int = 100, output_tokens: int = 50) -> MagicMock:
    """Create a mock Anthropic messages.create response."""
    response = MagicMock()
    response.content = [MockTextBlock(text=text)]
    response.usage = MockUsage(input_tokens=input_tokens, output_tokens=output_tokens)
    return response


def _make_provider(mock_response: MagicMock) -> tuple:
    """Create an AnthropicProvider with mocked client. Returns (provider, mock_create)."""
    from arandu.providers.anthropic import AnthropicProvider

    # Create provider (will try to import anthropic — we mock at client level)
    provider = AnthropicProvider.__new__(AnthropicProvider)
    provider._model = "claude-sonnet-4-20250514"
    provider._timeout = 30.0
    provider._default_max_tokens = 4096

    mock_client = MagicMock()
    mock_create = AsyncMock(return_value=mock_response)
    mock_client.messages.create = mock_create
    provider._client = mock_client

    return provider, mock_create


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------


class TestAnthropicProvider:
    def test_system_message_separated(self):
        """System messages are extracted and passed via 'system' param."""
        response = _make_mock_response('{"result": "ok"}')
        provider, mock_create = _make_provider(response)

        messages = [
            {"role": "system", "content": "You are a knowledge extractor."},
            {"role": "user", "content": "Extract facts from this message."},
        ]

        run_async(provider.complete(messages))

        call_kwargs = mock_create.call_args[1]
        assert "system" in call_kwargs
        assert "knowledge extractor" in call_kwargs["system"]
        # System message should NOT be in the messages list
        for msg in call_kwargs["messages"]:
            assert msg["role"] != "system"

    def test_json_response_format_appends_instruction(self):
        """response_format=json_object appends JSON instruction to system prompt."""
        response = _make_mock_response('{"scores": [0.8]}')
        provider, mock_create = _make_provider(response)

        messages = [
            {"role": "system", "content": "Rate relevance."},
            {"role": "user", "content": "Query and facts here."},
        ]

        run_async(provider.complete(messages, response_format={"type": "json_object"}))

        call_kwargs = mock_create.call_args[1]
        system_text = call_kwargs["system"]
        assert "valid JSON only" in system_text
        assert "Rate relevance" in system_text  # original system preserved

    def test_token_usage_extracted(self):
        """Usage from Anthropic response is mapped to TokenUsage."""
        response = _make_mock_response('{"ok": true}', input_tokens=150, output_tokens=75)
        provider, _ = _make_provider(response)

        result = run_async(provider.complete([{"role": "user", "content": "test"}]))

        assert isinstance(result, LLMResult)
        assert result.usage is not None
        assert result.usage.input_tokens == 150
        assert result.usage.output_tokens == 75
        assert result.usage.total_tokens == 225

    def test_markdown_fences_stripped(self):
        """Markdown code fences are stripped from response content."""
        response = _make_mock_response('```json\n{"entities": []}\n```')
        provider, _ = _make_provider(response)

        result = run_async(provider.complete([{"role": "user", "content": "test"}]))

        assert result.text == '{"entities": []}'
        assert "```" not in result.text
