"""
Configuration Management System for Storm Logger MCP Servers
============================================================

Centralized configuration management with environment variable support,
validation, and runtime reconfiguration capabilities.
"""

import os
import json
import logging
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Dict, Any, Optional, List, Union
from enum import Enum
import threading
import time

class ConfigSource(Enum):
    """Configuration source types."""
    ENVIRONMENT = "environment"
    FILE = "file"
    DEFAULT = "default"
    RUNTIME = "runtime"

@dataclass
class ServerConfig:
    """Configuration for individual MCP servers."""
    name: str
    enabled: bool = True
    max_connections: int = 100
    request_timeout: int = 30
    health_check_interval: int = 60
    log_level: str = "INFO"
    tools_enabled: List[str] = field(default_factory=list)
    custom_settings: Dict[str, Any] = field(default_factory=dict)
    
    def __post_init__(self):
        if not self.tools_enabled:
            self.tools_enabled = []

@dataclass
class MonitoringConfig:
    """Configuration for monitoring system."""
    enabled: bool = True
    metrics_collection_interval: int = 30
    dashboard_enabled: bool = True
    dashboard_port: int = 8080
    alert_thresholds: Dict[str, float] = field(default_factory=dict)
    performance_monitoring: bool = True
    business_intelligence: bool = True
    
    def __post_init__(self):
        if not self.alert_thresholds:
            self.alert_thresholds = {
                "response_time_warning": 1.0,
                "response_time_critical": 5.0,
                "error_rate_warning": 5.0,
                "error_rate_critical": 10.0,
                "cpu_usage_warning": 80.0,
                "cpu_usage_critical": 95.0,
                "memory_usage_warning": 85.0,
                "memory_usage_critical": 95.0
            }

@dataclass
class SecurityConfig:
    """Security configuration settings."""
    authentication_enabled: bool = False
    api_key_required: bool = False
    rate_limiting_enabled: bool = True
    max_requests_per_minute: int = 100
    allowed_hosts: List[str] = field(default_factory=list)
    tls_enabled: bool = False
    certificate_path: Optional[str] = None
    private_key_path: Optional[str] = None
    
    def __post_init__(self):
        if not self.allowed_hosts:
            self.allowed_hosts = ["localhost", "127.0.0.1"]

@dataclass
class LoggingConfig:
    """Logging configuration settings."""
    level: str = "INFO"
    format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    file_enabled: bool = True
    file_path: str = "logs/mcp/server.log"
    console_enabled: bool = True
    max_file_size: int = 10485760  # 10MB
    backup_count: int = 5
    structured_logging: bool = True
    
@dataclass
class MCPServerConfig:
    """Complete MCP server configuration."""
    servers: Dict[str, ServerConfig] = field(default_factory=dict)
    monitoring: MonitoringConfig = field(default_factory=MonitoringConfig)
    security: SecurityConfig = field(default_factory=SecurityConfig)
    logging: LoggingConfig = field(default_factory=LoggingConfig)
    global_settings: Dict[str, Any] = field(default_factory=dict)
    
    def __post_init__(self):
        if not self.servers:
            self._setup_default_servers()
        if not self.global_settings:
            self.global_settings = {
                "debug_mode": False,
                "development_mode": False,
                "environment": "production"
            }
    
    def _setup_default_servers(self):
        """Setup default server configurations."""
        default_servers = [
            "core", "coverage", "testing", "security", 
            "patterns", "gitlab", "python"
        ]
        
        for server_name in default_servers:
            self.servers[server_name] = ServerConfig(
                name=server_name,
                enabled=True,
                tools_enabled=[]
            )

