<?php
declare(strict_types=1);

namespace App\Framework\Logging\Processors;

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

/**
 * Ersetzt Platzhalter in Log-Nachrichten durch Kontext-Werte.
 */
final class InterpolationProcessor implements LogProcessor
{
    /**
     * @var int Priorität des Processors (höher = früher, 20 = sehr früh)
     */
    private const int PRIORITY = 20;

    /**
     * Verarbeitet einen Log-Record und ersetzt Platzhalter in der Nachricht
     */
    public function processRecord(LogRecord $record): LogRecord
    {
        $message = $record->getMessage();
        $context = $record->getContext();

        // Platzhalter ersetzen, falls vorhanden
        if (str_contains($message, '{')) {
            $message = $this->interpolate($message, $context);
            $record->setMessage($message);
        }

        return $record;
    }

    /**
     * Platzhalter im Log-Text ersetzen
     */
    private function interpolate(string $message, array $context): string
    {
        // Platzhalter ersetzen (Format: {key})
        $replace = [];

        foreach ($context as $key => $val) {
            if ($val === null || is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) {
                $replace['{' . $key . '}'] = $val;
            } elseif ($val instanceof \Throwable) {
                $replace['{' . $key . '}'] = $val->getMessage() . ' in ' . $val->getFile() . ':' . $val->getLine();
            } elseif (is_object($val)) {
                $replace['{' . $key . '}'] = '[object ' . get_class($val) . ']';
            } elseif (is_array($val)) {
                $replace['{' . $key . '}'] = 'array' . json_encode($val,
                    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
            } else {
                $replace['{' . $key . '}'] = '[' . gettype($val) . ']';
            }
        }

        return strtr($message, $replace);
    }

    /**
     * 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 'interpolation';
    }
}
