"""
Input validation utilities.
Provides consistent validation across the application.
"""

from datetime import date, datetime
from decimal import Decimal, InvalidOperation
from typing import Any, Dict, List, Optional

from flask import request
from marshmallow import ValidationError


def validate_required(data: Dict[str, Any], fields: List[str]) -> Dict[str, Any]:
    """
    Validate that required fields are present.

    Args:
        data: Dictionary to validate
        fields: List of required field names

    Returns:
        dict with 'valid' (bool) and 'errors' (list) keys

    Raises:
        ValidationError if validation fails
    """
    errors = []
    for field in fields:
        if field not in data or data[field] is None:
            errors.append(f"{field} is required")

    if errors:
        raise ValidationError(errors)

    return {"valid": True, "errors": []}


def validate_date_range(start_date: Any, end_date: Any) -> bool:
    """
    Validate that end_date is after start_date.

    Args:
        start_date: Start date (datetime, date, or string)
        end_date: End date (datetime, date, or string)

    Returns:
        True if valid

    Raises:
        ValidationError if invalid
    """
    if isinstance(start_date, str):
        start_date = datetime.fromisoformat(start_date.replace("Z", "+00:00"))
    if isinstance(end_date, str):
        end_date = datetime.fromisoformat(end_date.replace("Z", "+00:00"))

    if isinstance(start_date, datetime):
        start_date = start_date.date()
    if isinstance(end_date, datetime):
        end_date = end_date.date()

    if end_date <= start_date:
        raise ValidationError("end_date must be after start_date")

    return True


def validate_decimal(value: Any, min_value: Optional[Decimal] = None, max_value: Optional[Decimal] = None) -> Decimal:
    """
    Validate and convert a value to Decimal.

    Args:
        value: Value to validate
        min_value: Minimum allowed value
        max_value: Maximum allowed value

    Returns:
        Decimal value

    Raises:
        ValidationError if invalid
    """
    try:
        decimal_value = Decimal(str(value))
    except (ValueError, InvalidOperation, TypeError):
        raise ValidationError(f"Invalid decimal value: {value}")

    if min_value is not None and decimal_value < min_value:
        raise ValidationError(f"Value must be at least {min_value}")

    if max_value is not None and decimal_value > max_value:
        raise ValidationError(f"Value must be at most {max_value}")

    return decimal_value


def validate_integer(value: Any, min_value: Optional[int] = None, max_value: Optional[int] = None) -> int:
    """
    Validate and convert a value to integer.

    Args:
        value: Value to validate
        min_value: Minimum allowed value
        max_value: Maximum allowed value

    Returns:
        Integer value

    Raises:
        ValidationError if invalid
    """
    try:
        int_value = int(value)
    except (ValueError, TypeError):
        raise ValidationError(f"Invalid integer value: {value}")

    if min_value is not None and int_value < min_value:
        raise ValidationError(f"Value must be at least {min_value}")

    if max_value is not None and int_value > max_value:
        raise ValidationError(f"Value must be at most {max_value}")

    return int_value


def validate_string(value: Any, min_length: Optional[int] = None, max_length: Optional[int] = None) -> str:
    """
    Validate and convert a value to string.

    Args:
        value: Value to validate
        min_length: Minimum string length
        max_length: Maximum string length

    Returns:
        String value

    Raises:
        ValidationError if invalid
    """
    if value is None:
        raise ValidationError("String value cannot be None")

    str_value = str(value).strip()

    if min_length is not None and len(str_value) < min_length:
        raise ValidationError(f"String must be at least {min_length} characters")

    if max_length is not None and len(str_value) > max_length:
        raise ValidationError(f"String must be at most {max_length} characters")

    return str_value


def validate_email(email: str) -> str:
    """
    Validate email address format.

    Args:
        email: Email address to validate

    Returns:
        Validated email address

    Raises:
        ValidationError if invalid
    """
    import re

    email = email.strip().lower()
    pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"

    if not re.match(pattern, email):
        raise ValidationError(f"Invalid email address: {email}")

    return email


def validate_json_request() -> Dict[str, Any]:
    """
    Validate that request contains valid JSON.

    Returns:
        Parsed JSON data

    Raises:
        ValidationError if invalid
    """
    if not request.is_json:
        raise ValidationError("Request must contain JSON data")

    data = request.get_json()
    if data is None:
        raise ValidationError("Request JSON is empty")

    return data


def sanitize_input(value: str, max_length: Optional[int] = None) -> str:
    """
    Sanitize user input by removing dangerous characters.

    Args:
        value: Input string
        max_length: Maximum length to truncate to

    Returns:
        Sanitized string
    """
    import bleach

    # Remove HTML tags and dangerous characters
    sanitized = bleach.clean(value, tags=[], strip=True)

    # Truncate if needed
    if max_length and len(sanitized) > max_length:
        sanitized = sanitized[:max_length]

    return sanitized
