<?php

/**
 * Централизованный фильтр для безопасного доступа к суперглобальным переменным
 *
 * Предоставляет безопасные методы для работы с $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE
 * с автоматической санитизацией и валидацией данных
 *
 * ВНИМАНИЕ: Этот класс специально создан для безопасной работы с суперглобальными переменными.
 * Прямое использование $_GET, $_POST и т.д. в этом классе является частью его функциональности.
 *
 * @package Flowaxy\Infrastructure\Security
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Infrastructure\Security;

use Flowaxy\Infrastructure\Security\Validators\Sanitizer;

final class RequestFilter
{
    /**
     * Кеш отфильтрованных значений
     *
     * @var array<string, array<string, mixed>>
     */
    private static array $cache = [
        'get' => [],
        'post' => [],
        'request' => [],
        'server' => [],
        'cookie' => [],
    ];

    /**
     * Экземпляр санитизатора
     */
    private static ?Sanitizer $sanitizer = null;

    /**
     * Получить экземпляр санитизатора
     */
    private static function getSanitizer(): Sanitizer
    {
        if (self::$sanitizer === null) {
            self::$sanitizer = new Sanitizer();
        }

        return self::$sanitizer;
    }

    /**
     * Безопасное получение значения из $_GET
     *
     * @param string $key Ключ параметра
     * @param mixed $default Значение по умолчанию
     * @param string $type Тип данных (string, int, float, email, url, array)
     * @return mixed Отфильтрованное значение
     */
    public static function get(string $key, mixed $default = null, string $type = 'string'): mixed
    {
        $cacheKey = "{$key}_{$type}";

        if (isset(self::$cache['get'][$cacheKey])) {
            return self::$cache['get'][$cacheKey];
        }

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        $value = $_GET[$key] ?? $default;

        if ($value === $default) {
            return $default;
        }

        $filtered = self::filterValue($value, $type);
        self::$cache['get'][$cacheKey] = $filtered;

        return $filtered;
    }

    /**
     * Безопасное получение значения из $_POST
     *
     * @param string $key Ключ параметра
     * @param mixed $default Значение по умолчанию
     * @param string $type Тип данных (string, int, float, email, url, array)
     * @return mixed Отфильтрованное значение
     */
    public static function post(string $key, mixed $default = null, string $type = 'string'): mixed
    {
        $cacheKey = "{$key}_{$type}";

        if (isset(self::$cache['post'][$cacheKey])) {
            return self::$cache['post'][$cacheKey];
        }

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        $value = $_POST[$key] ?? $default;

        if ($value === $default) {
            return $default;
        }

        $filtered = self::filterValue($value, $type);
        self::$cache['post'][$cacheKey] = $filtered;

        return $filtered;
    }

    /**
     * Безопасное получение значения из $_REQUEST
     *
     * @param string $key Ключ параметра
     * @param mixed $default Значение по умолчанию
     * @param string $type Тип данных (string, int, float, email, url, array)
     * @return mixed Отфильтрованное значение
     */
    public static function request(string $key, mixed $default = null, string $type = 'string'): mixed
    {
        $cacheKey = "{$key}_{$type}";

        if (isset(self::$cache['request'][$cacheKey])) {
            return self::$cache['request'][$cacheKey];
        }

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        $value = $_REQUEST[$key] ?? $default;

        if ($value === $default) {
            return $default;
        }

        $filtered = self::filterValue($value, $type);
        self::$cache['request'][$cacheKey] = $filtered;

        return $filtered;
    }

    /**
     * Безопасное получение значения из $_SERVER
     *
     * @param string $key Ключ параметра
     * @param mixed $default Значение по умолчанию
     * @param string $type Тип данных (string, int, float, email, url, array)
     * @return mixed Отфильтрованное значение
     */
    public static function server(string $key, mixed $default = null, string $type = 'string'): mixed
    {
        $cacheKey = "{$key}_{$type}";

        if (isset(self::$cache['server'][$cacheKey])) {
            return self::$cache['server'][$cacheKey];
        }

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        $value = $_SERVER[$key] ?? $default;

        if ($value === $default) {
            return $default;
        }

        $filtered = self::filterValue($value, $type);
        self::$cache['server'][$cacheKey] = $filtered;

        return $filtered;
    }

    /**
     * Безопасное получение значения из $_COOKIE
     *
     * @param string $key Ключ параметра
     * @param mixed $default Значение по умолчанию
     * @param string $type Тип данных (string, int, float, email, url, array)
     * @return mixed Отфильтрованное значение
     */
    public static function cookie(string $key, mixed $default = null, string $type = 'string'): mixed
    {
        $cacheKey = "{$key}_{$type}";

        if (isset(self::$cache['cookie'][$cacheKey])) {
            return self::$cache['cookie'][$cacheKey];
        }

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        $value = $_COOKIE[$key] ?? $default;

        if ($value === $default) {
            return $default;
        }

        $filtered = self::filterValue($value, $type);
        self::$cache['cookie'][$cacheKey] = $filtered;

        return $filtered;
    }

    /**
     * Получить все отфильтрованные GET параметры
     *
     * @return array<string, mixed>
     */
    public static function allGet(): array
    {
        $result = [];

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        foreach ($_GET as $key => $value) {
            $result[$key] = self::get($key, null, 'string');
        }

        return $result;
    }

    /**
     * Получить все отфильтрованные POST параметры
     *
     * @return array<string, mixed>
     */
    public static function allPost(): array
    {
        $result = [];

        foreach ($_POST as $key => $value) {
            $result[$key] = self::post($key, null, 'string');
        }

        return $result;
    }

    /**
     * Получить все отфильтрованные SERVER переменные
     *
     * @return array<string, mixed>
     */
    public static function allServer(): array
    {
        $result = [];

        // @phpstan-ignore-next-line - это класс для безопасной работы с суперглобальными переменными
        foreach ($_SERVER as $key => $value) {
            $result[$key] = self::server($key, null, 'string');
        }

        return $result;
    }

    /**
     * Фильтрация значения по типу
     *
     * @param mixed $value Значение для фильтрации
     * @param string $type Тип данных
     * @return mixed Отфильтрованное значение
     */
    private static function filterValue(mixed $value, string $type): mixed
    {
        if ($value === null) {
            return null;
        }

        return match ($type) {
            'int', 'integer' => self::filterInt($value),
            'float', 'double' => self::filterFloat($value),
            'bool', 'boolean' => self::filterBool($value),
            'email' => self::getSanitizer()->sanitizeEmail((string) $value),
            'url' => self::getSanitizer()->sanitizeUrl((string) $value),
            'array' => self::filterArray($value),
            'string' => self::getSanitizer()->sanitizeString((string) $value),
            'raw' => $value, // Без фильтрации (для специальных случаев)
            default => self::getSanitizer()->sanitizeString((string) $value),
        };
    }

    /**
     * Фильтрация целого числа
     */
    private static function filterInt(mixed $value): int
    {
        if (is_int($value)) {
            return $value;
        }

        if (is_string($value)) {
            $filtered = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
            return (int) $filtered;
        }

        if (is_numeric($value)) {
            return (int) $value;
        }

        return 0;
    }

    /**
     * Фильтрация числа с плавающей точкой
     */
    private static function filterFloat(mixed $value): float
    {
        if (is_float($value)) {
            return $value;
        }

        if (is_string($value)) {
            $filtered = filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
            return (float) $filtered;
        }

        if (is_numeric($value)) {
            return (float) $value;
        }

        return 0.0;
    }

    /**
     * Фильтрация булевого значения
     */
    private static function filterBool(mixed $value): bool
    {
        if (is_bool($value)) {
            return $value;
        }

        if (is_string($value)) {
            $lower = strtolower(trim($value));
            return in_array($lower, ['1', 'true', 'on', 'yes'], true);
        }

        return (bool) $value;
    }

    /**
     * Фильтрация массива
     *
     * @param mixed $value Значение для фильтрации
     * @return array<string, mixed>
     */
    private static function filterArray(mixed $value): array
    {
        if (! is_array($value)) {
            return [];
        }

        return self::getSanitizer()->sanitizeArray($value);
    }

    /**
     * Очистить кеш
     */
    public static function clearCache(): void
    {
        self::$cache = [
            'get' => [],
            'post' => [],
            'request' => [],
            'server' => [],
            'cookie' => [],
        ];
    }
}
