"""ASV benchmarks for LLM trace observability overhead.

Measures the time to create LLMTrace models, record traces via
TraceService, compute metrics, and exercise lifecycle hooks.
"""

from __future__ import annotations

import importlib
import sys
from pathlib import Path
from unittest.mock import MagicMock

_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

import cleveragents  # noqa: E402

importlib.reload(cleveragents)

from cleveragents.application.services.trace_service import TraceService  # noqa: E402
from cleveragents.domain.models.observability.llm_trace import LLMTrace  # noqa: E402
from cleveragents.domain.models.observability.metrics import (  # noqa: E402
    MetricCollector,
    OperationalMetricKey,
)

VALID_PLAN_ID = "01HX0000000000PPPPPPPPPPPP"


class InMemoryRepo:
    """Minimal in-memory repo for benchmarking."""

    def __init__(self) -> None:
        self._store: dict[str, LLMTrace] = {}

    def save(self, trace: LLMTrace) -> None:
        self._store[trace.trace_id] = trace

    def get(self, trace_id: str) -> LLMTrace | None:
        return self._store.get(trace_id)

    def list_by_plan(self, plan_id: str) -> list[LLMTrace]:
        return [t for t in self._store.values() if t.plan_id == plan_id]

    def list_by_decision(self, decision_id: str) -> list[LLMTrace]:
        return [t for t in self._store.values() if t.decision_id == decision_id]


def _make_trace(idx: int) -> LLMTrace:
    idx_str = f"{idx:08d}"
    ulid = f"01HX{'A' * (22 - len(idx_str))}{idx_str}"
    return LLMTrace(
        trace_id=ulid,
        plan_id=VALID_PLAN_ID,
        actor="planner",
        provider="openai",
        model="gpt-4o",
        prompt_tokens=500 + idx,
        completion_tokens=200 + idx,
        cost_usd=0.01 + idx * 0.001,
        latency_ms=300.0 + idx,
    )


class LLMTraceModelCreationSuite:
    """Benchmark LLMTrace model creation."""

    def time_create_single_trace(self) -> None:
        """Create a single LLMTrace model."""
        _make_trace(0)

    def time_create_100_traces(self) -> None:
        """Create 100 LLMTrace models."""
        for i in range(100):
            _make_trace(i)


class LLMTraceServiceSuite:
    """Benchmark TraceService operations."""

    def setup(self) -> None:
        self._svc = TraceService(
            settings=MagicMock(),
            repository=InMemoryRepo(),  # type: ignore[arg-type]
        )
        self._traces = [_make_trace(i) for i in range(100)]

    def time_record_100_traces(self) -> None:
        """Record 100 traces."""
        for t in self._traces:
            self._svc.record_trace(t)

    def time_compute_metrics_100(self) -> None:
        """Compute metrics over 100 traces."""
        for t in self._traces:
            self._svc.record_trace(t)
        self._svc.compute_metrics(VALID_PLAN_ID)


class MetricCollectorSuite:
    """Benchmark MetricCollector operations."""

    def time_record_metric(self) -> None:
        """Create a single metric entry."""
        MetricCollector.record(
            OperationalMetricKey.PLAN_DURATION_MS,
            1500.0,
            VALID_PLAN_ID,
        )

    def time_plan_duration_convenience(self) -> None:
        """Use plan_duration convenience method."""
        MetricCollector.plan_duration(VALID_PLAN_ID, 1500.0)

    def time_lifecycle_hooks(self) -> None:
        """Exercise all lifecycle hooks."""
        svc = TraceService(
            settings=MagicMock(),
            repository=InMemoryRepo(),  # type: ignore[arg-type]
        )
        svc.on_plan_start(VALID_PLAN_ID)
        svc.on_actor_invocation(VALID_PLAN_ID, "planner", 250.0)
        svc.on_tool_execution(VALID_PLAN_ID, "file_write", errored=False)
