<?php

declare(strict_types=1);

namespace App\Framework\Logging\Handlers;

use App\Framework\Console\ConsoleColor;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;

/**
 * Handler für die Ausgabe von Log-Einträgen in der Konsole.
 */
class ConsoleHandler implements LogHandler
{
    /**
     * @var LogLevel Minimales Level, ab dem dieser Handler aktiv wird
     */
    private LogLevel $minLevel;

    /**
     * @var bool Ob der Handler nur im Debug-Modus aktiv ist
     */
    private bool $debugOnly;

    /**
     * @var string Format für die Ausgabe
     */
    private string $outputFormat;

    /**
     * Erstellt einen neuen ConsoleHandler
     *
     * @param LogLevel|int $minLevel Minimales Level, ab dem dieser Handler aktiv wird
     * @param bool $debugOnly Ob der Handler nur im Debug-Modus aktiv ist
     * @param string $outputFormat Format für die Ausgabe
     */
    public function __construct(
        LogLevel|int              $minLevel = LogLevel::DEBUG,
        bool                      $debugOnly = true,
        string                    $outputFormat = '{color}[{level_name}]{reset} {timestamp} {request_id}{message}{structured}',
        private readonly LogLevel $stderrLevel = LogLevel::WARNING,
    ) {
        $this->minLevel = $minLevel instanceof LogLevel ? $minLevel : LogLevel::fromValue($minLevel);
        $this->debugOnly = $debugOnly;
        $this->outputFormat = $outputFormat;
    }

    /**
     * Überprüft, ob dieser Handler den Log-Eintrag verarbeiten soll
     */
    public function isHandling(LogRecord $record): bool
    {
        // Nur im CLI-Modus aktiv - NIE bei Web-Requests!
        if (PHP_SAPI !== 'cli') {
            return false;
        }

        // Optional: Debug-Modus-Check nur in CLI
        if ($this->debugOnly && ! filter_var(getenv('APP_DEBUG'), FILTER_VALIDATE_BOOLEAN)) {
            return false;
        }


        return $record->getLevel()->value >= $this->minLevel->value;
    }

    /**
     * Verarbeitet einen Log-Eintrag
     */
    public function handle(LogRecord $record): void
    {
        $logLevel = $record->getLevel();
        $color = $logLevel->getConsoleColor()->value;
        $reset = ConsoleColor::RESET->value;

        // Request-ID-Teil erstellen, falls vorhanden
        $requestId = $record->hasExtra('request_id')
            ? "[{$record->getExtra('request_id')}] "
            : '';

        // Structured Logging Extras formatieren
        $structuredInfo = $this->formatStructuredExtras($record);

        // Werte für Platzhalter im Format
        $values = [
            '{color}' => $color,
            '{reset}' => $reset,
            '{level_name}' => $record->getLevel()->getName(),
            '{timestamp}' => $record->getFormattedTimestamp(),
            '{request_id}' => $requestId,
            '{message}' => $record->getMessage(),
            '{channel}' => $record->getChannel() ? "[{$record->getChannel()}] " : '',
            '{structured}' => $structuredInfo,
        ];

        // Formatierte Ausgabe erstellen
        $output = strtr($this->outputFormat, $values) . PHP_EOL;

        // Fehler und Warnungen auf stderr, alles andere auf stdout
        if ($record->getLevel()->value >= $this->stderrLevel->value) {
            // WARNING, ERROR, CRITICAL, ALERT, EMERGENCY -> stderr
            file_put_contents('php://stderr', $output);
        } else {
            // DEBUG, INFO, NOTICE -> stdout
            echo $output;
        }
    }

    /**
     * Minimales Log-Level setzen
     */
    public function setMinLevel(LogLevel|int $level): self
    {
        $this->minLevel = $level instanceof LogLevel ? $level : LogLevel::fromValue($level);

        return $this;
    }

    /**
     * Ausgabeformat setzen
     */
    public function setOutputFormat(string $format): self
    {
        $this->outputFormat = $format;

        return $this;
    }

    /**
     * Formatiert Structured Logging Extras für Console-Ausgabe mit Farben
     */
    private function formatStructuredExtras(LogRecord $record): string
    {
        $parts = [];
        $reset = ConsoleColor::RESET->toAnsi();

        // Tags anzeigen (Cyan mit Tag-Symbol)
        if ($record->hasExtra('structured_tags')) {
            $tags = $record->getExtra('structured_tags');
            if (! empty($tags)) {
                $cyan = ConsoleColor::CYAN->toAnsi();
                $tagString = implode(',', $tags);
                $parts[] = "{$cyan}🏷 [{$tagString}]{$reset}";
            }
        }

        // Trace-Kontext anzeigen (Blau mit Trace-Symbol)
        if ($record->hasExtra('trace_context')) {
            $traceContext = $record->getExtra('trace_context');
            $blue = ConsoleColor::BLUE->toAnsi();

            if (isset($traceContext['trace_id'])) {
                $traceId = substr($traceContext['trace_id'], 0, 8);
                $parts[] = "{$blue}🔍 {$traceId}{$reset}";
            }
            if (isset($traceContext['active_span']['spanId'])) {
                $spanId = substr($traceContext['active_span']['spanId'], 0, 8);
                $parts[] = "{$blue}↳ {$spanId}{$reset}";
            }
        }

        // User-Kontext anzeigen (Grün mit User-Symbol)
        if ($record->hasExtra('user_context')) {
            $userContext = $record->getExtra('user_context');
            $green = ConsoleColor::GREEN->toAnsi();

            if (isset($userContext['user_id'])) {
                // Anonymisierte User-ID für Privacy
                $userId = substr(md5($userContext['user_id']), 0, 8);
                $parts[] = "{$green}👤 {$userId}{$reset}";
            } elseif (isset($userContext['is_authenticated']) && ! $userContext['is_authenticated']) {
                $parts[] = "{$green}👤 anon{$reset}";
            }
        }

        // Request-Kontext anzeigen (Gelb mit HTTP-Symbol)
        if ($record->hasExtra('request_context')) {
            $requestContext = $record->getExtra('request_context');
            if (isset($requestContext['request_method'], $requestContext['request_uri'])) {
                $yellow = ConsoleColor::YELLOW->toAnsi();
                $method = $requestContext['request_method'];
                $uri = $requestContext['request_uri'];

                // Kompakte URI-Darstellung
                if (strlen($uri) > 25) {
                    $uri = substr($uri, 0, 22) . '...';
                }

                $parts[] = "{$yellow}🌐 {$method} {$uri}{$reset}";
            }
        }

        // Context-Data anzeigen (Grau mit Data-Symbol)
        $context = $record->getContext();
        if (! empty($context)) {
            $contextKeys = array_keys($context);
            // Interne Keys ausfiltern
            $contextKeys = array_filter($contextKeys, fn ($key) => ! str_starts_with($key, '_'));

            if (! empty($contextKeys)) {
                $gray = ConsoleColor::GRAY->toAnsi();
                $keyCount = count($contextKeys);

                if ($keyCount <= 3) {
                    $keyString = implode('·', $contextKeys);
                } else {
                    $keyString = implode('·', array_slice($contextKeys, 0, 2)) . "·+{$keyCount}";
                }
                $parts[] = "{$gray}📊 {$keyString}{$reset}";
            }
        }

        return empty($parts) ? '' : "\n    " . implode(' ', $parts);
    }
}
