"""
Tests for ChatGuard.score_chat_with_judge() — P(True) style 1-5 judge path.

Tests mock the Anthropic client to avoid API calls.
"""
from __future__ import annotations

import pytest
from unittest.mock import MagicMock, patch

from llm_guard.chat_guard import ChatGuard, ChatResult


def _make_mock_client(reply: str):
    """Build a mock anthropic client that returns the given reply text."""
    mock_msg = MagicMock()
    mock_msg.content[0].text = reply
    mock_client = MagicMock()
    mock_client.messages.create.return_value = mock_msg
    return mock_client


class TestScoreChatWithJudge:
    """Unit tests for score_chat_with_judge()."""

    def test_rating_5_gives_low_risk(self):
        """Haiku rating=5 (excellent) should give low final risk."""
        guard = ChatGuard()
        guard._client = _make_mock_client("5")
        result = guard.score_chat_with_judge(
            "What is the capital of France?",
            "The capital of France is Paris.",
            blend_native=False,
        )
        assert isinstance(result, ChatResult)
        assert result.trace_risk == pytest.approx(0.0, abs=1e-6), (
            f"Expected judge_risk=0.0 for rating=5, got {result.trace_risk}"
        )
        assert result.risk_score == pytest.approx(0.0, abs=1e-6)
        assert result.confidence_tier == "HIGH"

    def test_rating_1_gives_high_risk(self):
        """Haiku rating=1 (wrong) should give high final risk."""
        guard = ChatGuard()
        guard._client = _make_mock_client("1")
        result = guard.score_chat_with_judge(
            "What is the capital of France?",
            "The capital of France is Berlin.",
            blend_native=False,
        )
        assert result.trace_risk == pytest.approx(1.0, abs=1e-6)
        assert result.risk_score == pytest.approx(1.0, abs=1e-6)
        assert result.needs_alert is True

    def test_rating_3_gives_medium_risk(self):
        """Rating=3 should give risk=0.5 (blend_native=False)."""
        guard = ChatGuard()
        guard._client = _make_mock_client("3")
        result = guard.score_chat_with_judge(
            "Explain relativity.",
            "Relativity is about light speed.",
            blend_native=False,
        )
        assert result.trace_risk == pytest.approx(0.5, abs=1e-6)
        assert result.risk_score == pytest.approx(0.5, abs=1e-6)

    def test_blend_native_default(self):
        """Default blend_native=True should interpolate between native and judge."""
        guard = ChatGuard()
        guard._client = _make_mock_client("1")  # judge_risk = 1.0
        result = guard.score_chat_with_judge(
            "What is 2+2?",
            "4",
            blend_native=True,
            native_weight=0.30,
        )
        # judge_risk=1.0, native_risk likely low for clean short answer
        # final = 0.3*native + 0.7*1.0 ≥ 0.7
        assert result.trace_risk == pytest.approx(1.0, abs=1e-6)
        assert result.risk_score >= 0.50  # at least partially dominated by judge

    def test_rating_4_maps_correctly(self):
        """Rating=4 → risk=0.25."""
        guard = ChatGuard()
        guard._client = _make_mock_client("4")
        result = guard.score_chat_with_judge(
            "Who wrote Hamlet?",
            "Shakespeare wrote Hamlet.",
            blend_native=False,
        )
        assert result.trace_risk == pytest.approx(0.25, abs=1e-6)

    def test_rating_2_maps_correctly(self):
        """Rating=2 → risk=0.75."""
        guard = ChatGuard()
        guard._client = _make_mock_client("2")
        result = guard.score_chat_with_judge(
            "Who wrote Hamlet?",
            "Chaucer wrote Hamlet.",
            blend_native=False,
        )
        assert result.trace_risk == pytest.approx(0.75, abs=1e-6)

    def test_unexpected_reply_falls_back_to_neutral(self):
        """Non-digit reply should fall back to judge_risk=0.5 (neutral)."""
        guard = ChatGuard()
        guard._client = _make_mock_client("UNKNOWN")
        result = guard.score_chat_with_judge(
            "Test query.",
            "Test response.",
            blend_native=False,
        )
        # After 3 failed attempts, judge_risk stays at default 0.50
        assert result.trace_risk == pytest.approx(0.5, abs=1e-6)

    def test_result_has_native_features(self):
        """ChatResult must include native_features dict."""
        guard = ChatGuard()
        guard._client = _make_mock_client("5")
        result = guard.score_chat_with_judge("Hello?", "Hi.", blend_native=False)
        assert isinstance(result.native_features, dict)
        assert len(result.native_features) > 0

    def test_no_api_key_raises(self):
        """score_chat_with_judge without any API key should raise ValueError."""
        guard = ChatGuard()
        guard._api_key = ""
        guard._client = None
        with pytest.raises(ValueError, match="api_key"):
            guard.score_chat_with_judge("Question?", "Answer.", api_key=None)

    def test_risk_clamped_to_0_1(self):
        """Risk scores must be in [0, 1] regardless of native features."""
        guard = ChatGuard()
        guard._client = _make_mock_client("5")
        result = guard.score_chat_with_judge(
            "What is water?",
            "H2O",
            blend_native=True,
            native_weight=0.30,
        )
        assert 0.0 <= result.risk_score <= 1.0
