<?php

declare(strict_types=1);

namespace Fennec\Logger;

use Fennec\Clock\ClockInterface;

/**
 * Logger fichier : une ligne JSON par événement, un fichier par jour (app-{Y-m-d}.log).
 *
 * La rotation ne nécessite aucune détection de changement de jour : le nom de fichier
 * est recalculé à chaque écriture à partir de l'horloge injectée, donc toujours à jour.
 */
final class FileLogger implements LoggerInterface
{
    public function __construct(
        private readonly string $logDirectory,
        private readonly ClockInterface $clock,
    ) {
    }

    /**
     * @throws \RuntimeException Si le répertoire de log ne peut pas être créé, ou l'écriture échoue.
     * @throws \JsonException Si l'entrée de log ne peut pas être encodée en JSON.
     */
    public function debug(string $message, array $context = []): void
    {
        $this->write(LogLevel::DEBUG, $message, $context);
    }

    /**
     * @throws \RuntimeException Si le répertoire de log ne peut pas être créé, ou l'écriture échoue.
     * @throws \JsonException Si l'entrée de log ne peut pas être encodée en JSON.
     */
    public function info(string $message, array $context = []): void
    {
        $this->write(LogLevel::INFO, $message, $context);
    }

    /**
     * @throws \RuntimeException Si le répertoire de log ne peut pas être créé, ou l'écriture échoue.
     * @throws \JsonException Si l'entrée de log ne peut pas être encodée en JSON.
     */
    public function warning(string $message, array $context = []): void
    {
        $this->write(LogLevel::WARNING, $message, $context);
    }

    /**
     * @throws \RuntimeException Si le répertoire de log ne peut pas être créé, ou l'écriture échoue.
     * @throws \JsonException Si l'entrée de log ne peut pas être encodée en JSON.
     */
    public function error(string $message, array $context = []): void
    {
        $this->write(LogLevel::ERROR, $message, $context);
    }

    /**
     * @throws \RuntimeException Si le répertoire de log ne peut pas être créé, ou l'écriture échoue.
     * @throws \JsonException Si l'entrée de log ne peut pas être encodée en JSON.
     */
    public function critical(string $message, array $context = []): void
    {
        $this->write(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * @param array<string, mixed> $context
     *
     * @throws \RuntimeException Si le répertoire de log ne peut pas être créé, ou l'écriture échoue.
     * @throws \JsonException Si l'entrée de log ne peut pas être encodée en JSON.
     */
    private function write(LogLevel $level, string $message, array $context): void
    {
        $now = $this->clock->now();

        $entry = [
            'timestamp' => $now->format(\DateTimeInterface::ATOM),
            'level' => $level->value,
            'message' => $message,
            'context' => $context,
        ];

        $line = \json_encode($entry, \JSON_THROW_ON_ERROR) . \PHP_EOL;
        $filePath = $this->logDirectory . \DIRECTORY_SEPARATOR . \sprintf('app-%s.log', $now->format('Y-m-d'));

        if (!\is_dir($this->logDirectory) && !\mkdir($this->logDirectory, recursive: true) && !\is_dir($this->logDirectory)) {
            throw new \RuntimeException(\sprintf('Could not create log directory "%s".', $this->logDirectory));
        }

        if (\file_put_contents($filePath, $line, \FILE_APPEND | \LOCK_EX) === false) {
            throw new \RuntimeException(\sprintf('Could not write to log file "%s".', $filePath));
        }
    }
}
