"""
Configuration validation and loading for health checker.
"""

import json
import logging
from typing import Dict, Any, List, Optional
from jsonschema import validate, ValidationError, Draft7Validator
from urllib.parse import urlparse

logger = logging.getLogger(__name__)

# JSON Schema for configuration validation
CONFIG_SCHEMA = {
    "type": "object",
    "properties": {
        "services": {
            "type": "array",
            "minItems": 1,
            "items": {
                "type": "object",
                "required": ["name", "url", "health_endpoint"],
                "properties": {
                    "name": {
                        "type": "string",
                        "minLength": 1,
                        "description": "Human-readable name of the service"
                    },
                    "url": {
                        "type": "string",
                        "description": "Base URL of the service"
                    },
                    "health_endpoint": {
                        "type": "string",
                        "description": "Health check endpoint path"
                    },
                    "interval": {
                        "type": "number",
                        "minimum": 1,
                        "default": 60,
                        "description": "Health check interval in seconds"
                    },
                    "timeout": {
                        "type": "number",
                        "minimum": 1,
                        "default": 10,
                        "description": "Request timeout in seconds"
                    },
                    "max_retries": {
                        "type": "integer",
                        "minimum": 1,
                        "default": 3,
                        "description": "Maximum retry attempts"
                    },
                    "expected_status_code": {
                        "type": "integer",
                        "minimum": 100,
                        "maximum": 599,
                        "default": 200,
                        "description": "Expected HTTP status code"
                    },
                    "web_interface": {
                        "type": "object",
                        "properties": {
                            "enabled": {
                                "type": "boolean",
                                "default": False
                            }
                        },
                        "additionalProperties": False
                    },
                    "slack_integration": {
                        "type": "object",
                        "properties": {
                            "enabled": {
                                "type": "boolean",
                                "default": False
                            },
                            "channel": {
                                "type": "string",
                                "description": "Slack channel for alerts"
                            },
                            "api_key": {
                                "type": "string",
                                "description": "Slack API key"
                            },
                            "webhook_url": {
                                "type": "string",
                                "description": "Slack webhook URL"
                            },
                            "message_template": {
                                "type": "string",
                                "description": "Message template for alerts"
                            }
                        },
                        "required": ["enabled", "channel"],
                        "additionalProperties": False
                    },
                    "email_alert": {
                        "type": "object",
                        "properties": {
                            "enabled": {
                                "type": "boolean",
                                "default": False
                            },
                            "recipients": {
                                "type": "array",
                                "items": {
                                    "type": "string",
                                    "format": "email"
                                },
                                "minItems": 1,
                                "description": "List of email recipients"
                            },
                            "smtp_server": {
                                "type": "string",
                                "description": "SMTP server address"
                            },
                            "smtp_port": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 65535,
                                "description": "SMTP server port"
                            },
                            "username": {
                                "type": "string",
                                "description": "SMTP username"
                            },
                            "app_password": {
                                "type": "string",
                                "description": "SMTP app password"
                            }
                        },
                        "required": ["enabled", "recipients", "smtp_server", "smtp_port", "username", "app_password"],
                        "additionalProperties": False
                    }
                },
                "additionalProperties": False
            }
        }
    },
    "required": ["services"],
    "additionalProperties": False
}


class ConfigValidationError(Exception):
    """Raised when configuration validation fails."""
    pass


