<?php
/**
 * Input Validation and Sanitization Middleware
 * Provides comprehensive input validation and security checks
 */

class InputValidator {

    /**
     * Validate and sanitize string input
     */
    public static function sanitizeString($input, $maxLength = null, $allowHtml = false) {
        if ($input === null || $input === '') {
            return '';
        }

        $input = trim($input);

        if (!$allowHtml) {
            $input = strip_tags($input);
        }

        if ($maxLength !== null && strlen($input) > $maxLength) {
            $input = substr($input, 0, $maxLength);
        }

        return $input;
    }

    /**
     * Validate email address
     */
    public static function validateEmail($email) {
        if (empty($email)) {
            return false;
        }

        $email = trim($email);

        // Use PHP's built-in validation with additional checks
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return false;
        }

        // Additional security checks
        if (strlen($email) > 254) {
            return false;
        }

        // Check for suspicious patterns
        if (preg_match('/[<>\\\'"]/', $email)) {
            return false;
        }

        return true;
    }

    /**
     * Validate password strength
     */
    public static function validatePassword($password) {
        if (empty($password)) {
            return false;
        }

        // Minimum requirements
        if (strlen($password) < 8) {
            return false;
        }

        // Check for at least one uppercase letter
        if (!preg_match('/[A-Z]/', $password)) {
            return false;
        }

        // Check for at least one lowercase letter
        if (!preg_match('/[a-z]/', $password)) {
            return false;
        }

        // Check for at least one digit
        if (!preg_match('/\d/', $password)) {
            return false;
        }

        // Check for at least one special character
        if (!preg_match('/[!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>\/?]/', $password)) {
            return false;
        }

        return true;
    }

    /**
     * Validate integer with range
     */
    public static function validateInt($input, $min = null, $max = null) {
        if (!is_numeric($input)) {
            return false;
        }

        $intValue = (int)$input;

        if ($min !== null && $intValue < $min) {
            return false;
        }

        if ($max !== null && $intValue > $max) {
            return false;
        }

        return true;
    }

    /**
     * Validate date format (YYYY-MM-DD)
     */
    public static function validateDate($date) {
        if (empty($date)) {
            return false;
        }

        $date = trim($date);

        // Check format
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
            return false;
        }

        // Check if date is valid
        $parts = explode('-', $date);
        if (!checkdate($parts[1], $parts[2], $parts[0])) {
            return false;
        }

        return true;
    }

    /**
     * Validate time format (HH:MM:SS)
     */
    public static function validateTime($time) {
        if (empty($time)) {
            return false;
        }

        $time = trim($time);

        if (!preg_match('/^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/', $time)) {
            return false;
        }

        return true;
    }

    /**
     * Validate phone number (basic validation)
     */
    public static function validatePhone($phone) {
        if (empty($phone)) {
            return false;
        }

        $phone = trim($phone);

        // Remove spaces, dashes, parentheses
        $phone = preg_replace('/[\s\-\(\)]/', '', $phone);

        // Check if it's numeric and reasonable length
        if (!preg_match('/^\+?[\d]{8,15}$/', $phone)) {
            return false;
        }

        return true;
    }

    /**
     * Sanitize and validate input array
     */
    public static function validateArray($data, $rules) {
        $errors = [];
        $sanitized = [];

        foreach ($rules as $field => $rule) {
            $value = $data[$field] ?? null;

            // Check required fields
            if (isset($rule['required']) && $rule['required'] && ($value === null || $value === '')) {
                $errors[$field] = 'This field is required';
                continue;
            }

            // Skip validation if field is not required and empty
            if (!$rule['required'] && ($value === null || $value === '')) {
                continue;
            }

            // Apply sanitization
            if (isset($rule['type'])) {
                switch ($rule['type']) {
                    case 'string':
                        $sanitized[$field] = self::sanitizeString($value, $rule['max_length'] ?? null);
                        break;

                    case 'email':
                        if (!self::validateEmail($value)) {
                            $errors[$field] = 'Invalid email address';
                        } else {
                            $sanitized[$field] = trim($value);
                        }
                        break;

                    case 'password':
                        if (!self::validatePassword($value)) {
                            $errors[$field] = 'Password must be at least 8 characters with uppercase, lowercase, number, and special character';
                        } else {
                            $sanitized[$field] = $value; // Don't sanitize passwords
                        }
                        break;

                    case 'int':
                        if (!self::validateInt($value, $rule['min'] ?? null, $rule['max'] ?? null)) {
                            $errors[$field] = 'Invalid integer value';
                        } else {
                            $sanitized[$field] = (int)$value;
                        }
                        break;

                    case 'date':
                        if (!self::validateDate($value)) {
                            $errors[$field] = 'Invalid date format (YYYY-MM-DD)';
                        } else {
                            $sanitized[$field] = $value;
                        }
                        break;

                    case 'time':
                        if (!self::validateTime($value)) {
                            $errors[$field] = 'Invalid time format (HH:MM:SS)';
                        } else {
                            $sanitized[$field] = $value;
                        }
                        break;

                    case 'phone':
                        if (!self::validatePhone($value)) {
                            $errors[$field] = 'Invalid phone number';
                        } else {
                            $sanitized[$field] = $value;
                        }
                        break;

                    default:
                        $sanitized[$field] = self::sanitizeString($value);
                }
            } else {
                $sanitized[$field] = self::sanitizeString($value);
            }
        }

        return [
            'valid' => empty($errors),
            'data' => $sanitized,
            'errors' => $errors
        ];
    }

    /**
     * Check for SQL injection patterns
     */
    public static function checkSQLInjection($input) {
        if (empty($input)) {
            return false;
        }

        $patterns = [
            '/(\b(union|select|insert|update|delete|drop|create|alter|exec|execute)\b)/i',
            '/(\'|\")(\s*or\s+|\s+and\s+)/i',
            '/(\-\-|\#)/',
            '/(\/\*|\*\/)/',
            '/(xp_|sp_|sys\.|information_schema)/i',
            '/(script|javascript|vbscript|onload|onerror)/i'
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $input)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check for XSS patterns
     */
    public static function checkXSS($input) {
        if (empty($input)) {
            return false;
        }

        $patterns = [
            '/<script/i',
            '/javascript:/i',
            '/vbscript:/i',
            '/onload=/i',
            '/onerror=/i',
            '/onclick=/i',
            '/onmouseover=/i'
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $input)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Rate limiting check (basic implementation)
     */
    private static $requestCounts = [];

    /**
     * Reset rate limiting for testing (public access for tests)
     */
    public static function resetRateLimit($identifier) {
        unset(self::$requestCounts[$identifier]);
    }

    public static function checkRateLimit($identifier, $maxRequests = 100, $timeWindow = 60) {
        $currentTime = time();
        $windowStart = $currentTime - $timeWindow;

        // Clean old entries
        self::$requestCounts[$identifier] = array_filter(
            self::$requestCounts[$identifier] ?? [],
            function($timestamp) use ($windowStart) {
                return $timestamp > $windowStart;
            }
        );

        // Check current count
        $currentCount = count(self::$requestCounts[$identifier] ?? []);

        if ($currentCount >= $maxRequests) {
            return false;
        }

        // Add current request
        self::$requestCounts[$identifier][] = $currentTime;

        return true;
    }
}
