from bandito.models import Evaluation, Span, Trace


class TestSpan:
    def test_defaults(self):
        s = Span(name="test", kind="llm")
        assert s.name == "test"
        assert s.kind == "llm"
        assert s.span_id
        assert s.parent_id is None
        assert s.status == "ok"

    def test_to_dict_minimal(self):
        s = Span(name="test", kind="llm")
        d = s.to_dict()
        assert d["name"] == "test"
        assert d["kind"] == "llm"
        assert "input" not in d
        assert "model" not in d

    def test_to_dict_llm_fields(self):
        s = Span(
            name="test",
            kind="llm",
            model="gpt-4o",
            provider="openai",
            input_tokens=100,
            output_tokens=50,
            cost=0.01,
        )
        d = s.to_dict()
        assert d["model"] == "gpt-4o"
        assert d["input_tokens"] == 100
        assert d["cost"] == 0.01

    def test_to_dict_omits_none_llm_fields(self):
        s = Span(name="test", kind="tool", input={"key": "val"})
        d = s.to_dict()
        assert "model" not in d
        assert "input_tokens" not in d
        assert d["input"] == {"key": "val"}


class TestTrace:
    def test_defaults(self):
        t = Trace(project="p")
        assert t.project == "p"
        assert t.trace_id
        assert t.spans == []
        assert t.status == "ok"

    def test_to_dict_minimal(self):
        t = Trace(project="p")
        d = t.to_dict()
        assert d["project"] == "p"
        assert d["spans"] == []
        assert "session_id" not in d

    def test_to_dict_full(self):
        t = Trace(
            project="p",
            input="hi",
            output="hello",
            session_id="s1",
            user_id="u1",
            metadata={"k": "v"},
            tags=["a", "b"],
        )
        t.spans.append(Span(name="s", kind="llm"))
        d = t.to_dict()
        assert d["input"] == "hi"
        assert d["session_id"] == "s1"
        assert d["user_id"] == "u1"
        assert d["metadata"] == {"k": "v"}
        assert d["tags"] == ["a", "b"]
        assert len(d["spans"]) == 1


class TestEvaluation:
    def test_defaults(self):
        e = Evaluation(trace_id="t1")
        assert e.trace_id == "t1"
        assert e.human_score is None
        assert e.judge_score is None

    def test_human_grade(self):
        e = Evaluation(trace_id="t1", human_score=1, human_comment="good")
        d = e.to_dict()
        assert d["trace_id"] == "t1"
        assert d["human_score"] == 1
        assert d["human_comment"] == "good"
        assert "judge_score" not in d  # None, omitted

    def test_judge_grade(self):
        e = Evaluation(trace_id="t1", judge_score=0, judge_critique="bad output")
        d = e.to_dict()
        assert d["judge_score"] == 0
        assert d["judge_critique"] == "bad output"
        assert "human_score" not in d  # None, omitted

    def test_both_grades(self):
        e = Evaluation(
            trace_id="t1",
            human_score=1,
            human_comment="looks good",
            judge_score=0,
            judge_critique="missed details",
        )
        d = e.to_dict()
        assert d["human_score"] == 1
        assert d["judge_score"] == 0

    def test_human_span_ids(self):
        e = Evaluation(trace_id="t1", human_score=0, human_span_ids=["sp1", "sp2"])
        d = e.to_dict()
        assert d["human_span_ids"] == ["sp1", "sp2"]
