<?php declare(strict_types=1);

namespace Newland\PageFrameProvider\Tests\NodeResolution;

use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Neos\Flow\Tests\FunctionalTestCase;
use Newland\PageFrameProvider\NodeResolution\ConfigurationBasedNodeResolver;
use Newland\PageFrameProvider\NodeResolution\RootNodeResolver;
use Newland\PageFrameProvider\Tests\NodeResolution\Mock\NotImplementingRootNodeResolver;
use Newland\PageFrameProvider\Tests\NodeResolution\Mock\ResolverImplementingInterface;

class ConfigurationBasedNodeResolverTest extends FunctionalTestCase
{

    /** @var ConfigurationBasedNodeResolver */
    private $subject;

    public function setUp(): void
    {
        ResolverImplementingInterface::$calls = 0;
        ResolverImplementingInterface::$lastSetConfiguration = null;
        parent::setUp();
        $this->subject = $this->objectManager->get(ConfigurationBasedNodeResolver::class);
        $this->inject($this->subject, 'objectManager', $this->objectManager);
        $this->inject($this->subject, 'configurationManager', $this->objectManager->get(ConfigurationManager::class));
    }

    public function tearDown(): void
    {
        parent::tearDown();
        ResolverImplementingInterface::$calls = 0;
        ResolverImplementingInterface::$lastSetConfiguration = null;
    }

    public function testUsesConfiguredResolver(): void
    {
        $this->inject($this->subject, 'resolutionConfiguration', [
            'class' => ResolverImplementingInterface::class
        ]);

        $resolver = $this->subject->resolveRootNode(null);
        $this->assertEquals(1, ResolverImplementingInterface::$calls);
    }

    public function testHandsDownSettingsToResolver(): void
    {
        $this->inject($this->subject, 'resolutionConfiguration', [
            'class' => ResolverImplementingInterface::class,
            'foo' => 'bar'
        ]);

        $resolver = $this->subject->resolveRootNode(null);
        $this->assertArraySubset([ 'foo' => 'bar' ], ResolverImplementingInterface::$lastSetConfiguration);
    }

    public function testHandsDownConfigurationToResolver(): void
    {
        $this->inject($this->subject, 'resolutionConfiguration', [
            'class' => ResolverImplementingInterface::class,
        ]);

        $this->subject->setConfiguration([ 'foo' => 'bar' ]);
        $this->assertArraySubset([ 'foo' => 'bar' ], ResolverImplementingInterface::$lastSetConfiguration);
    }

    public function testThrowsExceptionIfNoConfigurationFound()
    {
        $this->inject($this->subject, 'resolutionConfiguration', []);
        $this->expectException(InvalidConfigurationException::class);
        $this->expectExceptionMessageRegExp('/No resolver class specified/');
        $this->subject->resolveRootNode(null);
    }

    public function testThrowsExceptionIfClassNotFound()
    {
        $this->inject($this->subject, 'resolutionConfiguration', [
            'class' => 'Class\\Does\\Not\\Exist'
        ]);
        $this->expectException(InvalidConfigurationException::class);
        $this->expectExceptionMessageRegExp('/Unknown resolver class/');
        $this->subject->resolveRootNode(null);
    }

    public function testThrowsExceptionIfClassDoesNotImplementInterface()
    {
        $this->inject($this->subject, 'resolutionConfiguration', [
            'class' => NotImplementingRootNodeResolver::class
        ]);
        $this->expectException(InvalidConfigurationException::class);
        $this->expectExceptionMessageRegExp('/Resolver.*must implement/');
        $this->subject->resolveRootNode(null);
    }

}