<?php declare(strict_types=1);

namespace Newland\Toubiz\Sync\Neos\Tests\Unit\Logging;

use Monolog\DateTimeImmutable;
use Monolog\Handler\TestHandler;
use Monolog\Logger;
use Neos\Flow\Tests\UnitTestCase;
use Newland\Toubiz\Sync\Neos\Logging\SummaryHandler;

class SummaryHandlerTest extends UnitTestCase
{

    /** @var TestHandler */
    protected $inner;

    /** @var SummaryHandler */
    protected $subject;

    public function setUp(): void
    {
        parent::setUp();
        $this->inner = new TestHandler();
        $this->subject = new SummaryHandler(Logger::WARNING, $this->inner, Logger::ERROR);
    }


    public function testShouldSendSingleMessageToWrappedHandlerOnClose(): void
    {
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ERROR));

        $this->assertEmpty($this->inner->getRecords());
        $this->subject->close();
        $this->assertCount(1, $this->inner->getRecords());
    }

    public function testSummaryHasHighestSeverityOfCounted(): void
    {
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ALERT));
        $this->subject->handle($this->record(Logger::WARNING));

        $this->subject->close();
        $this->assertEquals(Logger::ALERT, $this->inner->getRecords()[0]['level']);
    }

    public function testCountsMessagesBySeverity(): void
    {
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ALERT));
        $this->subject->handle($this->record(Logger::ALERT));
        $this->subject->handle($this->record(Logger::WARNING));
        $this->subject->handle($this->record(Logger::WARNING));
        $this->subject->handle($this->record(Logger::WARNING));

        $this->subject->close();
        $this->assertEquals(1, $this->inner->getRecords()[0]['extra']['error']);
        $this->assertEquals(2, $this->inner->getRecords()[0]['extra']['alert']);
        $this->assertEquals(3, $this->inner->getRecords()[0]['extra']['warning']);
    }

    public function testDoesNotSendIfOnlyLogEntriesUnderTriggerSeverityWereDetected(): void
    {
        // See setup: trigger severity is ERROR. If only WARNING is sent, then nothing should be passed on.
        $this->subject->handle($this->record(Logger::WARNING));
        $this->subject->handle($this->record(Logger::WARNING));
        $this->subject->handle($this->record(Logger::WARNING));
        $this->subject->handle($this->record(Logger::WARNING));

        $this->subject->close();
        $this->assertEmpty($this->inner->getRecords());
    }


    public function testShouldResetOnClose(): void
    {
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ERROR));
        $this->subject->handle($this->record(Logger::ERROR));

        $this->subject->close();
        $this->assertEquals(4, $this->inner->getRecords()[0]['extra']['error']);
        $this->assertArrayNotHasKey('alert', $this->inner->getRecords()[0]['extra']);

        $this->subject->handle($this->record(Logger::ALERT));
        $this->subject->handle($this->record(Logger::ALERT));
        $this->subject->handle($this->record(Logger::ALERT));
        $this->subject->handle($this->record(Logger::ALERT));

        $this->subject->close();
        $this->assertEquals(4, $this->inner->getRecords()[1]['extra']['alert']);
        $this->assertArrayNotHasKey('error', $this->inner->getRecords()[1]['extra']);
    }

    private function record(int $level): array
    {
        return [
            'message' => 'test',
            'level' => $level,
            'level_name' => Logger::getLevelName($level),
            'context' => [],
            'channel' => '',
            'datetime' => new DateTimeImmutable(false),
        ];
    }
}
