<?php
declare(strict_types=1);

namespace App\Framework\Logging\Handlers;

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

/**
 * Handler für die Ausgabe von Log-Einträgen als JSON in Dateien.
 * Besonders nützlich für maschinelle Verarbeitung und Log-Aggregatoren.
 */
class JsonFileHandler implements LogHandler
{
    /**
     * @var LogLevel Minimales Level, ab dem dieser Handler aktiv wird
     */
    private LogLevel $minLevel;

    /**
     * @var string Pfad zur Log-Datei
     */
    private string $logFile;

    /**
     * @var array Liste der Felder, die in der JSON-Ausgabe enthalten sein sollen
     */
    private array $includedFields;

    /**
     * Erstellt einen neuen JsonFileHandler
     *
     * @param string $logFile Pfad zur Log-Datei
     * @param LogLevel|int $minLevel Minimales Level, ab dem dieser Handler aktiv wird
     * @param array|null $includedFields Liste der Felder, die in der JSON-Ausgabe enthalten sein sollen (null = alle)
     */
    public function __construct(
        string $logFile,
        LogLevel|int $minLevel = LogLevel::INFO,
        ?array $includedFields = null
    ) {
        $this->logFile = $logFile;
        $this->minLevel = $minLevel instanceof LogLevel ? $minLevel : LogLevel::fromValue($minLevel);

        // Standardfelder, falls nicht anders angegeben
        $this->includedFields = $includedFields ?? [
            'timestamp',
            'level_name',
            'message',
            'context',
            'extra',
            'channel',
        ];

        // Stelle sicher, dass das Verzeichnis existiert
        $this->ensureDirectoryExists(dirname($logFile));
    }

    /**
     * Überprüft, ob dieser Handler den Log-Eintrag verarbeiten soll
     */
    public function isHandling(LogRecord $record): bool
    {
        return $record->getLevel()->value >= $this->minLevel->value;
    }

    /**
     * Verarbeitet einen Log-Eintrag
     */
    public function handle(LogRecord $record): void
    {
        // Alle Daten des Records als Array holen
        $data = $record->toArray();

        // Nur die gewünschten Felder behalten
        if (!empty($this->includedFields)) {
            $data = array_intersect_key($data, array_flip($this->includedFields));
        }

        // Zeitstempel als ISO 8601 formatieren für bessere Interoperabilität
        if (isset($data['datetime']) && $data['datetime'] instanceof \DateTimeInterface) {
            $data['timestamp_iso'] = $data['datetime']->format(\DateTimeInterface::ATOM);
            unset($data['datetime']); // DateTime-Objekt entfernen
        }

        // Als JSON formatieren und in die Datei schreiben
        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR) . PHP_EOL;
        $this->write($json);
    }

    /**
     * Schreibt einen String in die Log-Datei
     */
    protected function write(string $output): void
    {
        file_put_contents($this->logFile, $output, FILE_APPEND);
    }

    /**
     * Stellt sicher, dass ein Verzeichnis existiert
     */
    private function ensureDirectoryExists(string $dir): void
    {
        if (!file_exists($dir)) {
            mkdir($dir, 0777, true);
        }
    }

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

    /**
     * Setzt die Liste der Felder, die in der JSON-Ausgabe enthalten sein sollen
     */
    public function setIncludedFields(array $fields): self
    {
        $this->includedFields = $fields;
        return $this;
    }

    /**
     * Log-Datei setzen
     */
    public function setLogFile(string $logFile): self
    {
        $this->logFile = $logFile;
        $this->ensureDirectoryExists(dirname($logFile));
        return $this;
    }
}