class ConfigManager:
    """Centralized configuration management system."""
    
    def __init__(self, config_file: str = "config/mcp_config.json"):
        self.config_file = Path(config_file)
        self.config_file.parent.mkdir(parents=True, exist_ok=True)
        
        self.config = MCPServerConfig()
        self.logger = logging.getLogger('mcp_config_manager')
        self.lock = threading.Lock()
        
        # Configuration sources tracking
        self.config_sources: Dict[str, ConfigSource] = {}
        
        # Load configuration
        self._load_configuration()
        
        # Setup file watching for auto-reload
        self._last_modified = 0
        self._check_interval = 5  # seconds
        self._watch_config_file()
    
    def _load_configuration(self):
        """Load configuration from all sources."""
        try:
            # Load from file first
            self._load_from_file()
            
            # Override with environment variables
            self._load_from_environment()
            
            # Validate configuration
            self._validate_configuration()
            
            self.logger.info("Configuration loaded successfully")
            
        except Exception as e:
            self.logger.error(f"Failed to load configuration: {e}")
            # Use default configuration
            self.config = MCPServerConfig()
    
    def _load_from_file(self):
        """Load configuration from JSON file."""
        if not self.config_file.exists():
            # Create default configuration file
            self._save_configuration()
            return
        
        try:
            with open(self.config_file, 'r') as f:
                config_data = json.load(f)
            
            # Parse configuration sections
            if 'servers' in config_data:
                for server_name, server_data in config_data['servers'].items():
                    # Remove 'name' from server_data if it exists to avoid duplicate
                    server_config_data = server_data.copy()
                    server_config_data.pop('name', None)
                    
                    self.config.servers[server_name] = ServerConfig(
                        name=server_name,
                        **server_config_data
                    )
                    self.config_sources[f"servers.{server_name}"] = ConfigSource.FILE
            
            if 'monitoring' in config_data:
                self.config.monitoring = MonitoringConfig(**config_data['monitoring'])
                self.config_sources['monitoring'] = ConfigSource.FILE
            
            if 'security' in config_data:
                self.config.security = SecurityConfig(**config_data['security'])
                self.config_sources['security'] = ConfigSource.FILE
            
            if 'logging' in config_data:
                self.config.logging = LoggingConfig(**config_data['logging'])
                self.config_sources['logging'] = ConfigSource.FILE
            
            if 'global_settings' in config_data:
                self.config.global_settings.update(config_data['global_settings'])
                self.config_sources['global_settings'] = ConfigSource.FILE
            
            self._last_modified = self.config_file.stat().st_mtime
            
        except Exception as e:
            self.logger.error(f"Failed to load configuration file: {e}")
            raise
    
    def _load_from_environment(self):
        """Load configuration from environment variables."""
        env_mappings = {
            'MCP_DEBUG_MODE': ('global_settings.debug_mode', bool),
            'MCP_DEVELOPMENT_MODE': ('global_settings.development_mode', bool),
            'MCP_ENVIRONMENT': ('global_settings.environment', str),
            'MCP_MONITORING_ENABLED': ('monitoring.enabled', bool),
            'MCP_MONITORING_INTERVAL': ('monitoring.metrics_collection_interval', int),
            'MCP_DASHBOARD_ENABLED': ('monitoring.dashboard_enabled', bool),
            'MCP_DASHBOARD_PORT': ('monitoring.dashboard_port', int),
            'MCP_SECURITY_AUTH_ENABLED': ('security.authentication_enabled', bool),
            'MCP_SECURITY_API_KEY_REQUIRED': ('security.api_key_required', bool),
            'MCP_SECURITY_RATE_LIMITING': ('security.rate_limiting_enabled', bool),
            'MCP_SECURITY_MAX_REQUESTS': ('security.max_requests_per_minute', int),
            'MCP_LOGGING_LEVEL': ('logging.level', str),
            'MCP_LOGGING_FILE_PATH': ('logging.file_path', str),
            'MCP_LOGGING_CONSOLE_ENABLED': ('logging.console_enabled', bool),
        }
        
        for env_var, (config_path, config_type) in env_mappings.items():
            if env_var in os.environ:
                value = os.environ[env_var]
                
                # Convert value to appropriate type
                if config_type == bool:
                    value = value.lower() in ('true', '1', 'yes', 'on')
                elif config_type == int:
                    value = int(value)
                elif config_type == float:
                    value = float(value)
                
                # Set configuration value
                self._set_config_value(config_path, value)
                self.config_sources[config_path] = ConfigSource.ENVIRONMENT
    
    def _set_config_value(self, path: str, value: Any):
        """Set a configuration value using dot notation."""
        parts = path.split('.')
        obj = self.config
        
        for part in parts[:-1]:
            if hasattr(obj, part):
                obj = getattr(obj, part)
            else:
                return  # Path doesn't exist
        
        if hasattr(obj, parts[-1]):
            setattr(obj, parts[-1], value)
    
    def _validate_configuration(self):
        """Validate configuration values."""
        # Validate server configurations
        for server_name, server_config in self.config.servers.items():
            if server_config.max_connections < 1:
                raise ValueError(f"Invalid max_connections for {server_name}: {server_config.max_connections}")
            
            if server_config.request_timeout < 1:
                raise ValueError(f"Invalid request_timeout for {server_name}: {server_config.request_timeout}")
            
            if server_config.health_check_interval < 10:
                raise ValueError(f"Invalid health_check_interval for {server_name}: {server_config.health_check_interval}")
        
        # Validate monitoring configuration
        if self.config.monitoring.metrics_collection_interval < 1:
            raise ValueError(f"Invalid metrics_collection_interval: {self.config.monitoring.metrics_collection_interval}")
        
        if self.config.monitoring.dashboard_port < 1 or self.config.monitoring.dashboard_port > 65535:
            raise ValueError(f"Invalid dashboard_port: {self.config.monitoring.dashboard_port}")
        
        # Validate security configuration
        if self.config.security.max_requests_per_minute < 1:
            raise ValueError(f"Invalid max_requests_per_minute: {self.config.security.max_requests_per_minute}")
        
        # Validate logging configuration
        valid_log_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
        if self.config.logging.level not in valid_log_levels:
            raise ValueError(f"Invalid log level: {self.config.logging.level}")
        
        if self.config.logging.max_file_size < 1024:  # 1KB minimum
            raise ValueError(f"Invalid max_file_size: {self.config.logging.max_file_size}")
    
    def _save_configuration(self):
        """Save current configuration to file."""
        try:
            config_data = {
                'servers': {name: asdict(config) for name, config in self.config.servers.items()},
                'monitoring': asdict(self.config.monitoring),
                'security': asdict(self.config.security),
                'logging': asdict(self.config.logging),
                'global_settings': self.config.global_settings
            }
            
            with open(self.config_file, 'w') as f:
                json.dump(config_data, f, indent=2)
            
            self.logger.info(f"Configuration saved to {self.config_file}")
            
        except Exception as e:
            self.logger.error(f"Failed to save configuration: {e}")
            raise
    
    def _watch_config_file(self):
        """Watch configuration file for changes."""
        def watch_thread():
            while True:
                try:
                    if self.config_file.exists():
                        current_mtime = self.config_file.stat().st_mtime
                        if current_mtime > self._last_modified:
                            self.logger.info("Configuration file changed, reloading...")
                            self._load_configuration()
                    
                    time.sleep(self._check_interval)
                    
                except Exception as e:
                    self.logger.error(f"Error watching config file: {e}")
                    time.sleep(self._check_interval)
        
        import threading
        watch_thread = threading.Thread(target=watch_thread, daemon=True)
        watch_thread.start()
    
    def get_server_config(self, server_name: str) -> Optional[ServerConfig]:
        """Get configuration for a specific server."""
        with self.lock:
            return self.config.servers.get(server_name)
    
    def get_monitoring_config(self) -> MonitoringConfig:
        """Get monitoring configuration."""
        with self.lock:
            return self.config.monitoring
    
    def get_security_config(self) -> SecurityConfig:
        """Get security configuration."""
        with self.lock:
            return self.config.security
    
    def get_logging_config(self) -> LoggingConfig:
        """Get logging configuration."""
        with self.lock:
            return self.config.logging
    
    def get_global_settings(self) -> Dict[str, Any]:
        """Get global settings."""
        with self.lock:
            return self.config.global_settings.copy()
    
    def update_server_config(self, server_name: str, **kwargs):
        """Update configuration for a specific server."""
        with self.lock:
            if server_name not in self.config.servers:
                self.config.servers[server_name] = ServerConfig(name=server_name)
            
            server_config = self.config.servers[server_name]
            for key, value in kwargs.items():
                if hasattr(server_config, key):
                    setattr(server_config, key, value)
                    self.config_sources[f"servers.{server_name}.{key}"] = ConfigSource.RUNTIME
            
            self._save_configuration()
            self.logger.info(f"Updated configuration for server {server_name}")
    
    def update_monitoring_config(self, **kwargs):
        """Update monitoring configuration."""
        with self.lock:
            for key, value in kwargs.items():
                if hasattr(self.config.monitoring, key):
                    setattr(self.config.monitoring, key, value)
                    self.config_sources[f"monitoring.{key}"] = ConfigSource.RUNTIME
            
            self._save_configuration()
            self.logger.info("Updated monitoring configuration")
    
    def update_security_config(self, **kwargs):
        """Update security configuration."""
        with self.lock:
            for key, value in kwargs.items():
                if hasattr(self.config.security, key):
                    setattr(self.config.security, key, value)
                    self.config_sources[f"security.{key}"] = ConfigSource.RUNTIME
            
            self._save_configuration()
            self.logger.info("Updated security configuration")
    
    def update_logging_config(self, **kwargs):
        """Update logging configuration."""
        with self.lock:
            for key, value in kwargs.items():
                if hasattr(self.config.logging, key):
                    setattr(self.config.logging, key, value)
                    self.config_sources[f"logging.{key}"] = ConfigSource.RUNTIME
            
            self._save_configuration()
            self.logger.info("Updated logging configuration")
    
    def update_global_settings(self, **kwargs):
        """Update global settings."""
        with self.lock:
            self.config.global_settings.update(kwargs)
            for key in kwargs:
                self.config_sources[f"global_settings.{key}"] = ConfigSource.RUNTIME
            
            self._save_configuration()
            self.logger.info("Updated global settings")
    
    def get_config_summary(self) -> Dict[str, Any]:
        """Get a summary of current configuration."""
        with self.lock:
            return {
                'servers': {name: asdict(config) for name, config in self.config.servers.items()},
                'monitoring': asdict(self.config.monitoring),
                'security': asdict(self.config.security),
                'logging': asdict(self.config.logging),
                'global_settings': self.config.global_settings,
                'config_sources': self.config_sources,
                'config_file': str(self.config_file),
                'last_modified': self._last_modified
            }
    
    def export_config(self, format: str = 'json') -> str:
        """Export configuration in various formats."""
        config_data = self.get_config_summary()
        
        if format == 'json':
            return json.dumps(config_data, indent=2)
        elif format == 'yaml':
            try:
                import yaml
                return yaml.dump(config_data, default_flow_style=False)
            except ImportError:
                raise ValueError("PyYAML not installed, cannot export to YAML")
        else:
            raise ValueError(f"Unsupported format: {format}")
    
    def import_config(self, config_str: str, format: str = 'json'):
        """Import configuration from string."""
        try:
            if format == 'json':
                config_data = json.loads(config_str)
            elif format == 'yaml':
                import yaml
                config_data = yaml.safe_load(config_str)
            else:
                raise ValueError(f"Unsupported format: {format}")
            
            # Validate and apply configuration
            with self.lock:
                # Backup current configuration
                backup_config = self.config
                
                try:
                    # Parse and apply new configuration
                    if 'servers' in config_data:
                        for server_name, server_data in config_data['servers'].items():
                            self.config.servers[server_name] = ServerConfig(
                                name=server_name,
                                **server_data
                            )
                    
                    if 'monitoring' in config_data:
                        self.config.monitoring = MonitoringConfig(**config_data['monitoring'])
                    
                    if 'security' in config_data:
                        self.config.security = SecurityConfig(**config_data['security'])
                    
                    if 'logging' in config_data:
                        self.config.logging = LoggingConfig(**config_data['logging'])
                    
                    if 'global_settings' in config_data:
                        self.config.global_settings.update(config_data['global_settings'])
                    
                    # Validate new configuration
                    self._validate_configuration()
                    
                    # Save if validation passes
                    self._save_configuration()
                    
                    self.logger.info("Configuration imported successfully")
                    
                except Exception as e:
                    # Restore backup on error
                    self.config = backup_config
                    self.logger.error(f"Failed to import configuration: {e}")
                    raise
                    
        except Exception as e:
            self.logger.error(f"Failed to import configuration: {e}")
            raise
    
    def reset_to_defaults(self):
        """Reset configuration to default values."""
        with self.lock:
            self.config = MCPServerConfig()
            self.config_sources = {}
            self._save_configuration()
            self.logger.info("Configuration reset to defaults")

# Global configuration manager instance
config_manager = ConfigManager()