"""Tests that log_api_response is called before FatalEvalError raises.

Verifies the anthropic provider logs structured error events for fatal
errors (402, 403, 404, 401) so the team has visibility into failures.

Run with: uv run pytest tests/test_anthropic_error_logging.py -v
"""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from wafer_core.rollouts.dtypes import Endpoint, Message, Trajectory, Actor


def _make_actor(*, model: str = "claude-sonnet-4-20250514", api_base: str = "") -> Actor:
    """Build a minimal Actor with proper typed fields."""
    endpoint = Endpoint(
        provider="anthropic",
        model=model,
        api_base=api_base,
        api_key="test-key",
    )
    trajectory = Trajectory(
        messages=[Message(role="user", content="hello")],
    )
    return Actor(trajectory=trajectory, endpoint=endpoint)


class TestFatalErrorLogging:
    """log_api_response must be called before every FatalEvalError raise."""

    @pytest.mark.trio
    async def test_402_payment_required_logs(self) -> None:
        """402 Payment Required logs error_type='payment_required'."""
        import anthropic as anthropic_lib

        from wafer_core.rollouts.providers.anthropic import rollout_anthropic
        from wafer_core.rollouts.providers.base import FatalEvalError

        actor = _make_actor()
        mock_response = MagicMock()
        mock_response.status_code = 402
        error = anthropic_lib.APIStatusError(
            message="No credits", response=mock_response, body=None,
        )

        mock_client = AsyncMock()
        mock_client.messages = MagicMock()
        mock_client.messages.stream = MagicMock(side_effect=error)

        with patch("wafer_core.rollouts.providers.anthropic._create_anthropic_client", return_value=mock_client):
            with patch("wafer_core.rollouts.providers.base.log_api_response") as mock_log:
                with pytest.raises(FatalEvalError, match="No credits"):
                    await rollout_anthropic(actor, AsyncMock())

        mock_log.assert_called()
        logged = mock_log.call_args[1]
        assert logged["success"] is False
        assert logged["error_type"] == "payment_required"

    @pytest.mark.trio
    async def test_403_wafer_proxy_logs(self) -> None:
        """403 from wafer proxy logs error_type='permission_denied'."""
        import anthropic as anthropic_lib

        from wafer_core.rollouts.providers.anthropic import rollout_anthropic
        from wafer_core.rollouts.providers.base import FatalEvalError

        actor = _make_actor(api_base="https://www.api.wafer.ai/v1/anthropic")
        mock_response = MagicMock()
        mock_response.status_code = 403
        error = anthropic_lib.PermissionDeniedError(
            message="Forbidden", response=mock_response, body=None,
        )

        mock_client = AsyncMock()
        mock_client.messages = MagicMock()
        mock_client.messages.stream = MagicMock(side_effect=error)

        with patch("wafer_core.rollouts.providers.anthropic._create_anthropic_client", return_value=mock_client):
            with patch("wafer_core.rollouts.providers.base.log_api_response") as mock_log:
                with pytest.raises(FatalEvalError, match="wafer credits"):
                    await rollout_anthropic(actor, AsyncMock())

        mock_log.assert_called()
        logged = mock_log.call_args[1]
        assert logged["success"] is False
        assert logged["error_type"] == "permission_denied"

    @pytest.mark.trio
    async def test_404_model_not_found_logs(self) -> None:
        """404 Not Found logs error_type='model_not_found'."""
        import anthropic as anthropic_lib

        from wafer_core.rollouts.providers.anthropic import rollout_anthropic
        from wafer_core.rollouts.providers.base import FatalEvalError

        actor = _make_actor(model="nonexistent-model")
        mock_response = MagicMock()
        mock_response.status_code = 404
        error = anthropic_lib.NotFoundError(
            message="Not found", response=mock_response, body=None,
        )

        mock_client = AsyncMock()
        mock_client.messages = MagicMock()
        mock_client.messages.stream = MagicMock(side_effect=error)

        with patch("wafer_core.rollouts.providers.anthropic._create_anthropic_client", return_value=mock_client):
            with patch("wafer_core.rollouts.providers.base.log_api_response") as mock_log:
                with pytest.raises(FatalEvalError, match="Model not found"):
                    await rollout_anthropic(actor, AsyncMock())

        mock_log.assert_called()
        logged = mock_log.call_args[1]
        assert logged["success"] is False
        assert logged["error_type"] == "model_not_found"

    @pytest.mark.trio
    async def test_401_auth_failure_logs(self) -> None:
        """401 Authentication failure logs error_type='authentication_failed'."""
        import anthropic as anthropic_lib

        from wafer_core.rollouts.providers.anthropic import rollout_anthropic
        from wafer_core.rollouts.providers.base import FatalEvalError

        actor = _make_actor()
        mock_response = MagicMock()
        mock_response.status_code = 401
        error = anthropic_lib.AuthenticationError(
            message="invalid x-api-key", response=mock_response, body=None,
        )

        mock_client = AsyncMock()
        mock_client.messages = MagicMock()
        mock_client.messages.stream = MagicMock(side_effect=error)

        with patch("wafer_core.rollouts.providers.anthropic._create_anthropic_client", return_value=mock_client):
            with patch("wafer_core.rollouts.providers.base.log_api_response") as mock_log:
                with pytest.raises(FatalEvalError, match="Authentication failed"):
                    await rollout_anthropic(actor, AsyncMock())

        mock_log.assert_called()
        logged = mock_log.call_args[1]
        assert logged["success"] is False
        assert logged["error_type"] == "authentication_failed"
