"""
OpenTelemetry Metrics Integration for SigNoz
Exports metrics to SigNoz via OpenTelemetry Collector in addition to Prometheus
"""

import os
import structlog
from typing import Optional
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource, SERVICE_NAME, SERVICE_VERSION

logger = structlog.get_logger()


def setup_otel_metrics(
    service_name: str,
    service_version: str = "1.0.0",
    otel_endpoint: Optional[str] = None,
    export_interval_millis: int = 60000  # Export every 60 seconds
) -> Optional[MeterProvider]:
    """
    Setup OpenTelemetry metrics to export to SigNoz.

    This creates a dual-export strategy:
    - Prometheus exposition format at /metrics (for Prometheus scraping)
    - OTLP push to SigNoz collector (for direct ingestion)

    Args:
        service_name: Name of the service (e.g., "auth-service")
        service_version: Version of the service
        otel_endpoint: OpenTelemetry collector endpoint (default from env)
        export_interval_millis: How often to push metrics (default 60s)

    Returns:
        MeterProvider instance if successful, None otherwise

    Example:
        from shared.monitoring.metrics_exporter import setup_otel_metrics

        # Setup during service initialization
        meter_provider = setup_otel_metrics("auth-service", "1.0.0")

        # Create meters for your metrics
        meter = meter_provider.get_meter(__name__)
        request_counter = meter.create_counter(
            "http.server.requests",
            description="Total HTTP requests",
            unit="1"
        )

        # Record metrics
        request_counter.add(1, {"method": "GET", "status": "200"})
    """

    # Check if metrics export is enabled
    enable_otel_metrics = os.getenv("ENABLE_OTEL_METRICS", "true").lower() == "true"
    if not enable_otel_metrics:
        logger.info(
            "OpenTelemetry metrics export disabled",
            service=service_name,
            reason="ENABLE_OTEL_METRICS not set to 'true'"
        )
        return None

    # Get OTLP endpoint from environment or parameter
    if otel_endpoint is None:
        otel_endpoint = os.getenv(
            "OTEL_EXPORTER_OTLP_ENDPOINT",
            os.getenv("OTEL_COLLECTOR_ENDPOINT", "http://signoz-otel-collector.bakery-ia:4318")
        )

    # Ensure endpoint has /v1/metrics path for HTTP
    if not otel_endpoint.endswith("/v1/metrics"):
        otel_endpoint = f"{otel_endpoint}/v1/metrics"

    try:
        # Create resource with service information
        resource = Resource(attributes={
            SERVICE_NAME: service_name,
            SERVICE_VERSION: service_version,
            "deployment.environment": os.getenv("ENVIRONMENT", "development"),
            "k8s.namespace.name": os.getenv("K8S_NAMESPACE", "bakery-ia"),
            "k8s.pod.name": os.getenv("HOSTNAME", "unknown"),
        })

        # Configure OTLP exporter for metrics
        otlp_exporter = OTLPMetricExporter(
            endpoint=otel_endpoint,
            timeout=10
        )

        # Create periodic metric reader
        metric_reader = PeriodicExportingMetricReader(
            exporter=otlp_exporter,
            export_interval_millis=export_interval_millis
        )

        # Configure meter provider
        meter_provider = MeterProvider(
            resource=resource,
            metric_readers=[metric_reader]
        )

        # Set global meter provider
        metrics.set_meter_provider(meter_provider)

        logger.info(
            "OpenTelemetry metrics export configured",
            service=service_name,
            otel_endpoint=otel_endpoint,
            export_interval_seconds=export_interval_millis / 1000
        )

        return meter_provider

    except Exception as e:
        logger.error(
            "Failed to setup OpenTelemetry metrics export",
            service=service_name,
            error=str(e),
            reason="Will continue with Prometheus-only metrics"
        )
        return None


