<?php declare(strict_types=1);

namespace Newland\Toubiz\Sync\Neos\Logging;

use Monolog\DateTimeImmutable;
use Monolog\Handler\AbstractHandler;
use Monolog\Handler\HandlerInterface;
use Monolog\Logger;

/**
 * Wraps another handler and only calls it once at the very end with a summary message that lists
 * how many log entries occurred.
 *
 * - The `level` defines which records are summarized
 * - The `triggerLevel` defines when a summary should be sent at all
 */
class SummaryHandler extends AbstractHandler
{
    /** @var HandlerInterface */
    private $handler;

    /** @var int[] */
    private $counts;

    /** @var int */
    private $maxLevel;

    /** @var int */
    private $triggerLevel;

    public function __construct(int $level, HandlerInterface $handler, int $triggerLevel = null)
    {
        parent::__construct($level);
        $this->handler = $handler;
        $this->reset();
        $this->triggerLevel = $triggerLevel ?? $level;
    }

    public function reset(): void
    {
        $this->counts = [];
        $this->maxLevel = Logger::DEBUG;
    }

    public function handle(array $record): bool
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        $level = strtolower($record['level_name'] ?? 'unknown');
        $this->counts[$level] = ($this->counts[$level] ?? 0) + 1;

        if ($record['level'] > $this->maxLevel) {
            $this->maxLevel = $record['level'];
        }

        return false;
    }

    public function close(): void
    {
        if ($this->maxLevel >= $this->triggerLevel && count($this->counts) > 0) {
            $this->handler->handle($this->summaryRecord());
        }

        $this->reset();
    }

    private function summaryRecord(): array
    {
        $formattedCounts = [];
        foreach ($this->counts as $name => $count) {
            $formattedCounts[] = sprintf('*%s*: %d', $name, $count);
        }

        return [
            'message' => implode(', ', $formattedCounts),
            'level' => $this->maxLevel,
            'level_name' => '',
            'context' => [],
            'channel' => '',
            'datetime' => new DateTimeImmutable(false),
            'extra' => $this->counts,
        ];
    }

    public function setTriggerLevel(int $triggerLevel): void
    {
        $this->triggerLevel = $triggerLevel;
    }
}
