"""
Secret management configuration for thrillwiki project.

This module provides patterns for secure secret handling including:
- Secret validation
- Secret rotation support
- Integration points for secret management services
- Secure fallback to environment variables

For production, consider integrating with:
- AWS Secrets Manager
- HashiCorp Vault
- Google Secret Manager
- Azure Key Vault

Why python-decouple?
- Already used across the project for consistency
- Provides secure environment variable handling
- Supports .env files and environment variables
"""

import logging
import warnings

from decouple import UndefinedValueError, config

logger = logging.getLogger("security")

# =============================================================================
# Secret Configuration
# =============================================================================

# Enable secret rotation checking (set to True in production)
SECRET_ROTATION_ENABLED = config("SECRET_ROTATION_ENABLED", default=False, cast=bool)

# Secret version for tracking rotations
SECRET_KEY_VERSION = config("SECRET_KEY_VERSION", default="1")

# Secret expiry warning threshold (days before expiry to start warning)
SECRET_EXPIRY_WARNING_DAYS = config("SECRET_EXPIRY_WARNING_DAYS", default=30, cast=int)

# =============================================================================
# Required Secrets Registry
# =============================================================================
# List of required secrets with validation rules

REQUIRED_SECRETS = {
    "SECRET_KEY": {
        "min_length": 50,
        "description": "Django secret key for cryptographic signing",
        "rotation_period_days": 90,
    },
    "DATABASE_URL": {
        "min_length": 10,
        "description": "Database connection URL",
        "contains_password": True,
    },
}

# Optional secrets that should be validated if present
OPTIONAL_SECRETS = {
    "SENTRY_DSN": {
        "min_length": 10,
        "description": "Sentry error tracking DSN",
    },
    "CLOUDFLARE_IMAGES_API_TOKEN": {
        "min_length": 20,
        "description": "Cloudflare Images API token",
    },
    "FORWARD_EMAIL_API_KEY": {
        "min_length": 10,
        "description": "ForwardEmail API key",
    },
    "TURNSTILE_SECRET_KEY": {
        "min_length": 10,
        "description": "Cloudflare Turnstile secret key",
    },
}


# =============================================================================
# Secret Validation Functions
# =============================================================================


def validate_secret_strength(name: str, value: str, min_length: int = 10) -> bool:
    """
    Validate that a secret meets minimum strength requirements.

    Args:
        name: Name of the secret (for logging)
        value: The secret value to validate
        min_length: Minimum required length

    Returns:
        bool: True if valid, False otherwise
    """
    if not value:
        logger.error(f"Secret '{name}' is empty or not set")
        return False

    if len(value) < min_length:
        logger.error(f"Secret '{name}' is too short ({len(value)} chars, " f"minimum {min_length})")
        return False

    # Check for placeholder values
    placeholder_patterns = [
        "your-secret-key",
        "change-me",
        "placeholder",
        "example",
        "xxx",
        "todo",
    ]

    value_lower = value.lower()
    for pattern in placeholder_patterns:
        if pattern in value_lower:
            logger.warning(f"Secret '{name}' appears to contain a placeholder value")
            return False

    return True


def validate_secret_key(secret_key: str) -> bool:
    """
    Validate Django SECRET_KEY meets security requirements.

    Requirements:
    - At least 50 characters
    - Contains mixed case letters
    - Contains numbers
    - Contains special characters

    Args:
        secret_key: The SECRET_KEY value

    Returns:
        bool: True if valid, False otherwise
    """
    if len(secret_key) < 50:
        logger.error(f"SECRET_KEY is too short ({len(secret_key)} chars, minimum 50)")
        return False

    has_upper = any(c.isupper() for c in secret_key)
    has_lower = any(c.islower() for c in secret_key)
    has_digit = any(c.isdigit() for c in secret_key)
    has_special = any(not c.isalnum() for c in secret_key)

    if not all([has_upper, has_lower, has_digit, has_special]):
        logger.warning("SECRET_KEY should contain uppercase, lowercase, digits, " "and special characters")
        # Don't fail, just warn - some generated keys may not have all

    return True


def get_secret(
    name: str,
    default: str | None = None,
    required: bool = True,
    min_length: int = 0,
) -> str | None:
    """
    Safely retrieve a secret with validation.

    Args:
        name: Environment variable name
        default: Default value if not set
        required: Whether the secret is required
        min_length: Minimum required length

    Returns:
        The secret value or None if not found and not required

    Raises:
        ValueError: If required secret is missing or invalid
    """
    try:
        value = config(name, default=default)
    except UndefinedValueError:
        if required:
            raise ValueError(f"Required secret '{name}' is not set") from None
        return default

    if value and min_length > 0 and not validate_secret_strength(name, value, min_length):
        if required:
            raise ValueError(f"Secret '{name}' does not meet requirements")
        return default

    return value


