"""Observability configuration module for Robin AI.

Centralizes all OpenTelemetry and observability-related configuration,
providing a clean interface for feature flag integration and environment
variable management.
"""

import os
from typing import Any

from robin_commons.resilience.breaker import CircuitBreakerConfig


class ServiceConfig:
    """Configuration for service metadata."""

    def get_service_name(self) -> str:
        """Get service name for OpenTelemetry."""
        return os.getenv("OTEL_SERVICE_NAME", "robin-service")

    def get_service_version(self) -> str:
        """Get service version for OpenTelemetry."""
        return os.getenv("OTEL_SERVICE_VERSION", "1.0.0")

    def get_environment(self) -> str:
        """Get deployment environment."""
        return os.getenv("OTEL_ENVIRONMENT", os.getenv("ENVIRONMENT", "development"))

    def get_resource_attributes(self) -> dict[str, str]:
        """Get OpenTelemetry resource attributes."""
        base_attributes = {
            "service.name": self.get_service_name(),
            "service.version": self.get_service_version(),
            "deployment.environment": self.get_environment(),
            "service.namespace": "robin-platform",
        }

        # Add optional attributes if available
        if hostname := os.getenv("HOSTNAME"):
            base_attributes["host.name"] = hostname

        if instance_id := os.getenv("INSTANCE_ID"):
            base_attributes["service.instance.id"] = instance_id

        return base_attributes


class OtlpExporterConfig:
    """Configuration for OTLP exporter."""

    def __init__(self, config: "ObservabilityConfig") -> None:
        self._config = config

    def is_alloy_enabled(self) -> bool:
        """Check if Grafana Alloy is enabled for local export."""
        return self._config.is_alloy_enabled()

    def get_otlp_endpoint(self) -> str:
        """Get OTLP endpoint for traces."""
        if self.is_alloy_enabled():
            # Use local Alloy for cost optimization
            return os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://alloy:4317")
        else:
            # Direct to Grafana Cloud
            return os.getenv(
                "GRAFANA_CLOUD_OTLP_ENDPOINT",
                "https://otlp-gateway-prod-us-west-0.grafana.net/otlp",
            )

    def get_otlp_protocol(self) -> str:
        """Get OTLP protocol (grpc or http/protobuf)."""
        return os.getenv("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc")

    def get_otlp_headers(self) -> dict[str, str]:
        """Get OTLP headers for authentication."""
        headers = {}

        # Parse headers from environment variable
        if headers_str := os.getenv("OTEL_EXPORTER_OTLP_HEADERS"):
            for header in headers_str.split(","):
                if "=" in header:
                    key, value = header.strip().split("=", 1)
                    headers[key] = value

        # Add Grafana Cloud authentication if not using Alloy
        if not self.is_alloy_enabled():
            if api_key := self.get_grafana_api_key():
                headers["authorization"] = f"Bearer {api_key}"

        return headers

    def get_grafana_api_key(self) -> str | None:
        """Get Grafana Cloud API key."""
        return self._config.get_grafana_api_key()


class SamplingConfig:
    """Configuration for sampling rates."""

    def get_trace_sample_rate(self) -> float:
        """Get trace sampling rate (0.0 to 1.0)."""
        try:
            return float(os.getenv("OTEL_TRACE_SAMPLE_RATE", "1.0"))
        except ValueError:
            return 1.0

    def get_performance_sample_rate(self) -> float:
        """Get performance sampling rate for high-volume operations."""
        try:
            return float(os.getenv("OTEL_PERFORMANCE_SAMPLE_RATE", "0.1"))
        except ValueError:
            return 0.1


class BatchExportConfig:
    """Configuration for batch export."""

    def get_batch_export_config(self) -> dict[str, Any]:
        """Get batch export configuration for optimal performance."""
        return {
            "max_queue_size": int(os.getenv("OTEL_BSP_MAX_QUEUE_SIZE", "2048")),
            "max_export_batch_size": int(
                os.getenv("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", "512")
            ),
            "export_timeout_millis": int(os.getenv("OTEL_BSP_EXPORT_TIMEOUT", "30000")),
            "schedule_delay_millis": int(os.getenv("OTEL_BSP_SCHEDULE_DELAY", "5000")),
        }


