<?php

namespace D3\LoggerFactory\tests;

use D3\LoggerFactory\LoggerFactory;
use Generator;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
use RuntimeException;

class LoggerFactoryTest extends ApiTestCase
{
    /**
     * @test
     * @return void
     * @covers \D3\LoggerFactory\LoggerFactory::create
     */
    public function testCreate(): void
    {
        $instance = LoggerFactory::create();

        $this->assertInstanceOf(LoggerFactory::class, $instance);
    }

    /**
     * @test
     * @throws ReflectionException
     * @covers \D3\LoggerFactory\LoggerFactory::getFileLogger
     * @covers \D3\LoggerFactory\LoggerFactory::getFileLoggerStreamHandler
     * @dataProvider getFileLoggerDataProvider
     */
    public function testGetFileLogger(int $logLevel, ?int $maxFiles, string $expectedHandlerClass): void
    {
        $sut = LoggerFactory::create();

        /** @var Logger|MockObject $logger */
        $logger = $this->callMethod(
            $sut,
            'getFileLogger',
            ['nameFixture', 'file/path.log', $logLevel, $maxFiles]
        );

        $this->assertInstanceOf(Logger::class, $logger);
        $this->assertInstanceOf($expectedHandlerClass, $logger->getHandlers()[0]);
        $this->assertSame($logLevel, $logger->getHandlers()[0]->getLevel());
    }

    public static function getFileLoggerDataProvider(): Generator
    {
        yield 'no rotation' => [Logger::INFO, null, StreamHandler::class];
        yield 'rotation 1' => [Logger::ERROR, 1, RotatingFileHandler::class];
        yield 'rotation 20' => [Logger::DEBUG, 20, RotatingFileHandler::class];
    }

    /**
     * @test
     * @return void
     * @throws ReflectionException
     * @covers \D3\LoggerFactory\LoggerFactory::getCombinedOxidAndFileLogger
     */
    public function testGetCombinedOxidAndFileLoggerWithoutOxid(): void
    {
        $sut = LoggerFactory::create();

        $this->expectException(RuntimeException::class);

        $this->callMethod(
            $sut,
            'getCombinedOxidAndFileLogger',
            ['nameFixture', 'file/path.log', 1, 5]
        );
    }

    /**
     * @test
     * @return void
     * @throws ReflectionException
     * @covers \D3\LoggerFactory\LoggerFactory::getOxidLogPath
     */
    public function testGetOxidLogPathWithoutOxid(): void
    {
        $sut = LoggerFactory::create();

        $this->expectException(RuntimeException::class);

        $this->assertSame(
            'foo',
            $this->callMethod(
                $sut,
                'getOxidLogPath',
                ['fixture.log']
            )
        );
    }

    /**
     * @test
     * @return void
     * @throws ReflectionException
     * @covers \D3\LoggerFactory\LoggerFactory::getCombinedOxidAndFileLogger
     */
    public function testAddCombinedOxidAndFileLoggerInOxid(): void
    {
        require_once __DIR__.'/Helpers/classAliases.php';

        $sut = LoggerFactory::create();

        $logger = $this->callMethod(
            $sut,
            'getCombinedOxidAndFileLogger',
            ['nameFixture', 'file/path.log', 1, 5]
        );

        $this->assertInstanceOf(Logger::class, $logger);
    }

    /**
     * @test
     * @return void
     * @throws ReflectionException
     * @covers \D3\LoggerFactory\LoggerFactory::getOxidLogPath
     */
    public function testGetOxidLogPathInOxid(): void
    {
        require_once __DIR__.'/Helpers/classAliases.php';

        $sut = LoggerFactory::create();

        $this->assertStringEndsWith(
            'tests/Helpers/log/fixture.log',
            $this->callMethod(
                $sut,
                'getOxidLogPath',
                ['fixture.log']
            )
        );
    }
}