def validate_required_secrets(raise_on_error: bool = False) -> list[str]:
    """
    Validate all required secrets are set and meet requirements.

    Args:
        raise_on_error: If True, raise ValueError on first error

    Returns:
        List of error messages (empty if all valid)
    """
    errors = []

    for name, rules in REQUIRED_SECRETS.items():
        try:
            value = config(name)
            min_length = rules.get("min_length", 0)

            if not validate_secret_strength(name, value, min_length):
                msg = f"Secret '{name}' validation failed"
                errors.append(msg)
                if raise_on_error:
                    raise ValueError(msg)

        except UndefinedValueError:
            msg = f"Required secret '{name}' is not set: {rules['description']}"
            errors.append(msg)
            if raise_on_error:
                raise ValueError(msg) from None

    return errors


def check_secret_expiry() -> list[str]:
    """
    Check if any secrets are approaching expiry.

    This is a placeholder for integration with secret management services
    that track secret expiry dates.

    Returns:
        List of warning messages for secrets approaching expiry
    """
    warnings_list = []

    # Placeholder: In production, integrate with your secret manager
    # to check actual expiry dates

    # Example check based on version
    if SECRET_ROTATION_ENABLED:
        try:
            version = int(SECRET_KEY_VERSION)
            # If version is very old, suggest rotation
            if version < 2:
                warnings_list.append("SECRET_KEY version is old. Consider rotating secrets.")
        except ValueError:
            pass

    return warnings_list


# =============================================================================
# Secret Provider Integration Points
# =============================================================================


class SecretProvider:
    """
    Base class for secret provider integrations.

    Subclass this to integrate with secret management services:
    - AWS Secrets Manager
    - HashiCorp Vault
    - Google Secret Manager
    - Azure Key Vault
    """

    def get_secret(self, name: str) -> str | None:
        """Retrieve a secret by name."""
        raise NotImplementedError

    def set_secret(self, name: str, value: str) -> bool:
        """Set a secret value."""
        raise NotImplementedError

    def rotate_secret(self, name: str) -> str:
        """Rotate a secret and return the new value."""
        raise NotImplementedError

    def list_secrets(self) -> list[str]:
        """List all available secrets."""
        raise NotImplementedError


class EnvironmentSecretProvider(SecretProvider):
    """
    Default secret provider using environment variables.

    This is the fallback provider for development and simple deployments.
    """

    def get_secret(self, name: str) -> str | None:
        """Retrieve a secret from environment variables."""
        try:
            return config(name)
        except UndefinedValueError:
            return None

    def set_secret(self, name: str, value: str) -> bool:
        """Environment variables are read-only at runtime."""
        logger.warning(
            f"Cannot set secret '{name}' in environment provider. " "Update your .env file or environment variables."
        )
        return False

    def rotate_secret(self, name: str) -> str:
        """Cannot rotate secrets in environment provider."""
        raise NotImplementedError(
            "Secret rotation is not supported for environment variables. "
            "Use a proper secret management service in production."
        )

    def list_secrets(self) -> list[str]:
        """List all known secret names."""
        return list(REQUIRED_SECRETS.keys()) + list(OPTIONAL_SECRETS.keys())


# Default provider instance
_secret_provider: SecretProvider = EnvironmentSecretProvider()


def get_secret_provider() -> SecretProvider:
    """Get the current secret provider instance."""
    return _secret_provider


def set_secret_provider(provider: SecretProvider) -> None:
    """Set a custom secret provider."""
    global _secret_provider
    _secret_provider = provider


# =============================================================================
# Startup Validation
# =============================================================================


def run_startup_validation() -> None:
    """
    Run secret validation on application startup.

    This function should be called during Django initialization
    to catch configuration errors early.
    """
    debug_mode = config("DEBUG", default=True, cast=bool)

    # Validate required secrets
    errors = validate_required_secrets(raise_on_error=not debug_mode)

    if errors:
        for error in errors:
            if debug_mode:
                warnings.warn(f"Secret validation warning: {error}", stacklevel=2)
            else:
                logger.error(f"Secret validation error: {error}")

    # Check for expiring secrets
    if SECRET_ROTATION_ENABLED:
        expiry_warnings = check_secret_expiry()
        for warning in expiry_warnings:
            logger.warning(f"Secret expiry: {warning}")

    # Validate SECRET_KEY specifically
    try:
        secret_key = config("SECRET_KEY")
        if not validate_secret_key(secret_key) and not debug_mode:
            raise ValueError("SECRET_KEY does not meet security requirements")
    except UndefinedValueError:
        if not debug_mode:
            raise ValueError("SECRET_KEY is required in production") from None
