"""
Configuration management for CAAE service.

Supports environment variables and sensible defaults based on experimental validation.
"""

import os
import logging
from dataclasses import dataclass, field
from typing import Dict, Any, Optional

logger = logging.getLogger(__name__)


@dataclass
class CAAEConfig:
    """
    Configuration for CAAE Advisor service.

    All values are validated against experimental results from Experiments 1-7.
    """

    # Service configuration
    host: str = field(default_factory=lambda: os.getenv("CAAE_HOST", "0.0.0.0"))
    port: int = field(default_factory=lambda: int(os.getenv("CAAE_PORT", "8000")))
    log_level: str = field(default_factory=lambda: os.getenv("LOG_LEVEL", "INFO"))

    # Circuit breaker configuration (validated in Experiment 5)
    circuit_breaker_threshold_ms: float = field(
        default_factory=lambda: float(os.getenv("CAAE_CB_THRESHOLD_MS", "5.0"))
    )
    circuit_breaker_cooldown_ms: float = field(
        default_factory=lambda: float(os.getenv("CAAE_CB_COOLDOWN_MS", "1000.0"))
    )

    # Cost model coefficients (validated in Experiment 2)
    cost_model_confidence: float = 0.972  # 97.2% accuracy from experiments
    layer_depth_weight: float = 0.5
    seq_index_weight: float = 1.2
    decay_rate: float = 0.001

    # Hardware profiles (from Experiments 5 & 6)
    hardware_profiles: Dict[str, Dict[str, float]] = field(
        default_factory=lambda: {
            "gen3": {
                "bandwidth_gbps": 8.0,
                "optimal_threshold_tokens": 5000,
                "base_latency_ms": 0.5,
            },
            "gen4": {
                "bandwidth_gbps": 16.0,
                "optimal_threshold_tokens": 10000,
                "base_latency_ms": 0.25,
            },
            "gen5": {
                "bandwidth_gbps": 128.0,
                "optimal_threshold_tokens": 20000,
                "base_latency_ms": 0.1,
            },
        }
    )

    # Token to bytes conversion
    bytes_per_token: float = 2621.44  # ~2.56 KB per token (Llama-3-70B)

    # Timeout configuration
    decision_timeout_ms: float = field(
        default_factory=lambda: float(os.getenv("CAAE_DECISION_TIMEOUT_MS", "5.0"))
    )

    # Metrics configuration
    enable_prometheus: bool = field(
        default_factory=lambda: os.getenv("CAAE_ENABLE_PROMETHEUS", "true").lower()
        == "true"
    )

    def get_hardware_profile(self, bandwidth_gbps: float) -> Dict[str, Any]:
        """
        Get hardware profile based on bandwidth.

        Args:
            bandwidth_gbps: PCIe bandwidth in GB/s

        Returns:
            Hardware profile dictionary
        """
        if bandwidth_gbps <= 10:
            return self.hardware_profiles["gen3"]
        elif bandwidth_gbps <= 32:
            return self.hardware_profiles["gen4"]
        else:
            return self.hardware_profiles["gen5"]

    def to_dict(self) -> Dict[str, Any]:
        """Convert config to dictionary for logging/serialization."""
        return {
            "host": self.host,
            "port": self.port,
            "log_level": self.log_level,
            "circuit_breaker_threshold_ms": self.circuit_breaker_threshold_ms,
            "cost_model_confidence": self.cost_model_confidence,
            "enable_prometheus": self.enable_prometheus,
        }


# Global config instance
_config: CAAEConfig | None = None


def get_config() -> CAAEConfig:
    """Get or create global configuration instance."""
    global _config
    if _config is None:
        _config = CAAEConfig()
    return _config


def set_config(config: CAAEConfig) -> None:
    """Set global configuration instance (useful for testing)."""
    global _config
    _config = config


# ============================================================================
# AUTO-DETECTION UTILITIES
# ============================================================================


def detect_gpu_memory_gb() -> Optional[float]:
    """
    Auto-detect GPU memory in GB.
    
    Returns:
        GPU memory in GB, or None if detection fails.
    """
    try:
        import torch
        if torch.cuda.is_available():
            props = torch.cuda.get_device_properties(0)
            memory_gb = props.total_memory / (1024 ** 3)
            logger.info(f"Auto-detected GPU memory: {memory_gb:.1f} GB ({props.name})")
            return memory_gb
    except Exception as e:
        logger.debug(f"Failed to detect GPU memory: {e}")
    return None


def detect_pcie_bandwidth_gbps() -> Optional[float]:
    """
    Auto-detect PCIe bandwidth in GB/s.
    
    Uses NVML to query PCIe generation and width. Fallback to conservative default.
    
    Returns:
        PCIe bandwidth in GB/s, or None if detection fails.
    """
    try:
        import pynvml
        
        pynvml.nvmlInit()
        handle = pynvml.nvmlDeviceGetHandleByIndex(0)
        
        # Get PCIe generation (1-5)
        pcie_gen = pynvml.nvmlDeviceGetCurrPcieLinkGeneration(handle)
        
        # Get PCIe width (typically 16x)
        pcie_width = pynvml.nvmlDeviceGetCurrPcieLinkWidth(handle)
        
        pynvml.nvmlShutdown()
        
        # Calculate bidirectional bandwidth
        # Gen3: 1 GB/s per lane, Gen4: 2 GB/s per lane, Gen5: 4 GB/s per lane
        bandwidth_per_lane = {1: 0.25, 2: 0.5, 3: 1.0, 4: 2.0, 5: 4.0}
        gbps = bandwidth_per_lane.get(pcie_gen, 1.0) * pcie_width * 2  # Bidirectional
        
        logger.info(f"Auto-detected PCIe Gen{pcie_gen} x{pcie_width}: {gbps:.1f} GB/s")
        return gbps
    except Exception as e:
        logger.debug(f"Failed to detect PCIe bandwidth: {e}")
    return None


def get_pcie_bandwidth_with_fallback() -> float:
    """
    Get PCIe bandwidth with conservative fallback.
    
    Returns:
        PCIe bandwidth in GB/s (defaults to 16.0 GB/s for Gen4 x8).
    """
    bandwidth = detect_pcie_bandwidth_gbps()
    if bandwidth is None:
        bandwidth = 16.0  # Conservative Gen4 x8 default
        logger.warning(
            f"Could not detect PCIe bandwidth, using conservative default: {bandwidth} GB/s. "
            "Install nvidia-ml-py3 for accurate detection."
        )
    return bandwidth
