"""LangfuseTraceProvider — real backend, exercised via a fake Langfuse client."""

from __future__ import annotations

from typing import Any


class _FakeGeneration:
    def __init__(self) -> None:
        self.ended = False
        self.score_calls: list[dict[str, Any]] = []

    def end(self, **kwargs: Any) -> None:
        self.ended = True
        self.end_kwargs = kwargs


class _FakeTrace:
    def __init__(self, name: str, metadata: dict[str, Any]) -> None:
        self.id = f"trace-{name}"
        self.name = name
        self.metadata = metadata
        self.generations: list[dict[str, Any]] = []
        self.retrievals: list[dict[str, Any]] = []
        self.scores: list[dict[str, Any]] = []

    def generation(self, **kwargs: Any) -> _FakeGeneration:
        self.generations.append(kwargs)
        return _FakeGeneration()

    def span(self, **kwargs: Any) -> Any:
        self.retrievals.append(kwargs)
        return _FakeGeneration()

    def score(self, **kwargs: Any) -> None:
        self.scores.append(kwargs)


class _FakeLangfuseClient:
    def __init__(self) -> None:
        self.traces: list[_FakeTrace] = []
        self.flushed = 0

    def trace(self, *, name: str, metadata: dict[str, Any] | None = None) -> _FakeTrace:
        t = _FakeTrace(name, metadata or {})
        self.traces.append(t)
        return t

    def flush(self) -> None:
        self.flushed += 1


def test_langfuse_provider_satisfies_protocol() -> None:
    from axiom.infra.tracing import TraceProvider
    from axiom.infra.tracing.langfuse_provider import LangfuseTraceProvider

    p = LangfuseTraceProvider(client=_FakeLangfuseClient())
    assert isinstance(p, TraceProvider)


def test_langfuse_provider_routes_events_to_client() -> None:
    from axiom.infra.tracing.langfuse_provider import LangfuseTraceProvider

    client = _FakeLangfuseClient()
    p = LangfuseTraceProvider(client=client)

    tid = p.start_trace("research", session="s1")
    p.log_generation(tid, model="bonsai", prompt="q", output="a", tokens=42)
    p.log_retrieval(tid, query="q", results=[{"id": 1}], k=1)
    p.score(tid, name="faithfulness", value=0.9)
    p.flush()

    assert len(client.traces) == 1
    t = client.traces[0]
    assert t.name == "research"
    assert t.metadata == {"session": "s1"}
    assert len(t.generations) == 1
    assert t.generations[0]["model"] == "bonsai"
    assert len(t.retrievals) == 1
    assert t.scores == [{"name": "faithfulness", "value": 0.9}]
    assert client.flushed == 1


def test_langfuse_provider_unknown_trace_id_is_safe() -> None:
    from axiom.infra.tracing.langfuse_provider import LangfuseTraceProvider

    p = LangfuseTraceProvider(client=_FakeLangfuseClient())
    # Logging against an unknown trace id should not raise — degrade gracefully.
    p.log_generation("bogus", model="m", prompt="p", output="o")
    p.score("bogus", name="s", value=1.0)
    p.flush()
