"""Tests for QueryTrace and TurnTrace data model."""

from __future__ import annotations

import pytest

from openjarvis.evals.core.trace import QueryTrace, TurnTrace


class TestTurnTrace:
    def test_defaults(self):
        t = TurnTrace(turn_index=0)
        assert t.input_tokens == 0
        assert t.output_tokens == 0
        assert t.tools_called == []
        assert t.gpu_energy_joules is None
        assert t.cost_usd is None

    def test_to_dict_roundtrip(self):
        t = TurnTrace(
            turn_index=1,
            input_tokens=100,
            output_tokens=50,
            tools_called=["calculator"],
            tool_latencies_s={"calculator": 0.5},
            wall_clock_s=1.2,
            gpu_energy_joules=10.0,
            cost_usd=0.001,
        )
        d = t.to_dict()
        t2 = TurnTrace.from_dict(d)
        assert t2.turn_index == 1
        assert t2.input_tokens == 100
        assert t2.output_tokens == 50
        assert t2.tools_called == ["calculator"]
        assert t2.tool_latencies_s == {"calculator": 0.5}
        assert t2.wall_clock_s == pytest.approx(1.2)
        assert t2.gpu_energy_joules == pytest.approx(10.0)
        assert t2.cost_usd == pytest.approx(0.001)

    def test_from_dict_missing_keys(self):
        d = {"turn_index": 0}
        t = TurnTrace.from_dict(d)
        assert t.input_tokens == 0
        assert t.tools_called == []
        assert t.error is None


class TestQueryTrace:
    def _make_trace(self, **kwargs):
        defaults = {
            "query_id": "q0001",
            "workload_type": "coding",
            "query_text": "Hello",
            "response_text": "World",
            "turns": [
                TurnTrace(
                    turn_index=0,
                    input_tokens=100,
                    output_tokens=50,
                    wall_clock_s=1.0,
                    gpu_energy_joules=5.0,
                    cost_usd=0.01,
                ),
                TurnTrace(
                    turn_index=1,
                    input_tokens=150,
                    output_tokens=75,
                    wall_clock_s=1.5,
                    gpu_energy_joules=7.5,
                    cost_usd=0.015,
                ),
            ],
            "total_wall_clock_s": 2.5,
            "completed": True,
        }
        defaults.update(kwargs)
        return QueryTrace(**defaults)

    def test_num_turns(self):
        t = self._make_trace()
        assert t.num_turns == 2

    def test_total_tokens(self):
        t = self._make_trace()
        assert t.total_input_tokens == 250
        assert t.total_output_tokens == 125
        assert t.total_tokens == 375

    def test_total_gpu_energy(self):
        t = self._make_trace()
        assert t.total_gpu_energy_joules == pytest.approx(12.5)

    def test_total_gpu_energy_fallback(self):
        t = self._make_trace(
            turns=[TurnTrace(turn_index=0)],
            query_gpu_energy_joules=20.0,
        )
        assert t.total_gpu_energy_joules == pytest.approx(20.0)

    def test_total_cost(self):
        t = self._make_trace()
        assert t.total_cost_usd == pytest.approx(0.025)

    def test_total_cost_none(self):
        t = self._make_trace(turns=[TurnTrace(turn_index=0)])
        assert t.total_cost_usd is None

    def test_throughput(self):
        t = self._make_trace()
        assert t.throughput_tokens_per_sec == pytest.approx(125 / 2.5)

    def test_energy_per_token(self):
        t = self._make_trace()
        assert t.energy_per_token_joules == pytest.approx(12.5 / 125)

    def test_avg_gpu_power(self):
        t = self._make_trace()
        # No per-turn power set, so falls back to query-level
        assert t.avg_gpu_power_watts is None

    def test_to_dict_roundtrip(self):
        t = self._make_trace(is_resolved=True)
        d = t.to_dict()
        t2 = QueryTrace.from_dict(d)
        assert t2.query_id == "q0001"
        assert t2.workload_type == "coding"
        assert t2.num_turns == 2
        assert t2.completed is True
        assert t2.is_resolved is True
        assert t2.total_input_tokens == 250

    def test_save_load_jsonl(self, tmp_path):
        t1 = self._make_trace(query_id="q0001")
        t2 = self._make_trace(query_id="q0002")
        path = tmp_path / "traces.jsonl"
        t1.save_jsonl(path)
        t2.save_jsonl(path)
        loaded = QueryTrace.load_jsonl(path)
        assert len(loaded) == 2
        assert loaded[0].query_id == "q0001"
        assert loaded[1].query_id == "q0002"

    def test_tool_call_count(self):
        t = self._make_trace(
            turns=[
                TurnTrace(turn_index=0, tools_called=["calc", "search"]),
                TurnTrace(turn_index=1, tools_called=["read"]),
            ]
        )
        assert t.total_tool_calls == 3
