"""Tests for HallucinationMetric."""

from __future__ import annotations

from evalkit.metrics.hallucination import HallucinationMetric
from evalkit.models import MetricScore
from evalkit.models import TestCase as EvalTestCase


class TestHallucinationMetric:
    def test_name(self) -> None:
        assert HallucinationMetric.name == "hallucination"

    def test_default_threshold_is_0_7(self) -> None:
        """Hallucination uses a stricter threshold of 0.7."""
        metric = HallucinationMetric()
        assert metric.threshold == 0.7

    def test_custom_threshold(self) -> None:
        metric = HallucinationMetric(threshold=0.9)
        assert metric.threshold == 0.9

    # ------------------------------------------------------------------
    # Prompt construction
    # ------------------------------------------------------------------

    def test_prompt_contains_question(self, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        prompt = metric._build_prompt(sample_test_case)
        assert sample_test_case.question in prompt

    def test_prompt_contains_answer(self, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        prompt = metric._build_prompt(sample_test_case)
        assert sample_test_case.answer in prompt

    def test_prompt_contains_context_chunks(self, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        prompt = metric._build_prompt(sample_test_case)
        for chunk in sample_test_case.context:
            assert chunk in prompt

    def test_prompt_numbers_context_chunks(self, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        prompt = metric._build_prompt(sample_test_case)
        assert "[1]" in prompt

    # ------------------------------------------------------------------
    # score() result
    # ------------------------------------------------------------------

    def test_score_returns_metric_score(self, mock_judge, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        result = metric.score(sample_test_case, mock_judge)
        assert isinstance(result, MetricScore)

    def test_score_metric_name(self, mock_judge, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        result = metric.score(sample_test_case, mock_judge)
        assert result.metric_name == "hallucination"

    def test_score_above_threshold_passes(self, sample_test_case: EvalTestCase) -> None:
        from tests.conftest import MockJudge

        judge = MockJudge(score=0.9, reason="No hallucination detected")
        metric = HallucinationMetric()
        result = metric.score(sample_test_case, judge)
        assert result.passed is True

    def test_score_below_threshold_fails(self, sample_test_case: EvalTestCase) -> None:
        """Score of 0.6 is below the 0.7 threshold — should fail."""
        from tests.conftest import MockJudge

        judge = MockJudge(score=0.6, reason="Some unverified claims")
        metric = HallucinationMetric()
        result = metric.score(sample_test_case, judge)
        assert result.passed is False

    def test_score_exactly_at_threshold_passes(self, sample_test_case: EvalTestCase) -> None:
        """Score exactly equal to threshold should pass (>=)."""
        from tests.conftest import MockJudge

        judge = MockJudge(score=0.7, reason="Borderline")
        metric = HallucinationMetric()
        result = metric.score(sample_test_case, judge)
        assert result.passed is True

    def test_score_calls_judge_once(self, mock_judge, sample_test_case: EvalTestCase) -> None:
        metric = HallucinationMetric()
        metric.score(sample_test_case, mock_judge)
        assert mock_judge.call_count == 1
