<?php

/**
 * Клас для роботи з HTTP запитами
 * Обробка GET, POST, FILES та інших даних запиту
 *
 * @package Flowaxy\Interface\Http
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Interface\Http;

use Flowaxy\Infrastructure\Security\Security;
use Flowaxy\Infrastructure\Security\RequestFilter;
use Flowaxy\Support\Helpers\UrlHelper;

final class Request
{
    private static ?self $instance = null;
    private array $data;

    private function __construct()
    {
        $this->data = array_merge(RequestFilter::allGet(), RequestFilter::allPost());
    }

    /**
     * Отримання екземпляра (Singleton)
     *
     * @return self
     */
    public static function getInstance(): self
    {
        return self::$instance ??= new self();
    }

    /**
     * Отримання значення з запиту
     *
     * @param string $key Ключ
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public function get(string $key, $default = null)
    {
        return $this->data[$key] ?? $default;
    }

    /**
     * Отримання всіх даних запиту
     *
     * @return array<string, mixed>
     */
    public function all(): array
    {
        return $this->data;
    }

    /**
     * Отримання тільки вказаних ключів
     *
     * @param array<int, string> $keys Масив ключів
     * @return array<string, mixed>
     */
    public function only(array $keys): array
    {
        return array_intersect_key($this->data, array_flip($keys));
    }

    /**
     * Отримання всіх даних крім вказаних ключів
     *
     * @param array<int, string> $keys Масив ключів
     * @return array<string, mixed>
     */
    public function except(array $keys): array
    {
        return array_diff_key($this->data, array_flip($keys));
    }

    /**
     * Перевірка наявності ключа
     *
     * @param string $key Ключ
     * @return bool
     */
    public function has(string $key): bool
    {
        return isset($this->data[$key]);
    }

    /**
     * Отримання значення з POST
     *
     * @param string $key Ключ
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public function postValue(string $key, $default = null)
    {
        return RequestFilter::post($key, $default);
    }

    /**
     * Отримання значення з GET
     *
     * @param string $key Ключ
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public function queryValue(string $key, $default = null)
    {
        return RequestFilter::get($key, $default);
    }

    /**
     * Отримання значення з GET (для використання через instance)
     *
     * @param string $key Ключ
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public function query(string $key, $default = null)
    {
        return $this->queryValue($key, $default);
    }

    /**
     * Отримання методу запиту
     *
     * @return string
     */
    public function method(): string
    {
        return RequestFilter::server('REQUEST_METHOD', 'GET', 'string');
    }

    /**
     * Перевірка методу запиту
     *
     * @param string $method Метод
     * @return bool
     */
    public function isMethod(string $method): bool
    {
        return strcasecmp($this->method(), $method) === 0;
    }

    /**
     * Отримання URL
     *
     * @return string
     */
    public function url(): string
    {
        // Використовуємо UrlHelper для отримання протоколу
        if (class_exists(UrlHelper::class)) {
            $protocol = UrlHelper::getProtocol();
        } elseif (function_exists('detectProtocol')) {
            $protocol = detectProtocol();
        } else {
            // Fallback якщо класи не доступні
            $https = RequestFilter::server('HTTPS', '', 'string');
            $requestScheme = RequestFilter::server('REQUEST_SCHEME', '', 'string');
            $serverPort = (int)RequestFilter::server('SERVER_PORT', 0, 'int');
            $forwardedProto = RequestFilter::server('HTTP_X_FORWARDED_PROTO', '', 'string');

            $isHttps = (
                (!empty($https) && $https !== 'off') ||
                ($requestScheme === 'https') ||
                ($serverPort === 443) ||
                ($forwardedProto === 'https')
            );
            $protocol = $isHttps ? 'https://' : 'http://';
        }

        $host = RequestFilter::server('HTTP_HOST', '', 'string');
        $uri = RequestFilter::server('REQUEST_URI', '', 'string');
        return $protocol . $host . $uri;
    }

    /**
     * Отримання шляху
     *
     * @return string
     */
    public function path(): string
    {
        $requestUri = RequestFilter::server('REQUEST_URI', '/', 'string');
        return parse_url($requestUri, PHP_URL_PATH) ?? '/';
    }

    /**
     * Отримання IP адреси клієнта
     *
     * @return string
     */
    public function ip(): string
    {
        return Security::getClientIp();
    }

    /**
     * Отримання User Agent
     *
     * @return string
     */
    public function userAgent(): string
    {
        return RequestFilter::server('HTTP_USER_AGENT', '', 'string');
    }

    /**
     * Отримання завантажених файлів
     *
     * @return array<string, array<string, mixed>>
     */
    public function files(): array
    {
        return $_FILES ?? [];
    }

    /**
     * Статичний метод: Швидке отримання значення
     *
     * @param string $key Ключ
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public static function input(string $key, $default = null)
    {
        return self::getInstance()->get($key, $default);
    }

    /**
     * Статичний метод: Швидке отримання методу
     *
     * @return string
     */
    public static function getMethod(): string
    {
        return self::getInstance()->method();
    }

    /**
     * Статичний метод: Швидке отримання значення з POST
     *
     * @param string $key Ключ
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public static function post(string $key, $default = null)
    {
        return RequestFilter::post($key, $default);
    }

    /**
     * Перевірка, чи це AJAX запит
     *
     * @return bool
     */
    public function isAjax(): bool
    {
        $requestedWith = RequestFilter::server('HTTP_X_REQUESTED_WITH', '', 'string');
        return !empty($requestedWith) &&
               strtolower($requestedWith) === 'xmlhttprequest';
    }
}
