<?php

declare(strict_types=1);

namespace App\Framework\Exception;

final readonly class RequestContext
{
    public function __construct(
        public ?string $hostIp = null,
        public ?string $hostname = null,
        public ?string $protocol = null,
        public ?string $port = null,
        public ?string $requestUri = null,
        public ?string $requestMethod = null,
        public ?string $userAgent = null,
        public ?string $clientIp = null,
        public array $headers = []
    ) {}

    public static function fromGlobals(): self
    {
        return new self(
            hostIp: $_SERVER['SERVER_ADDR'] ?? null,
            hostname: $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? null,
            protocol: isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http',
            port: $_SERVER['SERVER_PORT'] ?? null,
            requestUri: $_SERVER['REQUEST_URI'] ?? null,
            requestMethod: $_SERVER['REQUEST_METHOD'] ?? null,
            userAgent: $_SERVER['HTTP_USER_AGENT'] ?? null,
            clientIp: self::getClientIp(),
            headers: self::getHeaders()
        );
    }

    public static function empty(): self
    {
        return new self();
    }

    public function toArray(): array
    {
        return [
            'host_ip' => $this->hostIp,
            'hostname' => $this->hostname,
            'protocol' => $this->protocol,
            'port' => $this->port,
            'request_uri' => $this->requestUri,
            'request_method' => $this->requestMethod,
            'user_agent' => $this->userAgent,
            'client_ip' => $this->clientIp,
            'headers' => $this->headers
        ];
    }

    private static function getClientIp(): ?string
    {
        $ipKeys = [
            'HTTP_CF_CONNECTING_IP',     // Cloudflare
            'HTTP_X_FORWARDED_FOR',      // Load balancer/proxy
            'HTTP_X_FORWARDED',          // Proxy
            'HTTP_X_CLUSTER_CLIENT_IP',  // Cluster
            'HTTP_FORWARDED_FOR',        // Proxy
            'HTTP_FORWARDED',            // Proxy
            'REMOTE_ADDR'                // Standard
        ];

        foreach ($ipKeys as $key) {
            if (!empty($_SERVER[$key])) {
                $ips = explode(',', $_SERVER[$key]);
                $ip = trim($ips[0]);
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                    return $ip;
                }
            }
        }

        return $_SERVER['REMOTE_ADDR'] ?? null;
    }

    private static function getHeaders(): array
    {
        $headers = [];

        foreach ($_SERVER as $key => $value) {
            if (str_starts_with($key, 'HTTP_')) {
                $header = str_replace('_', '-', substr($key, 5));
                $header = ucwords(strtolower($header), '-');
                $headers[$header] = $value;
            }
        }

        return $headers;
    }
}
