"""Instrumentación OpenTelemetry para el supervisor.

Activación: establece OTEL_ENABLED=true en .env.
Si las librerías OTel no están instaladas, el módulo no falla — simplemente
devuelve un tracer no-op para que el resto del código no cambie.

Variables de entorno relevantes:
  OTEL_ENABLED=true|false          (default: false)
  OTEL_SERVICE_NAME                (default: xzonas-supervisor)
  OTEL_EXPORTER_OTLP_ENDPOINT      (default: http://localhost:4317)
    → apunta al colector OTel propio o al Jaeger all-in-one de docker-compose
"""
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

logger = logging.getLogger(__name__)

if TYPE_CHECKING:
    from opentelemetry.trace import Tracer


def setup_tracing(
    service_name: str,
    otlp_endpoint: str,
    enabled: bool = False,
) -> "Tracer":
    """Inicializa OTel SDK y devuelve el Tracer del servicio.

    Si enabled=False o las librerías no están disponibles devuelve un tracer
    no-op (NoOpTracer) que no produce overhead alguno.
    """
    if not enabled:
        return _noop_tracer()

    try:
        from opentelemetry import trace
        from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
            OTLPSpanExporter,
        )
        from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
        from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
        from opentelemetry.sdk.resources import SERVICE_NAME, Resource
        from opentelemetry.sdk.trace import TracerProvider
        from opentelemetry.sdk.trace.export import BatchSpanProcessor

        resource = Resource(attributes={SERVICE_NAME: service_name})
        provider = TracerProvider(resource=resource)

        exporter = OTLPSpanExporter(endpoint=otlp_endpoint, insecure=True)
        provider.add_span_processor(BatchSpanProcessor(exporter))

        trace.set_tracer_provider(provider)

        # Instrumentaciones automáticas
        FastAPIInstrumentor().instrument()
        HTTPXClientInstrumentor().instrument()

        tracer: Tracer = trace.get_tracer(service_name)
        logger.info(
            "otel.tracing.started",
            extra={"service": service_name, "endpoint": otlp_endpoint},
        )
        return tracer  # type: ignore[return-value]

    except ImportError:
        logger.warning(
            "otel.tracing.disabled",
            extra={"reason": "opentelemetry packages not installed"},
        )
        return _noop_tracer()
    except Exception as exc:  # noqa: BLE001
        logger.warning(
            "otel.tracing.error",
            extra={"reason": str(exc)},
        )
        return _noop_tracer()


def teardown_tracing() -> None:
    """Flush y cierre del TracerProvider al hacer shutdown del supervisor."""
    try:
        from opentelemetry import trace

        provider = trace.get_tracer_provider()
        if hasattr(provider, "shutdown"):
            provider.shutdown()  # type: ignore[union-attr]
            logger.info("otel.tracing.shutdown")
    except Exception:  # noqa: BLE001
        pass


def _noop_tracer() -> Any:
    try:
        from opentelemetry.trace import NonRecordingSpan, NoOpTracer  # type: ignore[attr-defined]

        return NoOpTracer()
    except ImportError:
        # Stub mínimo por si acaso la librería no está instalada en absoluto
        class _NoOp:
            def start_as_current_span(self, *_a: Any, **_kw: Any):  # type: ignore[override]
                from contextlib import contextmanager

                @contextmanager
                def _ctx():  # type: ignore[misc]
                    yield None

                return _ctx()

            def start_span(self, *_a: Any, **_kw: Any) -> Any:
                return None

        return _NoOp()
