<?php

declare(strict_types=1);

namespace App\Framework\Logging\Processors;

use App\Framework\Logging\LogProcessor;
use App\Framework\Logging\LogRecord;

/**
 * Fügt Web-Informationen wie IP, URL, Methode und User-Agent zu Log-Einträgen hinzu.
 */
final class WebInfoProcessor implements LogProcessor
{
    /**
     * @var int Priorität des Processors (5 = früh, aber nach Request-ID)
     */
    private const int PRIORITY = 5;

    /**
     * @var array Konfiguration, welche Informationen gesammelt werden sollen
     */
    private array $config;

    /**
     * Erstellt einen neuen WebInfoProcessor
     *
     * @param array $config Konfiguration, welche Informationen gesammelt werden sollen
     */
    public function __construct(array $config = [])
    {
        $this->config = array_merge(
            [
                'url' => true,
                'ip' => true,
                'http_method' => true,
                'user_agent' => true,
                'referrer' => false,
            ],
            $config
        );
    }

    /**
     * Verarbeitet einen Log-Record und fügt Web-Informationen hinzu
     */
    public function processRecord(LogRecord $record): LogRecord
    {
        // Nur im Web-Kontext ausführen
        if (PHP_SAPI === 'cli') {
            return $record;
        }

        // Webinfos als Extra-Felder hinzufügen
        $extras = $this->collectWebInfo();
        if (! empty($extras)) {
            $record = $record->withExtras($extras);
        }

        return $record;
    }

    /**
     * Sammelt Informationen aus dem Web-Kontext
     */
    private function collectWebInfo(): array
    {
        $info = [];

        if ($this->config['url'] && isset($_SERVER['REQUEST_URI'])) {
            $info['url'] = $this->getFullUrl();
        }

        if ($this->config['ip'] && isset($_SERVER['REMOTE_ADDR'])) {
            $info['ip'] = $this->getClientIp();
        }

        if ($this->config['http_method'] && isset($_SERVER['REQUEST_METHOD'])) {
            $info['http_method'] = $_SERVER['REQUEST_METHOD'];
        }

        if ($this->config['user_agent'] && isset($_SERVER['HTTP_USER_AGENT'])) {
            $info['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
        }

        if ($this->config['referrer'] && isset($_SERVER['HTTP_REFERER'])) {
            $info['referrer'] = $_SERVER['HTTP_REFERER'];
        }

        return $info;
    }

    /**
     * Ermittelt die vollständige URL des Requests
     */
    private function getFullUrl(): string
    {
        $protocol = (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
        $uri = $_SERVER['REQUEST_URI'] ?? '/';

        return $protocol . '://' . $host . $uri;
    }

    /**
     * Ermittelt die IP-Adresse des Clients unter Berücksichtigung von Proxies
     */
    private function getClientIp(): string
    {
        $keys = [
            'HTTP_CF_CONNECTING_IP', // Cloudflare
            'HTTP_X_FORWARDED_FOR',  // Standard Proxy/Load Balancer
            'HTTP_CLIENT_IP',        // Manche Proxies
            'REMOTE_ADDR',           // Fallback direkte Verbindung
        ];

        foreach ($keys as $key) {
            if (isset($_SERVER[$key])) {
                // Bei X-Forwarded-For kann eine Komma-separierte Liste vorliegen
                if ($key === 'HTTP_X_FORWARDED_FOR') {
                    $ips = explode(',', $_SERVER[$key]);

                    return trim($ips[0]); // Erste IP = ursprünglicher Client
                }

                return $_SERVER[$key];
            }
        }

        return 'unknown';
    }

    /**
     * Gibt die Priorität des Processors zurück
     */
    public function getPriority(): int
    {
        return self::PRIORITY;
    }

    /**
     * Gibt einen eindeutigen Namen für den Processor zurück
     */
    public function getName(): string
    {
        return 'web_info';
    }
}
