<?php
declare(strict_types=1);

namespace App\Application\Security\Services;

use App\Application\Security\Events\Input\{
    InputValidationFailureEvent,
    SqlInjectionAttemptEvent,
    XssAttemptEvent,
    MaliciousInputDetectedEvent
};
use App\Framework\Core\Events\EventDispatcher;

final class InputValidationService
{
    private const SQL_INJECTION_PATTERNS = [
        '/(\bunion\b.*\bselect\b)/i',
        '/(\bdrop\b.*\btable\b)/i',
        '/(\binsert\b.*\binto\b)/i',
        '/(\bdelete\b.*\bfrom\b)/i',
        '/(\bupdate\b.*\bset\b)/i',
        '/(\b(or|and)\b.*[\'"].*[\'"].*=.*[\'"].*[\'"])/i'
    ];

    private const XSS_PATTERNS = [
        '/<script[^>]*>.*?<\/script>/is',
        '/<iframe[^>]*>.*?<\/iframe>/is',
        '/javascript:/i',
        '/on\w+\s*=/i',
        '/<object[^>]*>.*?<\/object>/is'
    ];

    public function __construct(
        private EventDispatcher $eventDispatcher
    ) {}

    public function validateInput(string $fieldName, mixed $value, array $rules = []): bool
    {
        $stringValue = (string) $value;
        $userEmail = $_SESSION['user_email'] ?? null;

        // SQL Injection Detection
        if ($this->detectSqlInjection($stringValue)) {
            $this->eventDispatcher->dispatch(new SqlInjectionAttemptEvent(
                attackPayload: $stringValue,
                targetField: $fieldName,
                detectionMethod: 'pattern_matching',
                email: $userEmail
            ));
            return false;
        }

        // XSS Detection
        if ($this->detectXss($stringValue)) {
            $this->eventDispatcher->dispatch(new XssAttemptEvent(
                attackPayload: $stringValue,
                targetField: $fieldName,
                xssType: 'reflected_xss',
                email: $userEmail
            ));
            return false;
        }

        // Standard Validation Rules
        foreach ($rules as $rule => $parameter) {
            if (!$this->applyRule($rule, $stringValue, $parameter)) {
                $this->eventDispatcher->dispatch(new InputValidationFailureEvent(
                    fieldName: $fieldName,
                    invalidValue: $stringValue,
                    validationRule: $rule,
                    email: $userEmail
                ));
                return false;
            }
        }

        return true;
    }

    public function sanitizeInput(string $input, string $context = 'html'): string
    {
        return match ($context) {
            'html' => htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
            'sql' => addslashes($input),
            'url' => urlencode($input),
            'javascript' => json_encode($input, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT),
            default => filter_var($input, FILTER_SANITIZE_SPECIAL_CHARS)
        };
    }

    private function detectSqlInjection(string $input): bool
    {
        foreach (self::SQL_INJECTION_PATTERNS as $pattern) {
            if (preg_match($pattern, $input)) {
                return true;
            }
        }
        return false;
    }

    private function detectXss(string $input): bool
    {
        foreach (self::XSS_PATTERNS as $pattern) {
            if (preg_match($pattern, $input)) {
                return true;
            }
        }
        return false;
    }

    private function applyRule(string $rule, string $value, mixed $parameter): bool
    {
        return match ($rule) {
            'required' => !empty($value),
            'email' => filter_var($value, FILTER_VALIDATE_EMAIL) !== false,
            'min_length' => strlen($value) >= $parameter,
            'max_length' => strlen($value) <= $parameter,
            'numeric' => is_numeric($value),
            'alpha' => ctype_alpha($value),
            'alphanumeric' => ctype_alnum($value),
            'url' => filter_var($value, FILTER_VALIDATE_URL) !== false,
            default => true
        };
    }
}