class ConfigValidator:
    """Validates and loads health checker configuration."""
    
    def __init__(self):
        self.schema = CONFIG_SCHEMA
        self.validator = Draft7Validator(self.schema)
    
    def validate_url(self, url: str) -> bool:
        """
        Validate URL format.
        
        Args:
            url: URL to validate
            
        Returns:
            True if URL is valid, False otherwise
        """
        try:
            result = urlparse(url)
            return all([result.scheme in ('http', 'https'), result.netloc])
        except Exception:
            return False
    
    def validate_config(self, config: Dict[str, Any]) -> List[str]:
        """
        Validate configuration against schema and additional checks.
        
        Args:
            config: Configuration dictionary
            
        Returns:
            List of validation error messages
            
        Raises:
            ConfigValidationError: If validation fails
        """
        errors = []
        
        # Schema validation
        try:
            validate(instance=config, schema=self.schema)
        except ValidationError as e:
            errors.append(f"Schema validation error: {e.message}")
        
        # Additional custom validations
        for i, service in enumerate(config.get('services', [])):
            service_name = service.get('name', f'Service {i + 1}')
            
            # URL validation
            url = service.get('url', '')
            if not self.validate_url(url):
                errors.append(f"Invalid URL format in '{service_name}' field: {url}")
            
            # Health endpoint validation
            health_endpoint = service.get('health_endpoint', '')
            if not health_endpoint.startswith('/'):
                errors.append(f"Health endpoint must start with '/' in '{service_name}': {health_endpoint}")
            
            # Slack integration validation
            slack_config = service.get('slack_integration', {})
            if slack_config.get('enabled', False):
                if not slack_config.get('api_key') and not slack_config.get('webhook_url'):
                    errors.append(f"Slack integration enabled but missing either API key or webhook URL in '{service_name}'")
                if not slack_config.get('channel'):
                    errors.append(f"Slack integration enabled but missing channel in '{service_name}'")
            
            # Email alert validation
            email_config = service.get('email_alert', {})
            if email_config.get('enabled', False):
                if not email_config.get('recipients'):
                    errors.append(f"Email alerts enabled but missing recipients in '{service_name}'")
                if not email_config.get('smtp_server'):
                    errors.append(f"Email alerts enabled but missing SMTP server in '{service_name}'")
        
        if errors:
            raise ConfigValidationError(f"Configuration validation failed: {'; '.join(errors)}")
        
        return []
    
    def load_config(self, config_path: str) -> Dict[str, Any]:
        """
        Load and validate configuration from file.
        
        Args:
            config_path: Path to configuration file
            
        Returns:
            Validated configuration dictionary
            
        Raises:
            ConfigValidationError: If loading or validation fails
        """
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
        except json.JSONDecodeError as e:
            raise ConfigValidationError(f"Invalid JSON format: {e}")
        except FileNotFoundError:
            raise ConfigValidationError(f"Configuration file not found: {config_path}")
        except Exception as e:
            raise ConfigValidationError(f"Error reading configuration file: {e}")
        
        self.validate_config(config)
        logger.info(f"Configuration loaded and validated successfully from {config_path}")
        return config
    
    def validate_config_direct(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """
        Validate a pre-loaded configuration dictionary.
        
        Args:
            config: Configuration dictionary to validate
            
        Returns:
            Validated configuration dictionary
            
        Raises:
            ConfigValidationError: If validation fails
        """
        self.validate_config(config)
        logger.info("Configuration validated successfully")
        return config
    
    def get_default_config(self) -> Dict[str, Any]:
        """
        Get default configuration template.
        
        Returns:
            Default configuration dictionary
        """
        return {
            "services": [
                {
                    "name": "Example Service",
                    "url": "https://example.com",
                    "health_endpoint": "/health",
                    "interval": 60,
                    "timeout": 10,
                    "max_retries": 3,
                    "expected_status_code": 200,
                    "web_interface": {
                        "enabled": True
                    },
                    "slack_integration": {
                        "enabled": False,
                        "channel": "alerts",
                        "api_key": "your-slack-api-key",
                        "message_template": "🚨 Service {name} is down. Status: {status}"
                    },
                    "email_alert": {
                        "enabled": False,
                        "recipients": ["admin@example.com"],
                        "smtp_server": "smtp.gmail.com",
                        "smtp_port": 587,
                        "username": "monitor@example.com",
                        "app_password": "your-app-password"
                    }
                }
            ]
        }


# Standalone functions for external use
def load_config(config_path: str) -> Dict[str, Any]:
    """
    Load and validate configuration from file.
    
    Args:
        config_path: Path to configuration file
        
    Returns:
        Validated configuration dictionary
        
    Raises:
        ConfigValidationError: If loading or validation fails
    """
    validator = ConfigValidator()
    return validator.load_config(config_path)


def validate_config(config: Dict[str, Any]) -> None:
    """
    Validate a pre-loaded configuration dictionary.
    
    Args:
        config: Configuration dictionary to validate
        
    Raises:
        ConfigValidationError: If validation fails
    """
    validator = ConfigValidator()
    validator.validate_config_direct(config)