"""
Centralized configuration management with secure secret handling.
Implements fail-fast validation for required environment variables.
"""

import os
import re
import logging
from typing import Optional, Set
from functools import lru_cache
from pathlib import Path

from pydantic import Field, validator, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict

# Configure logging with secret redaction
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class ConfigurationError(Exception):
    """Raised when required configuration is missing or invalid."""
    pass


class SecretAuditError(Exception):
    """Raised when potential secrets are detected in source code."""
    pass


class DatabaseSettings(BaseSettings):
    """Database connection settings."""
    model_config = SettingsConfigDict(
        env_prefix="DB_",
        case_sensitive=False,
        extra='ignore'
    )
    
    host: str = Field(..., description="Database hostname")
    port: int = Field(5432, description="Database port")
    name: str = Field(..., description="Database name")
    user: str = Field(..., description="Database username")
    password: SecretStr = Field(..., description="Database password")
    ssl_mode: str = Field("require", description="SSL mode for connection")
    
    @validator('port')
    def validate_port(cls, v: int) -> int:
        if not 1 <= v <= 65535:
            raise ValueError(f"Invalid port number: {v}")
        return v


class AWSSettings(BaseSettings):
    """AWS credentials and configuration."""
    model_config = SettingsConfigDict(
        env_prefix="AWS_",
        case_sensitive=False,
        extra='ignore'
    )
    
    access_key_id: Optional[str] = Field(None, description="AWS Access Key ID")
    secret_access_key: Optional[SecretStr] = Field(None, description="AWS Secret Access Key")
    region: str = Field("us-east-1", description="AWS Region")
    s3_bucket: Optional[str] = Field(None, description="S3 Bucket name")
    
    @validator('access_key_id')
    def validate_aws_key_format(cls, v: Optional[str]) -> Optional[str]:
        if v and not re.match(r'^AKIA[0-9A-Z]{16}$', v):
            logger.warning("AWS Access Key ID does not match expected format")
        return v


class OpenAISettings(BaseSettings):
    """OpenAI API configuration."""
    model_config = SettingsConfigDict(
        env_prefix="OPENAI_",
        case_sensitive=False,
        extra='ignore'
    )
    
    api_key: SecretStr = Field(..., description="OpenAI API Key")
    model: str = Field("gpt-4", description="Model identifier")
    timeout: int = Field(30, description="Request timeout in seconds")
    
    @validator('api_key')
    def validate_api_key_format(cls, v: SecretStr) -> SecretStr:
        key = v.get_secret_value()
        if not key.startswith('sk-'):
            raise ValueError("OpenAI API key must start with 'sk-'")
        if len(key) < 20:
            raise ValueError("OpenAI API key appears to be truncated")
        return v


class ApplicationSettings(BaseSettings):
    """
    Main application settings aggregating all secret configurations.
    Implements strict validation to ensure no secrets are hardcoded.
    """
    model_config = SettingsConfigDict(
        env_file='.env',
        env_file_encoding='utf-8',
        case_sensitive=False,
        extra='ignore'
    )
    
    # Application metadata
    app_name: str = Field("RECEPTIONISTAI", description="Application identifier")
    debug: bool = Field(False, description="Debug mode flag")
    environment: str = Field(..., description="Environment (development/staging/production)")
    
    # Security settings
    secret_key: SecretStr = Field(..., description="Flask/Django secret key")
    jwt_secret: SecretStr = Field(..., description="JWT signing secret")
    encryption_key: Optional[SecretStr] = Field(None, description="Data encryption key")
    
    # Sub-configurations
    database: DatabaseSettings = Field(default_factory=lambda: DatabaseSettings())
    aws: AWSSettings = Field(default_factory=lambda: AWSSettings())
    openai: OpenAISettings = Field(default_factory=lambda: OpenAISettings())
    
    @validator('environment')
    def validate_environment(cls, v: str) -> str:
        allowed = {'development', 'staging', 'production', 'test'}
        if v.lower() not in allowed:
            raise ValueError(f"Environment must be one of: {allowed}")
        return v.lower()
    
    @validator('secret_key')
    def validate_secret_key_strength(cls, v: SecretStr) -> SecretStr:
        key = v.get_secret_value()
        if len(key) < 32:
            raise ValueError("Secret key must be at least 32 characters long")
        # Check entropy (basic check for complexity)
        if not any(c.isdigit() for c in key):
            raise ValueError("Secret key must contain at least one digit")
        if not any(c.isupper() for c in key):
            raise ValueError("Secret key must contain at least one uppercase letter")
        return v


@lru_cache()
def get_settings() -> ApplicationSettings:
    """
    Cached settings singleton to avoid reloading on every call.
    Thread-safe and efficient for production use.
    
    Returns:
        ApplicationSettings: Validated configuration object
        
    Raises:
        ConfigurationError: If required environment variables are missing
    """
    try:
        settings = ApplicationSettings()
        logger.info(
            f"Configuration loaded successfully for environment: {settings.environment}"
        )
        return settings
    except Exception as e:
        logger.error(f"Failed to load configuration: {e}")
        raise ConfigurationError(f"Configuration validation failed: {e}") from e


def validate_required_env_vars(required_vars: Set[str]) -> None:
    """
    Explicit validation for critical environment variables.
    Use during application startup before initializing connections.
    
    Args:
        required_vars: Set of environment variable names that must be present
        
    Raises:
        ConfigurationError: If any required variable is missing
    """
    missing = []
    for var in required_vars:
        value = os.environ.get(var)
        if not value or value.strip() == '':
            missing.append(var)
    
    if missing:
        raise ConfigurationError(
            f"Missing required environment variables: {', '.join(missing)}. "
            f"Please check your .env file or system environment."
        )


def redact_sensitive_data(data: dict, sensitive_keys: Optional[Set[str]] = None) -> dict:
    """
    Recursively redact sensitive values from dictionaries for logging.
    
    Args:
        data: Dictionary potentially containing secrets
        sensitive_keys: Keys to redact (defaults to common secret patterns)
        
    Returns:
        dict: Copy of data with sensitive values replaced with [REDACTED]
    """
    if sensitive_keys is None:
        sensitive_keys = {
            'password', 'secret', 'token', 'key', 'api_key', 'apikey',
            'access_key', 'private_key', 'credential', 'auth'
        }
    
    redacted = {}
    for key, value in data.items():
        key_lower = key.lower()
        if any(sensitive in key_lower for sensitive in sensitive_keys):
            redacted[key] = '[REDACTED]'
        elif isinstance(value, dict):
            redacted[key] = redact_sensitive_data(value, sensitive_keys)
        elif isinstance(value, SecretStr):
            redacted[key] = '[REDACTED]'
        else:
            redacted[key] = value
    return redacted