"""Optional Prometheus metrics exposition for otelctx.

This module is safe to import even if ``prometheus_client`` is not installed;
all operations become no-ops in that case.

Usage::

    from otelctx.telemetry import telemetry

    # Record events
    telemetry.record_ingested("traces", count=5)
    telemetry.record_ingested("logs", count=12)
    telemetry.record_dlq_depth(3)
    telemetry.record_sampler_dropped(7)

    # Expose /metrics endpoint (call once at startup):
    app.include_router(telemetry.build_router())
"""

from __future__ import annotations

from typing import TYPE_CHECKING

try:
    from prometheus_client import (  # type: ignore
        CONTENT_TYPE_LATEST,
        Counter,
        Gauge,
        Histogram,
        generate_latest,
    )
    _PROM_AVAILABLE = True
except ImportError:
    _PROM_AVAILABLE = False
    Counter = Gauge = Histogram = None  # type: ignore


# ---------------------------------------------------------------------------
# Metric definitions (created only when prometheus_client is installed)
# ---------------------------------------------------------------------------

if _PROM_AVAILABLE:
    _ingestion_total = Counter(
        "otelctx_ingestion_total",
        "Total number of signals ingested",
        ["signal"],
    )
    _ingest_errors_total = Counter(
        "otelctx_ingest_errors_total",
        "Total ingestion errors",
        ["signal"],
    )
    _dlq_depth = Gauge(
        "otelctx_dlq_depth",
        "Current number of entries in the dead-letter queue",
    )
    _sampler_dropped_total = Counter(
        "otelctx_sampler_dropped_total",
        "Total events dropped by the adaptive sampler",
    )
    _sampler_accepted_total = Counter(
        "otelctx_sampler_accepted_total",
        "Total events accepted by the adaptive sampler",
    )
    _active_ws_connections = Gauge(
        "otelctx_ws_connections",
        "Number of active WebSocket connections",
    )
    _ingest_duration_seconds = Histogram(
        "otelctx_ingest_duration_seconds",
        "Ingest handler latency in seconds",
        ["signal"],
        buckets=(0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5),
    )
else:
    _ingestion_total = None
    _ingest_errors_total = None
    _dlq_depth = None
    _sampler_dropped_total = None
    _sampler_accepted_total = None
    _active_ws_connections = None
    _ingest_duration_seconds = None


# ---------------------------------------------------------------------------
# Public facade
# ---------------------------------------------------------------------------

class _Noop:
    """No-op stand-in when prometheus_client is unavailable."""

    def labels(self, **_kwargs):
        return self

    def inc(self, *args, **kwargs) -> None:
        pass

    def set(self, *args, **kwargs) -> None:
        pass

    def observe(self, *args, **kwargs) -> None:
        pass

    def time(self):
        """Context manager no-op."""
        import contextlib
        return contextlib.nullcontext()


_NOOP = _Noop()


class OtelTelemetry:
    """Thin wrapper around Prometheus metrics with graceful degradation."""

    @property
    def available(self) -> bool:
        return _PROM_AVAILABLE

    # ------------------------------------------------------------------
    # Ingestion
    # ------------------------------------------------------------------

    def record_ingested(self, signal: str, count: int = 1) -> None:
        counter = _ingestion_total if _PROM_AVAILABLE else _NOOP
        if _PROM_AVAILABLE:
            counter.labels(signal=signal).inc(count)

    def record_ingest_error(self, signal: str) -> None:
        counter = _ingest_errors_total if _PROM_AVAILABLE else _NOOP
        if _PROM_AVAILABLE:
            counter.labels(signal=signal).inc()

    def ingest_timer(self, signal: str):
        """Return a context manager that records ingest latency."""
        if not _PROM_AVAILABLE:
            return _NOOP.time()
        return _ingest_duration_seconds.labels(signal=signal).time()

    # ------------------------------------------------------------------
    # DLQ
    # ------------------------------------------------------------------

    def record_dlq_depth(self, depth: int) -> None:
        if _PROM_AVAILABLE:
            _dlq_depth.set(depth)

    # ------------------------------------------------------------------
    # Sampler
    # ------------------------------------------------------------------

    def record_sampler_dropped(self, count: int = 1) -> None:
        if _PROM_AVAILABLE:
            _sampler_dropped_total.inc(count)

    def record_sampler_accepted(self, count: int = 1) -> None:
        if _PROM_AVAILABLE:
            _sampler_accepted_total.inc(count)

    # ------------------------------------------------------------------
    # WebSocket
    # ------------------------------------------------------------------

    def set_ws_connections(self, count: int) -> None:
        if _PROM_AVAILABLE:
            _active_ws_connections.set(count)

    # ------------------------------------------------------------------
    # /metrics endpoint
    # ------------------------------------------------------------------

    def build_router(self):
        """Return a FastAPI router that exposes ``GET /metrics``."""
        from fastapi import APIRouter
        from fastapi.responses import Response

        router = APIRouter()

        if not _PROM_AVAILABLE:
            @router.get("/metrics")
            async def metrics_unavailable() -> dict:
                return {"error": "prometheus_client not installed"}
            return router

        @router.get("/metrics")
        async def metrics() -> Response:
            data = generate_latest()
            return Response(content=data, media_type=CONTENT_TYPE_LATEST)

        return router


# Module-level singleton
telemetry = OtelTelemetry()
