"""
Flask route decorators for automatic request validation using Pydantic
Provides Laravel-style validation decorators for Flask routes
"""

from functools import wraps
from typing import Type, Optional, Callable, Any, Dict
from flask import request, jsonify
from pydantic import BaseModel, ValidationError
from app.exceptions.validation_exception import ValidationException
from app.utils.response_helper import ApiResponse


def validate_json(schema: Type[BaseModel], message: str = "The given data was invalid."):
    """
    Decorator to validate JSON request body using Pydantic schema

    Args:
        schema: Pydantic BaseModel class for validation
        message: Custom error message for validation failures

    Usage:
        @validate_json(ActiveUsersRequest)
        def get_active_users(validated_data):
            # validated_data is now an ActiveUsersRequest instance
            pass
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                # Get JSON data from request. silent=True so a malformed/empty body
                # returns None (handled below as a 422) instead of raising BadRequest,
                # which the broad except would otherwise mask as a 500.
                json_data = request.get_json(silent=True)
                if json_data is None:
                    raise ValidationException.from_custom_errors(
                        {"json": ["Request must contain valid JSON data"]},
                        "Invalid JSON data"
                    )

                # Validate using Pydantic schema
                validated_data = schema(**json_data)

                # Call original function with validated data
                return f(*args, validated_json=validated_data, **kwargs)

            except ValidationError as e:
                # Convert Pydantic error to ValidationException
                validation_exception = ValidationException.from_pydantic_error(e, message)
                return ApiResponse.validation_error(validation_exception.errors, validation_exception.message)

            except ValidationException as e:
                # Handle custom validation exceptions
                return ApiResponse.validation_error(e.errors, e.message)

            except Exception as e:
                # Handle unexpected errors
                return ApiResponse.error(f"Validation failed: {str(e)}", status_code=500)

        return decorated_function
    return decorator


def validate_query(schema: Type[BaseModel], message: str = "Invalid query parameters."):
    """
    Decorator to validate query parameters using Pydantic schema

    Args:
        schema: Pydantic BaseModel class for validation
        message: Custom error message for validation failures

    Usage:
        @validate_query(PaginationRequest)
        def get_items(validated_query):
            # validated_query contains validated query parameters
            pass
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                # Get query parameters
                query_data = request.args.to_dict()

                # Convert string values to appropriate types for common cases
                for key, value in query_data.items():
                    # Try to convert numeric strings
                    if value.isdigit():
                        query_data[key] = int(value)
                    elif value.lower() in ['true', 'false']:
                        query_data[key] = value.lower() == 'true'

                # Validate using Pydantic schema
                validated_data = schema(**query_data)

                # Call original function with validated data
                return f(validated_data, *args, **kwargs)

            except ValidationError as e:
                validation_exception = ValidationException.from_pydantic_error(e, message)
                return ApiResponse.validation_error(validation_exception.errors, validation_exception.message)

            except ValidationException as e:
                return ApiResponse.validation_error(e.errors, e.message)

            except Exception as e:
                return ApiResponse.error(f"Query validation failed: {str(e)}", status_code=500)

        return decorated_function
    return decorator


