"""Structured observability via OpenTelemetry."""

from __future__ import annotations

from contextlib import contextmanager
from typing import Any

try:
    from opentelemetry import metrics, trace

    HAS_OTEL = True
except ImportError:  # pragma: no cover
    HAS_OTEL = False


class NeurovaultTelemetry:
    """Observability layer for NEUROVAULT operations."""

    def __init__(self, enabled: bool = True):
        self._enabled = bool(enabled) and HAS_OTEL
        self._tracer = None
        self._ingest_counter = None
        self._recall_histogram = None
        self._consolidation_histogram = None

        if self._enabled:
            self._tracer = trace.get_tracer("neurovault")
            meter = metrics.get_meter("neurovault")
            self._ingest_counter = meter.create_counter(
                "neurovault.memories.ingested",
                description="Number of memories ingested",
            )
            self._recall_histogram = meter.create_histogram(
                "neurovault.recall.duration_ms",
                description="Recall operation duration in ms",
            )
            self._consolidation_histogram = meter.create_histogram(
                "neurovault.consolidation.duration_s",
                description="Consolidation cycle duration in seconds",
            )

    @contextmanager
    def span(self, name: str, attributes: dict[str, Any] | None = None):
        """Create a trace span."""
        if self._enabled and self._tracer is not None:
            with self._tracer.start_as_current_span(name) as span_obj:
                if attributes:
                    for k, v in attributes.items():
                        span_obj.set_attribute(k, v)
                yield span_obj
        else:
            yield None

    def record_ingest(self, source: str, count: int = 1) -> None:
        if self._enabled and self._ingest_counter is not None:
            self._ingest_counter.add(count, {"source": source})

    def record_recall(self, duration_ms: float, mode: str, result_count: int) -> None:
        if self._enabled and self._recall_histogram is not None:
            self._recall_histogram.record(duration_ms, {"mode": mode, "results": result_count})

    def record_consolidation(self, duration_s: float, insights: int) -> None:
        if self._enabled and self._consolidation_histogram is not None:
            self._consolidation_histogram.record(duration_s, {"insights": insights})