class ObservabilityCircuitBreakerConfig:
    """Configuration for circuit breaker."""

    def get_circuit_breaker_config(self) -> CircuitBreakerConfig:
        """Get circuit breaker configuration for exporter resilience."""
        return CircuitBreakerConfig(
            failure_threshold=int(
                os.getenv("OTEL_CIRCUIT_BREAKER_FAILURE_THRESHOLD", "5")
            ),
            recovery_timeout_seconds=int(
                os.getenv("OTEL_CIRCUIT_BREAKER_RECOVERY_TIMEOUT", "60")
            ),
            success_threshold=int(
                os.getenv("OTEL_CIRCUIT_BREAKER_SUCCESS_THRESHOLD", "2")
            ),
        )


class InstrumentationConfig:
    """Configuration for auto-instrumentation."""

    def is_fastapi_instrumentation_enabled(self) -> bool:
        """Check if FastAPI auto-instrumentation is enabled."""
        return os.getenv("ENABLE_FASTAPI_INSTRUMENTATION", "true").lower() == "true"

    def is_sqlalchemy_instrumentation_enabled(self) -> bool:
        """Check if SQLAlchemy auto-instrumentation is enabled."""
        return os.getenv("ENABLE_SQLALCHEMY_INSTRUMENTATION", "true").lower() == "true"

    def is_redis_instrumentation_enabled(self) -> bool:
        """Check if Redis auto-instrumentation is enabled."""
        return os.getenv("ENABLE_REDIS_INSTRUMENTATION", "true").lower() == "true"

    def is_httpx_instrumentation_enabled(self) -> bool:
        """Check if HTTP client auto-instrumentation is enabled."""
        return os.getenv("ENABLE_HTTPX_INSTRUMENTATION", "true").lower() == "true"

    def is_nats_instrumentation_enabled(self) -> bool:
        """Check if NATS instrumentation is enabled."""
        return os.getenv("ENABLE_NATS_INSTRUMENTATION", "true").lower() == "true"

    def is_grpc_instrumentation_enabled(self) -> bool:
        """Check if gRPC instrumentation is enabled."""
        return os.getenv("ENABLE_GRPC_INSTRUMENTATION", "true").lower() == "true"