def validate_form(schema: Type[BaseModel], message: str = "Invalid form data."):
    """
    Decorator to validate form data using Pydantic schema

    Args:
        schema: Pydantic BaseModel class for validation
        message: Custom error message for validation failures

    Usage:
        @validate_form(UserRegistrationRequest)
        def register_user(validated_form):
            # validated_form contains validated form data
            pass
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                # Get form data
                form_data = request.form.to_dict()

                # Handle file uploads if present
                if request.files:
                    for key, file in request.files.items():
                        form_data[key] = file

                # Validate using Pydantic schema
                validated_data = schema(**form_data)

                # Call original function with validated data
                return f(validated_data, *args, **kwargs)

            except ValidationError as e:
                validation_exception = ValidationException.from_pydantic_error(e, message)
                return ApiResponse.validation_error(validation_exception.errors, validation_exception.message)

            except ValidationException as e:
                return ApiResponse.validation_error(e.errors, e.message)

            except Exception as e:
                return ApiResponse.error(f"Form validation failed: {str(e)}", status_code=500)

        return decorated_function
    return decorator


def validate_multiple(
    json_schema: Optional[Type[BaseModel]] = None,
    query_schema: Optional[Type[BaseModel]] = None,
    form_schema: Optional[Type[BaseModel]] = None,
    message: str = "Validation failed."
):
    """
    Decorator to validate multiple data sources (JSON, query, form) using different schemas

    Args:
        json_schema: Pydantic schema for JSON body validation
        query_schema: Pydantic schema for query parameters validation
        form_schema: Pydantic schema for form data validation
        message: Custom error message for validation failures

    Usage:
        @validate_multiple(
            json_schema=ContentStatsRequest,
            query_schema=PaginationRequest
        )
        def get_content_stats(validated_json=None, validated_query=None, validated_form=None):
            # Each validated parameter contains the respective validated data
            pass
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                validated_json = None
                validated_query = None
                validated_form = None

                # Validate JSON if schema provided
                if json_schema:
                    json_data = request.get_json(silent=True)
                    if json_data is not None:
                        validated_json = json_schema(**json_data)

                # Validate query parameters if schema provided
                if query_schema:
                    query_data = request.args.to_dict()
                    # Convert string values to appropriate types
                    for key, value in query_data.items():
                        if value.isdigit():
                            query_data[key] = int(value)
                        elif value.lower() in ['true', 'false']:
                            query_data[key] = value.lower() == 'true'

                    if query_data:
                        validated_query = query_schema(**query_data)

                # Validate form data if schema provided
                if form_schema:
                    form_data = request.form.to_dict()
                    if request.files:
                        for key, file in request.files.items():
                            form_data[key] = file

                    if form_data:
                        validated_form = form_schema(**form_data)

                # Call original function with validated data
                return f(
                    validated_json=validated_json,
                    validated_query=validated_query,
                    validated_form=validated_form,
                    *args,
                    **kwargs
                )

            except ValidationError as e:
                validation_exception = ValidationException.from_pydantic_error(e, message)
                return ApiResponse.validation_error(validation_exception.errors, validation_exception.message)

            except ValidationException as e:
                return ApiResponse.validation_error(e.errors, e.message)

            except Exception as e:
                return ApiResponse.error(f"Multi-source validation failed: {str(e)}", status_code=500)

        return decorated_function
    return decorator


def validate_business_rules(validator_func: Callable, message: str = "Business rule validation failed."):
    """
    Decorator for custom business logic validation

    Args:
        validator_func: Function that takes request data and returns True if valid,
                       raises ValidationException if invalid
        message: Custom error message for validation failures

    Usage:
        def validate_user_permissions(data):
            if not user_has_permission(data.user_id, 'read_reports'):
                raise ValidationException.from_custom_errors(
                    {"user_id": ["User does not have permission to access reports"]},
                    "Permission denied"
                )
            return True

        @validate_business_rules(validate_user_permissions)
        def get_reports(validated_data):
            pass
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                # Extract validated data from previous decorators or raw request
                validated_data = None
                if args and hasattr(args[0], '__dict__'):
                    validated_data = args[0]
                else:
                    # Fallback to raw request data (silent=True: tolerate malformed body)
                    validated_data = request.get_json(silent=True) or request.form.to_dict() or request.args.to_dict()

                # Run business rule validation
                validator_func(validated_data)

                # Call original function
                return f(*args, **kwargs)

            except ValidationException as e:
                return ApiResponse.validation_error(e.errors, e.message)

            except Exception as e:
                return ApiResponse.error(f"Business rule validation failed: {str(e)}", status_code=500)

        return decorated_function
    return decorator
