"""Tests for evaluation comparison logic."""

import pytest

from evalkit.server.comparison import SCORE_TOLERANCE, build_comparison


def _make_eval_data(eval_id, results, tags=None):
    """Helper to build the dict format that build_comparison expects."""
    return {
        "id": eval_id,
        "suite_name": "rag_qa",
        "average_score": sum(r["overall_score"] for r in results) / len(results) if results else 0,
        "created_at": f"2026-03-{eval_id:02d}T00:00:00",
        "tags": tags or {},
        "results": results,
    }


def _make_result(question, answer, overall_score, metrics=None):
    return {
        "question": question,
        "answer": answer,
        "context": ["some context"],
        "expected_answer": None,
        "overall_score": overall_score,
        "metrics": metrics
        or [{"name": "faithfulness", "score": overall_score, "passed": overall_score >= 0.5, "reason": "ok"}],
    }


def test_basic_comparison_two_evals():
    baseline = _make_eval_data(1, [_make_result("What is Python?", "A language", 0.8)])
    comparison = _make_eval_data(2, [_make_result("What is Python?", "A programming language", 0.95)])

    result = build_comparison(baseline, [comparison])

    assert result["baseline"]["id"] == 1
    assert len(result["comparisons"]) == 1
    assert len(result["questions"]) == 1

    q = result["questions"][0]
    assert q["question"] == "What is Python?"
    assert q["baseline_result"]["overall_score"] == 0.8
    assert len(q["comparison_results"]) == 1
    assert q["comparison_results"][0]["overall_score"] == 0.95
    assert q["comparison_results"][0]["score_delta"] == pytest.approx(0.15)


def test_comparison_per_metric_delta():
    baseline = _make_eval_data(
        1,
        [
            _make_result(
                "Q?",
                "A",
                0.7,
                [
                    {"name": "faithfulness", "score": 0.6, "passed": True, "reason": "ok"},
                    {"name": "relevancy", "score": 0.8, "passed": True, "reason": "ok"},
                ],
            )
        ],
    )
    comp = _make_eval_data(
        2,
        [
            _make_result(
                "Q?",
                "A",
                0.9,
                [
                    {"name": "faithfulness", "score": 0.9, "passed": True, "reason": "better"},
                    {"name": "relevancy", "score": 0.9, "passed": True, "reason": "better"},
                ],
            )
        ],
    )

    result = build_comparison(baseline, [comp])
    metrics = result["questions"][0]["comparison_results"][0]["metrics"]
    faith = next(m for m in metrics if m["name"] == "faithfulness")
    assert faith["score_delta"] == pytest.approx(0.3)


def test_question_matching_case_insensitive():
    baseline = _make_eval_data(1, [_make_result("What is Python?", "A", 0.8)])
    comp = _make_eval_data(2, [_make_result("  what is python?  ", "B", 0.9)])

    result = build_comparison(baseline, [comp])
    assert len(result["questions"]) == 1
    assert result["questions"][0]["comparison_results"][0]["score_delta"] == pytest.approx(0.1)


def test_question_in_baseline_not_in_comparison():
    baseline = _make_eval_data(
        1,
        [
            _make_result("Q1?", "A1", 0.8),
            _make_result("Q2?", "A2", 0.7),
        ],
    )
    comp = _make_eval_data(2, [_make_result("Q1?", "A1b", 0.9)])

    result = build_comparison(baseline, [comp])
    q2 = next(q for q in result["questions"] if q["question"] == "Q2?")
    assert q2["comparison_results"][0] is None


def test_question_in_comparison_not_in_baseline():
    baseline = _make_eval_data(1, [_make_result("Q1?", "A1", 0.8)])
    comp = _make_eval_data(
        2,
        [
            _make_result("Q1?", "A1b", 0.9),
            _make_result("Q3?", "A3", 0.6),
        ],
    )

    result = build_comparison(baseline, [comp])
    assert any(q["question"] == "Q3?" for q in result["new_questions"])


def test_summary_improved_regressed_unchanged():
    baseline = _make_eval_data(
        1,
        [
            _make_result("Q1?", "A", 0.5),
            _make_result("Q2?", "A", 0.9),
            _make_result("Q3?", "A", 0.7),
        ],
    )
    comp = _make_eval_data(
        2,
        [
            _make_result("Q1?", "A", 0.8),  # improved (+0.3)
            _make_result("Q2?", "A", 0.6),  # regressed (-0.3)
            _make_result("Q3?", "A", 0.72),  # unchanged (within tolerance)
        ],
    )

    result = build_comparison(baseline, [comp])
    assert result["summary"]["improved"] == 1
    assert result["summary"]["regressed"] == 1
    assert result["summary"]["unchanged"] == 1


def test_score_tolerance():
    assert SCORE_TOLERANCE == 0.05


def test_multiple_comparisons():
    baseline = _make_eval_data(1, [_make_result("Q?", "A", 0.5)])
    comp1 = _make_eval_data(2, [_make_result("Q?", "A", 0.8)])
    comp2 = _make_eval_data(3, [_make_result("Q?", "A", 0.3)])

    result = build_comparison(baseline, [comp1, comp2])
    assert len(result["comparisons"]) == 2
    q = result["questions"][0]
    assert len(q["comparison_results"]) == 2
    assert q["comparison_results"][0]["score_delta"] == pytest.approx(0.3)
    assert q["comparison_results"][1]["score_delta"] == pytest.approx(-0.2)