class ObservabilityConfig:
    """Centralized configuration for observability features."""

    def __init__(self) -> None:
        """Initialize observability configuration."""
        self._service_config = ServiceConfig()
        self._sampling_config = SamplingConfig()
        self._batch_config = BatchExportConfig()
        self._circuit_breaker_config = ObservabilityCircuitBreakerConfig()
        # OtlpExporterConfig needs self for is_alloy_enabled
        self._otlp_config = OtlpExporterConfig(self)
        self._instrumentation_config = InstrumentationConfig()

    def is_alloy_enabled(self) -> bool:
        """Check if Grafana Alloy is enabled for local export."""
        return os.getenv("ENABLE_GRAFANA_ALLOY", "false").lower() == "true"

    # Core OpenTelemetry Configuration - Delegate to ServiceConfig
    def get_service_name(self) -> str:
        """Get service name for OpenTelemetry."""
        return self._service_config.get_service_name()

    def get_service_version(self) -> str:
        """Get service version for OpenTelemetry."""
        return self._service_config.get_service_version()

    def get_environment(self) -> str:
        """Get deployment environment."""
        return self._service_config.get_environment()

    def get_resource_attributes(self) -> dict[str, str]:
        """Get OpenTelemetry resource attributes."""
        return self._service_config.get_resource_attributes()

    # OTLP Exporter Configuration - Delegate to OtlpExporterConfig
    def get_otlp_endpoint(self) -> str:
        """Get OTLP endpoint for traces."""
        return self._otlp_config.get_otlp_endpoint()

    def get_otlp_protocol(self) -> str:
        """Get OTLP protocol (grpc or http/protobuf)."""
        return self._otlp_config.get_otlp_protocol()

    def get_otlp_headers(self) -> dict[str, str]:
        """Get OTLP headers for authentication."""
        return self._otlp_config.get_otlp_headers()

    def get_grafana_api_key(self) -> str | None:
        """Get Grafana Cloud API key."""
        return os.getenv("GRAFANA_CLOUD_OTLP_API_KEY")

    # Sampling Configuration - Delegate to SamplingConfig
    def get_trace_sample_rate(self) -> float:
        """Get trace sampling rate (0.0 to 1.0)."""
        return self._sampling_config.get_trace_sample_rate()

    def get_performance_sample_rate(self) -> float:
        """Get performance sampling rate for high-volume operations."""
        return self._sampling_config.get_performance_sample_rate()

    # Batch Export Configuration - Delegate to BatchExportConfig
    def get_batch_export_config(self) -> dict[str, Any]:
        """Get batch export configuration for optimal performance."""
        return self._batch_config.get_batch_export_config()

    # Circuit Breaker Configuration - Delegate to ObservabilityCircuitBreakerConfig
    def get_circuit_breaker_config(self) -> CircuitBreakerConfig:
        """Get circuit breaker configuration for exporter resilience."""
        return self._circuit_breaker_config.get_circuit_breaker_config()

    # Feature Flag Checks - Using Envs as default
    def is_observability_enabled(self) -> bool:
        """Master switch for all observability features."""
        return os.getenv("ENABLE_OBSERVABILITY", "true").lower() == "true"

    def is_advanced_features_enabled(self) -> bool:
        """Check if advanced observability features are enabled."""
        return os.getenv("ENABLE_ADVANCED_OBSERVABILITY", "true").lower() == "true"

    # Core Features (controlled by master switch)
    def is_tracing_enabled(self) -> bool:
        """Check if OpenTelemetry tracing is enabled."""
        return self.is_observability_enabled()

    def is_metrics_enabled(self) -> bool:
        """Check if OpenTelemetry metrics are enabled."""
        return self.is_observability_enabled()

    def is_logs_enabled(self) -> bool:
        """Check if OpenTelemetry logs are enabled."""
        return self.is_observability_enabled()

    # Auto-Instrumentation - Delegate to InstrumentationConfig
    def is_fastapi_instrumentation_enabled(self) -> bool:
        """Check if FastAPI auto-instrumentation is enabled."""
        return self._instrumentation_config.is_fastapi_instrumentation_enabled()

    def is_sqlalchemy_instrumentation_enabled(self) -> bool:
        """Check if SQLAlchemy auto-instrumentation is enabled."""
        return self._instrumentation_config.is_sqlalchemy_instrumentation_enabled()

    def is_redis_instrumentation_enabled(self) -> bool:
        """Check if Redis auto-instrumentation is enabled."""
        return self._instrumentation_config.is_redis_instrumentation_enabled()

    def is_httpx_instrumentation_enabled(self) -> bool:
        """Check if HTTP client auto-instrumentation is enabled."""
        return self._instrumentation_config.is_httpx_instrumentation_enabled()

    def is_nats_instrumentation_enabled(self) -> bool:
        """Check if NATS instrumentation is enabled."""
        return self._instrumentation_config.is_nats_instrumentation_enabled()

    def is_grpc_instrumentation_enabled(self) -> bool:
        """Check if gRPC instrumentation is enabled."""
        return self._instrumentation_config.is_grpc_instrumentation_enabled()


# Global configuration instance
_config_instance: ObservabilityConfig | None = None


def get_observability_config() -> ObservabilityConfig:
    """Get the global observability configuration instance."""
    global _config_instance
    if _config_instance is None:
        _config_instance = ObservabilityConfig()
    return _config_instance