class OTelMetricsCollector:
    """
    Wrapper for OpenTelemetry metrics that provides a similar interface
    to the Prometheus MetricsCollector.

    This allows services to emit metrics that go to both Prometheus and SigNoz.
    """

    def __init__(self, service_name: str, meter_provider: MeterProvider):
        self.service_name = service_name
        self.meter_provider = meter_provider
        self.meter = meter_provider.get_meter(__name__)

        # Store created instruments
        self._counters = {}
        self._histograms = {}
        self._gauges = {}

    def create_counter(self, name: str, description: str = "", unit: str = "1"):
        """Create or get an OpenTelemetry Counter"""
        if name not in self._counters:
            self._counters[name] = self.meter.create_counter(
                name=f"{self.service_name.replace('-', '_')}_{name}",
                description=description,
                unit=unit
            )
        return self._counters[name]

    def create_histogram(self, name: str, description: str = "", unit: str = "1"):
        """Create or get an OpenTelemetry Histogram"""
        if name not in self._histograms:
            self._histograms[name] = self.meter.create_histogram(
                name=f"{self.service_name.replace('-', '_')}_{name}",
                description=description,
                unit=unit
            )
        return self._histograms[name]

    def create_gauge(self, name: str, description: str = "", unit: str = "1"):
        """
        Create or get an OpenTelemetry observable gauge.
        Note: Gauges in OTEL require a callback function.
        """
        if name not in self._gauges:
            # Store gauge reference for callback registration
            self._gauges[name] = {
                "name": f"{self.service_name.replace('-', '_')}_{name}",
                "description": description,
                "unit": unit,
                "value": 0,
                "attributes": {}
            }
        return self._gauges[name]

    def increment_counter(self, name: str, value: int = 1, attributes: dict = None):
        """Increment a counter with optional attributes"""
        if name in self._counters:
            if attributes is None:
                attributes = {"service": self.service_name}
            elif "service" not in attributes:
                attributes["service"] = self.service_name

            self._counters[name].add(value, attributes)

    def observe_histogram(self, name: str, value: float, attributes: dict = None):
        """Record a histogram observation with optional attributes"""
        if name in self._histograms:
            if attributes is None:
                attributes = {"service": self.service_name}
            elif "service" not in attributes:
                attributes["service"] = self.service_name

            self._histograms[name].record(value, attributes)

    def set_gauge(self, name: str, value: float, attributes: dict = None):
        """Set a gauge value (stores for next callback)"""
        if name in self._gauges:
            if attributes is None:
                attributes = {"service": self.service_name}
            elif "service" not in attributes:
                attributes["service"] = self.service_name

            self._gauges[name]["value"] = value
            self._gauges[name]["attributes"] = attributes


def create_dual_metrics_collector(service_name: str, service_version: str = "1.0.0"):
    """
    Create a metrics collector that exports to both Prometheus and SigNoz.

    This function sets up both collection strategies:
    1. Prometheus client library (for /metrics endpoint scraping)
    2. OpenTelemetry metrics (for OTLP push to SigNoz)

    Returns a tuple: (prometheus_collector, otel_collector)
    Both collectors can be used independently or together.

    Example:
        from shared.monitoring.metrics_exporter import create_dual_metrics_collector

        prom_collector, otel_collector = create_dual_metrics_collector("auth-service")

        # Prometheus counter
        prom_collector.register_counter("requests_total", "Total requests")
        prom_collector.increment_counter("requests_total", labels={"status": "200"})

        # OpenTelemetry counter (pushed to SigNoz)
        counter = otel_collector.create_counter("requests_total", "Total requests")
        counter.add(1, {"status": "200"})
    """
    from shared.monitoring.metrics import MetricsCollector

    # Create Prometheus collector
    prom_collector = MetricsCollector(service_name)

    # Create OpenTelemetry collector
    meter_provider = setup_otel_metrics(service_name, service_version)
    otel_collector = None
    if meter_provider:
        otel_collector = OTelMetricsCollector(service_name, meter_provider)

    return prom_collector, otel_collector
