<?php
declare(strict_types=1);

namespace Celena\Core\Http;

/**
 * Объект HTTP-запроса. Не зависит от суперглобалов после fromGlobals().
 */
final class Request
{
    /** @param array<string, mixed> $get
     *  @param array<string, mixed> $post
     *  @param array<string, string> $cookies
     *  @param array<string, mixed> $files
     *  @param array<string, string> $server
     *  @param array<string, string> $headers */
    public function __construct(
        public readonly string $method,
        public readonly string $path,
        public readonly array $get,
        public readonly array $post,
        public readonly array $cookies,
        public readonly array $files,
        public readonly array $server,
        public readonly array $headers,
        public readonly string $rawBody = '',
    ) {
    }

    public static function fromGlobals(): self
    {
        $method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
        $uri = $_SERVER['REQUEST_URI'] ?? '/';
        $path = parse_url($uri, PHP_URL_PATH) ?: '/';
        // Декодируем percent-encoded символы (кириллица в URL → UTF-8)
        $path = rawurldecode($path);
        $headers = self::collectHeaders();
        $rawBody = (string) file_get_contents('php://input');

        $post = $_POST;
        if (empty($post) && in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) {
            $contentType = $headers['content-type'] ?? '';
            if (str_contains($contentType, 'application/json') && $rawBody !== '') {
                $decoded = json_decode($rawBody, true);
                if (is_array($decoded)) {
                    $post = $decoded;
                }
            }
        }

        return new self(
            method: $method,
            path: '/' . ltrim($path, '/'),
            get: $_GET,
            post: $post,
            cookies: $_COOKIE,
            files: $_FILES,
            server: $_SERVER,
            headers: $headers,
            rawBody: $rawBody,
        );
    }

    public function isHttps(): bool
    {
        return ($this->server['HTTPS'] ?? 'off') !== 'off'
            || ($this->server['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'
            || (int) ($this->server['SERVER_PORT'] ?? 0) === 443;
    }

    public function host(): string
    {
        return (string) ($this->headers['host'] ?? $this->server['SERVER_NAME'] ?? 'localhost');
    }

    public function ip(): string
    {
        return (string) ($this->server['REMOTE_ADDR'] ?? '0.0.0.0');
    }

    public function isAjax(): bool
    {
        return strtolower($this->headers['x-requested-with'] ?? '') === 'xmlhttprequest';
    }

    public function isJson(): bool
    {
        return str_contains($this->headers['content-type'] ?? '', 'application/json');
    }

    public function wantsJson(): bool
    {
        return $this->isAjax() || $this->isJson()
            || str_contains($this->headers['accept'] ?? '', 'application/json');
    }

    public function header(string $name): ?string
    {
        return $this->headers[strtolower($name)] ?? null;
    }

    public function query(string $key, mixed $default = null): mixed
    {
        return $this->get[$key] ?? $default;
    }

    public function input(string $key, mixed $default = null): mixed
    {
        return $this->post[$key] ?? $this->get[$key] ?? $default;
    }

    public function cookie(string $name): ?string
    {
        return $this->cookies[$name] ?? null;
    }

    public function file(string $name): ?array
    {
        return $this->files[$name] ?? null;
    }

    public function bearerToken(): ?string
    {
        $auth = $this->header('authorization') ?? '';
        if (preg_match('/^Bearer\s+(.+)$/i', $auth, $m)) {
            return $m[1];
        }
        return null;
    }

    public function url(): string
    {
        return ($this->isHttps() ? 'https' : 'http') . '://' . $this->host() . $this->path;
    }

    /** @return array<string, string> */
    private static function collectHeaders(): array
    {
        $headers = [];
        foreach ($_SERVER as $key => $value) {
            if (str_starts_with((string) $key, 'HTTP_')) {
                $name = strtolower(str_replace('_', '-', substr($key, 5)));
                $headers[$name] = (string) $value;
            }
        }
        if (isset($_SERVER['CONTENT_TYPE'])) {
            $headers['content-type'] = (string) $_SERVER['CONTENT_TYPE'];
        }
        if (isset($_SERVER['CONTENT_LENGTH'])) {
            $headers['content-length'] = (string) $_SERVER['CONTENT_LENGTH'];
        }
        return $headers;
    }
}
