"""
Input validation and sanitization utilities for the Pixel API.

Provides:
- SQL injection prevention
- XSS (Cross-Site Scripting) prevention
- Input sanitization for user-provided content
- Path traversal prevention
"""
import re
import html
import logging
from typing import Any, Optional
from urllib.parse import quote, unquote

logger = logging.getLogger(__name__)

# SQL注入 patterns to detect
SQL_INJECTION_PATTERNS = [
    r"(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE|UNION|DECLARE)\b)",
    r"(--|;|\/\*|\*\/)",
    r"(\bOR\b.*=.*)",
    r"(\bAND\b.*=.*)",
    r"('|\"|`)",
]

# 跨站脚本攻击 patterns to detect
XSS_PATTERNS = [
    r"<script[^>]*>.*?</script>",
    r"javascript:",
    r"on\w+\s*=",  # 事件 handlers like onclick, onload, etc.
    r"<iframe[^>]*>",
    r"<object[^>]*>",
    r"<embed[^>]*>",
]

# 路径 traversal patterns
PATH_TRAVERSAL_PATTERNS = [
    r"\.\./",
    r"\.\.",
    r"%2e%2e",
    r"%252e%252e",
]


def sanitize_sql_input(value: str) -> str:
    """ 清理 input to prevent SQL injection.
    
    Note: This is a defense-in-depth measure. The primary defense
    should be using parameterized queries/ORM.
    
    Args:
        value: Input string to sanitize
        
    Returns:
        Sanitized string
    """
    if not isinstance(value, str):
        return value
    
    # Check for SQL injection patterns
    for pattern in SQL_INJECTION_PATTERNS:
        if re.search(pattern, value, re.IGNORECASE):
            logger.warning(
                f"Potential SQL injection attempt detected",
                extra={'pattern': pattern, 'value_preview': value[:100]}
            )
            # Remove suspicious patterns
            value = re.sub(pattern, '', value, flags=re.IGNORECASE)
    
    return value


def sanitize_xss_input(value: str, allow_html: bool = False) -> str:
    """ 清理 input to prevent XSS attacks.
    
    Args:
        value: Input string to sanitize
        allow_html: If True, only escape dangerous HTML. If False, escape all HTML.
        
    Returns:
        Sanitized string
    """
    if not isinstance(value, str):
        return value
    
    # Check for XSS patterns
    for pattern in XSS_PATTERNS:
        if re.search(pattern, value, re.IGNORECASE):
            logger.warning(
                f"Potential XSS attempt detected",
                extra={'pattern': pattern, 'value_preview': value[:100]}
            )
            # Remove suspicious patterns
            value = re.sub(pattern, '', value, flags=re.IGNORECASE)
    
    if not allow_html:
        # Escape all HTML entities
        value = html.escape(value)
    
    return value


def sanitize_path(path: str) -> str:
    """ 清理 file path to prevent path traversal attacks.
    
    Args:
        path: File path to sanitize
        
    Returns:
        Sanitized path
    """
    if not isinstance(path, str):
        return path
    
    # Check for path traversal patterns
    for pattern in PATH_TRAVERSAL_PATTERNS:
        if re.search(pattern, path, re.IGNORECASE):
            logger.warning(
                f"Potential path traversal attempt detected",
                extra={'pattern': pattern, 'path': path}
            )
            # Remove suspicious patterns
            path = re.sub(pattern, '', path, flags=re.IGNORECASE)
    
    # Remove leading slashes to prevent absolute path access
    path = path.lstrip('/')
    
    # 正常ize path separators
    path = path.replace('\\', '/')
    
    return path


def sanitize_filename(filename: str) -> str:
    """ 清理 filename to prevent security issues.
    
    Args:
        filename: Filename to sanitize
        
    Returns:
        Sanitized filename
    """
    if not isinstance(filename, str):
        return filename
    
    # Remove path components
    filename = filename.split('/')[-1].split('\\')[-1]
    
    # Remove dangerous characters
    filename = re.sub(r'[^\w\s\-\.]', '', filename)
    
    # Limit length
    if len(filename) > 255:
        name, ext = filename.rsplit('.', 1) if '.' in filename else (filename, '')
        filename = name[:250] + ('.' + ext if ext else '')
    
    return filename


def validate_email(email: str) -> bool:
    """ 验证 email address format.
    
    Args:
        email: Email address to validate
        
    Returns:
        True if valid, False otherwise
    """
    if not isinstance(email, str):
        return False
    
    # 简单 email validation regex
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))


