"""Tests for Long-MT-Bench+ adapter (#1281)."""
from __future__ import annotations

import json
import pytest

from cortex.eval.long_mt_bench import (
    LongMTBenchAdapter,
    LongMTBenchConfig,
    MTBenchConversation,
    MTBenchReport,
    MTBenchResult,
    MTBenchTurn,
    load_long_mt_bench,
)


class MockLLM:
    """Mock LLM that returns predictable responses."""
    def __init__(self, gen_text="This is a test response.", judge_json=None):
        self._gen_text = gen_text
        self._judge_json = judge_json or {"score": 7.5, "reasoning": "Good response"}

    async def complete(self, system="", user="", **kwargs):
        class Resp:
            pass
        r = Resp()
        if "json" in kwargs.get("response_format", ""):
            r.text = json.dumps(self._judge_json)
        else:
            r.text = self._gen_text
        return r


class TestMTBenchConversation:
    def test_num_turns_auto(self):
        conv = MTBenchConversation(
            conversation_id="test",
            history=[
                MTBenchTurn(role="user", content="hello"),
                MTBenchTurn(role="assistant", content="hi"),
            ],
            follow_up_question="how are you?",
        )
        assert conv.num_turns == 2


class TestLongMTBenchAdapter:
    @pytest.mark.asyncio
    async def test_evaluate_basic(self):
        adapter = LongMTBenchAdapter(llm=MockLLM())
        conversations = [
            MTBenchConversation(
                conversation_id="c1",
                history=[
                    MTBenchTurn(role="user", content="I like Python."),
                    MTBenchTurn(role="assistant", content="Python is great!"),
                ],
                follow_up_question="What Python frameworks do you recommend?",
                reference_answer="Django and Flask are popular choices.",
                category="coding",
            ),
        ]
        report = await adapter.evaluate(conversations)

        assert isinstance(report, MTBenchReport)
        assert report.metrics["total"] == 1
        assert report.metrics["avg_score"] == pytest.approx(7.5)
        assert len(report.results) == 1
        assert report.results[0].judge_score == pytest.approx(7.5)

    @pytest.mark.asyncio
    async def test_evaluate_multiple_categories(self):
        adapter = LongMTBenchAdapter(llm=MockLLM())
        conversations = [
            MTBenchConversation(
                conversation_id=f"c{i}",
                history=[MTBenchTurn(role="user", content=f"msg {i}")],
                follow_up_question=f"question {i}",
                category=cat,
            )
            for i, cat in enumerate(["coding", "reasoning", "coding"])
        ]
        report = await adapter.evaluate(conversations)

        assert report.metrics["total"] == 3
        assert "by_category" in report.metrics
        assert "coding" in report.metrics["by_category"]
        assert "reasoning" in report.metrics["by_category"]

    @pytest.mark.asyncio
    async def test_summary_output(self):
        adapter = LongMTBenchAdapter(llm=MockLLM())
        conversations = [
            MTBenchConversation(
                conversation_id="c1",
                history=[MTBenchTurn(role="user", content="hello")],
                follow_up_question="test",
            ),
        ]
        report = await adapter.evaluate(conversations)
        summary = report.summary()

        assert "Long-MT-Bench+" in summary
        assert "Average judge score" in summary

    @pytest.mark.asyncio
    async def test_to_dict(self):
        adapter = LongMTBenchAdapter(llm=MockLLM())
        conversations = [
            MTBenchConversation(
                conversation_id="c1",
                history=[MTBenchTurn(role="user", content="hello")],
                follow_up_question="test",
            ),
        ]
        report = await adapter.evaluate(conversations)
        d = report.to_dict()

        assert "metrics" in d
        assert "results" in d
        assert len(d["results"]) == 1

    @pytest.mark.asyncio
    async def test_generation_failure_handled(self):
        class FailingLLM:
            async def complete(self, **kwargs):
                raise RuntimeError("LLM down")

        adapter = LongMTBenchAdapter(llm=FailingLLM())
        conversations = [
            MTBenchConversation(
                conversation_id="c1",
                history=[MTBenchTurn(role="user", content="hello")],
                follow_up_question="test",
            ),
        ]
        report = await adapter.evaluate(conversations)
        assert report.results[0].error is not None


class TestLoadLongMTBench:
    def test_load_sample(self):
        conversations = load_long_mt_bench()
        assert len(conversations) == 3
        assert conversations[0].conversation_id == "mt_001"
        assert len(conversations[0].history) > 0
        assert conversations[0].follow_up_question != ""

    def test_load_nonexistent_raises(self):
        with pytest.raises(FileNotFoundError):
            load_long_mt_bench("/nonexistent/path.json")
