"""tests/test_verification_nli_grounding.py — Unit tests for NLIGroundingVerifier."""
import pytest
import numpy as np

pytestmark = pytest.mark.unit

STEPS = [
    {"thought": "searching", "action_type": "Search",
     "observation": "Paris is the capital city of France, with a population of over 2 million."},
]


class TestImport:
    def test_import(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        assert NLIGroundingVerifier is not None


class TestInstantiation:
    def test_default_construction(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        assert not v.is_loaded

    def test_model_name_stored(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier(model_name="cross-encoder/nli-deberta-v3-small")
        assert "deberta" in v.model_name


class TestKeyObservationExtraction:
    def test_extracts_longest(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        steps = [
            {"observation": "Short obs."},
            {"observation": "This is a much longer observation with many more words that provides more content."},
        ]
        obs = v._extract_key_observation(steps)
        assert "longer" in obs

    def test_empty_steps_returns_empty(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        assert v._extract_key_observation([]) == ""

    def test_short_obs_filtered(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        steps = [{"observation": "Hi."}]  # < 20 chars
        obs = v._extract_key_observation(steps)
        assert obs == ""

    def test_output_field_fallback(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        steps = [{"output": "The answer is found in this longer observation text."}]
        obs = v._extract_key_observation(steps)
        assert len(obs) > 0


class TestScoresToRisk:
    def test_entailment_dominant(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        v._label_order = ["contradiction", "entailment", "neutral"]
        # High entailment → low risk
        risk = v._scores_to_risk([0.0, 5.0, 0.0])  # logits → softmax → high entailment
        assert risk < 0.5

    def test_contradiction_dominant(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        v._label_order = ["contradiction", "entailment", "neutral"]
        # High contradiction → high risk
        risk = v._scores_to_risk([5.0, 0.0, 0.0])
        assert risk > 0.5

    def test_already_probabilities(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        v._label_order = ["contradiction", "entailment", "neutral"]
        risk = v._scores_to_risk([0.0, 1.0, 0.0])
        assert risk == pytest.approx(0.20, abs=0.05)


class TestScoreNoModel:
    def test_score_returns_half_no_model(self):
        """When model cannot load, score() should return 0.5."""
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        # _get_model will fail gracefully if sentence_transformers unavailable
        # Patch to simulate no model
        v._model = None
        from unittest.mock import patch
        with patch.object(v, "_get_model", return_value=None):
            score = v.score("Q", STEPS, "Paris")
        assert score == 0.5

    def test_score_batch_returns_halves_no_model(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        chains = [{"question": "Q", "steps": STEPS, "final_answer": "Paris"}]
        from unittest.mock import patch
        with patch.object(v, "_get_model", return_value=None):
            scores = v.score_batch(chains)
        assert scores == [0.5]

    def test_no_observation_returns_half(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        score = v.score("Q", [], "Some answer")  # empty steps → no obs
        assert score == 0.5

    def test_repr(self):
        from llm_guard.verification.nli_grounding import NLIGroundingVerifier
        v = NLIGroundingVerifier()
        r = repr(v)
        assert "NLIGroundingVerifier" in r