def validate_url(url: str, allowed_schemes: Optional[list] = None) -> bool:
    """ 验证 URL format and scheme.
    
    Args:
        url: URL to validate
        allowed_schemes: List of allowed URL schemes (default: ['http', 'https'])
        
    Returns:
        True if valid, False otherwise
    """
    if not isinstance(url, str):
        return False
    
    if allowed_schemes is None:
        allowed_schemes = ['http', 'https']
    
    # Check scheme
    if not any(url.startswith(f'{scheme}://') for scheme in allowed_schemes):
        return False
    
    # 基本 URL validation
    pattern = r'^https?://[^\s/$.?#].[^\s]*$'
    return bool(re.match(pattern, url, re.IGNORECASE))


def sanitize_user_input(
    value: Any,
    max_length: Optional[int] = None,
    allow_html: bool = False,
    strip_whitespace: bool = True
) -> Any:
    """
    General-purpose input sanitization for user-provided content.
    
    Args:
        value: Input value to sanitize
        max_length: Maximum allowed length (None for no limit)
        allow_html: Whether to allow HTML content
        strip_whitespace: Whether to strip leading/trailing whitespace
        
    Returns:
        Sanitized value
    """
    # Handle non-string types
    if not isinstance(value, str):
        return value
    
    # Strip whitespace
    if strip_whitespace:
        value = value.strip()
    
    # 清理 SQL injection
    value = sanitize_sql_input(value)
    
    # 清理 XSS
    value = sanitize_xss_input(value, allow_html=allow_html)
    
    # Enforce max length
    if max_length and len(value) > max_length:
        logger.warning(
            f"Input exceeds max length",
            extra={'max_length': max_length, 'actual_length': len(value)}
        )
        value = value[:max_length]
    
    return value


def validate_json_structure(data: dict, required_fields: list, optional_fields: list = None) -> tuple[bool, Optional[str]]:
    """ 验证 JSON structure has required fields.
    
    Args:
        data: Dictionary to validate
        required_fields: List of required field names
        optional_fields: List of optional field names
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if not isinstance(data, dict):
        return False, "Input must be a dictionary"
    
    # Check required fields
    missing_fields = [field for field in required_fields if field not in data]
    if missing_fields:
        return False, f"Missing required fields: {', '.join(missing_fields)}"
    
    # Check for unexpected fields
    if optional_fields is not None:
        allowed_fields = set(required_fields + optional_fields)
        unexpected_fields = [field for field in data.keys() if field not in allowed_fields]
        if unexpected_fields:
            logger.warning(
                f"Unexpected fields in input",
                extra={'unexpected_fields': unexpected_fields}
            )
    
    return True, None


def sanitize_dict_values(data: dict, max_length: Optional[int] = None, allow_html: bool = False) -> dict:
    """
    Recursively sanitize all string values in a dictionary.
    
    Args:
        data: Dictionary to sanitize
        max_length: Maximum allowed length for string values
        allow_html: Whether to allow HTML content
        
    Returns:
        Sanitized dictionary
    """
    if not isinstance(data, dict):
        return data
    
    sanitized = {}
    for key, value in data.items():
        if isinstance(value, str):
            sanitized[key] = sanitize_user_input(value, max_length=max_length, allow_html=allow_html)
        elif isinstance(value, dict):
            sanitized[key] = sanitize_dict_values(value, max_length=max_length, allow_html=allow_html)
        elif isinstance(value, list):
            sanitized[key] = [
                sanitize_user_input(item, max_length=max_length, allow_html=allow_html)
                if isinstance(item, str)
                else sanitize_dict_values(item, max_length=max_length, allow_html=allow_html)
                if isinstance(item, dict)
                else item
                for item in value
            ]
        else:
            sanitized[key] = value
    
    return sanitized


# 装饰器 for automatic input sanitization
def sanitize_inputs(max_length: Optional[int] = None, allow_html: bool = False):
    """ 装饰器 to automatically sanitize function inputs.
    
    Args:
        max_length: Maximum allowed length for string values
        allow_html: Whether to allow HTML content
        
    Example:
        @sanitize_inputs(max_length=1000)
        async def create_project(name: str, description: str):
            # name and description are automatically sanitized
            pass
    """
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # 清理 kwargs
            sanitized_kwargs = {}
            for key, value in kwargs.items():
                if isinstance(value, str):
                    sanitized_kwargs[key] = sanitize_user_input(
                        value,
                        max_length=max_length,
                        allow_html=allow_html
                    )
                elif isinstance(value, dict):
                    sanitized_kwargs[key] = sanitize_dict_values(
                        value,
                        max_length=max_length,
                        allow_html=allow_html
                    )
                else:
                    sanitized_kwargs[key] = value
            
            return await func(*args, **sanitized_kwargs)
        
        return wrapper
    return decorator
