<?php declare(strict_types=1);

namespace Newland\NeosCommon\Tests\Unit\LinkHandler;

use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Neos\Flow\Tests\FunctionalTestCase;
use Newland\NeosCommon\LinkHandler\LinkHandlerFactory;
use Newland\NeosCommon\Tests\Unit\LinkHandler\Mock\ClassThatDoesNotExtendLinkHandler;
use Newland\NeosCommon\Tests\Unit\LinkHandler\Mock\LinkHandlerMock;

class LinkHandlerFactoryTest extends FunctionalTestCase
{

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

    public function setUp(): void
    {
        parent::setUp();
        $this->subject = $this->objectManager->get(LinkHandlerFactory::class);
    }

    public function testDoesNotResolveOnInvalidConfiguration()
    {
        $this->inject($this->subject, 'configuration', [
            'foobar' => 'baz'
        ]);

        $this->expectException(InvalidConfigurationException::class);
        $this->subject->get('foo');
    }

    public function testResolvesConfiguredLinkHandler()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [
                    'handler' => LinkHandlerMock::class
                ]
            ]
        ]);

        $this->assertInstanceOf(
            LinkHandlerMock::class,
            $this->subject->get('foo')
        );
    }

    public function testInjectsSettingsIntoLinkHandler()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [
                    'handler' => LinkHandlerMock::class,
                    'someSetting' => 'mySetting'
                ]
            ]
        ]);

        $this->assertEquals(
            'mySetting',
            $this->subject->get('foo')->getConfiguration()['someSetting']
        );
    }

    public function testPassesRecordTypeNameDown()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [
                    'handler' => LinkHandlerMock::class,
                ]
            ]
        ]);

        $this->assertEquals(
            'foo',
            $this->subject->get('foo')->getRecordType()
        );
    }

    public function testPassesLanguageDown()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [
                    'handler' => LinkHandlerMock::class,
                ]
            ]
        ]);

        $this->assertEquals(
            'en',
            $this->subject->get('foo', 'en')->getLanguage()
        );
        $this->assertNull(
            $this->subject->get('foo')->getLanguage()
        );
    }

    public function testThrowsExceptionIfNonExistentClassConfigured()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [
                    'handler' => 'This\\Class\\Does\\Not\\Exist',
                ]
            ]
        ]);

        $this->expectException(InvalidConfigurationException::class);
        $this->subject->get('foo');
    }

    public function testThrowsExceptionIfLinkHandlerDoesNotImplementInterface()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [
                    'handler' => ClassThatDoesNotExtendLinkHandler::class,
                ]
            ]
        ]);

        $this->expectException(InvalidConfigurationException::class);
        $this->subject->get('foo');
    }

    public function testExtractsAllKnownTypes()
    {
        $this->inject($this->subject, 'configuration', [
            'recordTypes' => [
                'foo' => [ 'handler' => ClassThatDoesNotExtendLinkHandler::class ],
                'bar' => [ 'handler' => LinkHandlerMock::class ]
            ]
        ]);

        $this->assertEquals(
            [ 'foo', 'bar' ],
            $this->subject->allTypes()
        );
    }
}