"""tests/test_scoring_nano.py — Unit tests for llm_guard.scoring.nano."""
import pytest
from unittest.mock import MagicMock, patch

pytestmark = pytest.mark.unit

STEPS = [
    {"thought": "search for answer", "action_type": "Search",
     "action_arg": "Who invented telephone?", "observation": "Alexander Graham Bell."},
    {"thought": "found it", "action_type": "Finish",
     "action_arg": "Alexander Graham Bell", "observation": ""},
]


class TestImport:
    def test_import(self):
        from llm_guard.scoring.nano import QPPGNano
        assert QPPGNano is not None


class TestInstantiation:
    def test_default_no_judge(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        assert not nano._judge

    def test_version_attribute(self):
        from llm_guard.scoring.nano import QPPGNano
        assert "nano" in QPPGNano.VERSION.lower()

    def test_custom_threshold(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano(alert_threshold=0.8)
        assert nano._thr == 0.8


class TestScoreChain:
    def test_returns_required_keys(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_chain("Who invented the telephone?", STEPS, "Alexander Graham Bell")
        assert "risk_score" in result
        assert "confidence_tier" in result
        assert "needs_alert" in result
        assert "failure_mode" in result
        assert "behavioral_score" in result
        assert "step_count" in result
        assert "latency_ms" in result

    def test_risk_score_range(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_chain("Q", STEPS, "A")
        assert 0.0 <= result["risk_score"] <= 1.0

    def test_confidence_tier_valid(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_chain("Q", STEPS, "A")
        assert result["confidence_tier"] in ("HIGH", "MEDIUM", "LOW")

    def test_step_count(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_chain("Q", STEPS, "A")
        assert result["step_count"] == len(STEPS)

    def test_needs_alert_bool(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_chain("Q", STEPS, "A")
        assert isinstance(result["needs_alert"], bool)

    def test_empty_steps(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_chain("Q", [], "A")
        assert "risk_score" in result

    def test_judge_not_called_without_key(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano(judge=True, api_key="")  # no key → judge skipped
        result = nano.score_chain("Q", STEPS, "A")
        # judge_label should be None since no key
        assert result["judge_label"] is None

    @patch("llm_guard.scoring.nano.QPPGNano._call_judge")
    def test_judge_called_with_key(self, mock_judge):
        from llm_guard.scoring.nano import QPPGNano
        mock_judge.return_value = (0.3, "GOOD")
        nano = QPPGNano(judge=True, api_key="sk-ant-fake")
        result = nano.score_chain("Q", STEPS, "A")
        mock_judge.assert_called_once()
        assert result["judge_label"] == "GOOD"


class TestScorePrefix:
    def test_returns_required_keys(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_prefix("Who?", [], "search query")
        assert "risk" in result
        assert "risk_score" in result
        assert "step" in result

    def test_risk_level_valid(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        result = nano.score_prefix("Who?", [], "search")
        assert result["risk"] in ("low", "medium", "high")


class TestFailureModeDetection:
    def test_long_chain_failure(self):
        from llm_guard.scoring.nano import QPPGNano
        nano = QPPGNano()
        long_steps = [{"thought": f"t{i}", "action_type": "Search",
                        "action_arg": f"q{i}", "observation": f"obs{i} some text here with enough tokens to pass"}
                       for i in range(8)]
        result = nano.score_chain("Q", long_steps, "A")
        # At 7+ steps with varied obs, long_chain may be detected
        assert result["failure_mode"] in (None, "long_chain", "repeated_query",
                                           "retrieval_fail", "no_evidence")
