"""Tests for FaithfulnessMetric."""

from __future__ import annotations

import pytest

from evalkit.metrics.faithfulness import FaithfulnessMetric
from evalkit.models import MetricScore
from evalkit.models import TestCase as EvalTestCase


class TestFaithfulnessMetric:
    def test_name(self) -> None:
        assert FaithfulnessMetric.name == "faithfulness"

    def test_default_threshold(self) -> None:
        metric = FaithfulnessMetric()
        assert metric.threshold == 0.5

    def test_custom_threshold(self) -> None:
        metric = FaithfulnessMetric(threshold=0.8)
        assert metric.threshold == 0.8

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

    def test_prompt_contains_question(self, sample_test_case: EvalTestCase) -> None:
        metric = FaithfulnessMetric()
        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 = FaithfulnessMetric()
        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 = FaithfulnessMetric()
        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 = FaithfulnessMetric()
        prompt = metric._build_prompt(sample_test_case)
        assert "[1]" in prompt

    def test_prompt_with_multiple_context_chunks(self) -> None:
        test_case = EvalTestCase(
            question="What is 2+2?",
            context=["First chunk.", "Second chunk."],
            answer="Four.",
        )
        metric = FaithfulnessMetric()
        prompt = metric._build_prompt(test_case)
        assert "[1]" in prompt
        assert "[2]" in prompt

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

    def test_score_returns_metric_score(self, mock_judge, sample_test_case: EvalTestCase) -> None:
        metric = FaithfulnessMetric()
        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 = FaithfulnessMetric()
        result = metric.score(sample_test_case, mock_judge)
        assert result.metric_name == "faithfulness"

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

        judge = MockJudge(score=0.9, reason="All claims supported by context")
        metric = FaithfulnessMetric()
        result = metric.score(sample_test_case, judge)
        assert result.score == pytest.approx(0.9)
        assert result.reason == "All claims supported by context"
        assert result.passed is True

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

        judge = MockJudge(score=0.2, reason="Answer hallucinates facts")
        metric = FaithfulnessMetric()
        result = metric.score(sample_test_case, judge)
        assert result.score == pytest.approx(0.2)
        assert result.passed is False

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

    def test_score_passes_correct_prompt_to_judge(self, mock_judge, sample_test_case: EvalTestCase) -> None:
        metric = FaithfulnessMetric()
        metric.score(sample_test_case, mock_judge)
        assert sample_test_case.question in mock_judge.last_prompt
        assert sample_test_case.answer in mock_judge.last_prompt
