<?php declare(strict_types=1);

namespace Newland\NeosCommon\Tests\Unit\LinkHandler;

use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\Tests\FunctionalTestCase;
use Neos\Neos\Domain\Service\NodeShortcutResolver;
use Newland\NeosCommon\LinkHandler\Domain\Model\Record;
use Newland\NeosCommon\LinkHandler\LinkHandlerShortcutResolver;
use Newland\NeosCommon\LinkHandler\LinkingService;
use Newland\NeosCommon\Tests\Unit\LinkHandler\Mock\LinkHandlerMock;
use Newland\NeosTestingHelpers\InteractsWithNodes;
use Newland\NeosTestingHelpers\InvokesJoinPoint;
use Mockery;
use Mockery\MockInterface;
use Newland\NeosCommon\LinkHandler\LinkHandlerFactory;

class LinkHandlerShortcutResolverTest extends FunctionalTestCase
{
    protected static $testablePersistenceEnabled = true;
    use InteractsWithNodes;
    use InvokesJoinPoint;

    /** @var MockInterface<NodeShortcutResolver> */
    private $originalResolver;

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

    /** @var Node */
    private $node;

    public function setUp(): void
    {
        parent::setUp();
        $this->subject = $this->objectManager->get(LinkHandlerShortcutResolver::class);
        $this->originalResolver = Mockery::mock($this->objectManager->get(NodeShortcutResolver::class));
        $this->node = $this->initializeNode('/sites/node-abc-def');
    }

    /**
     * @dataProvider provideUriConfigurations
     * @param string $target
     * @param array|null $linkHandlerConfiguration
     * @param string|null $expectedGeneratedUrl
     */
    public function testBuildsUris(?string $target, ?array $linkHandlerConfiguration, ?string $expectedGeneratedUrl)
    {
        if ($target) {
            $this->node->setProperty('target', $target);
        }
        if ($linkHandlerConfiguration) {
            $this->injectLinkhandlerConfiguration($linkHandlerConfiguration);
        }

        $result = $this->invokeAdvice(
            $this->originalResolver,
            'resolveShortcutTarget',
            $this->subject,
            'resolveShortcutTarget',
            [ 'node' => $this->node ]
        );

        if ($expectedGeneratedUrl) {
            $this->assertEquals($result, $expectedGeneratedUrl);
        } else {
            $this->expectNotToPerformAssertions();
            $this->originalResolver->shouldHaveReceived('resolveShortcutTarget');
        }
    }

    public function provideUriConfigurations(): array
    {
        $linkHandlerConfiguration = [
            'recordTypes' => [
                'foo' => [
                    'handler' => LinkHandlerMock::class,
                    'recordToFind' => new Record(),
                    'linkToGenerate' => 'http://link-handler.com/generated-url'
                ],
            ],
        ];

        return [
            'noTargetSpecified' => [ null, $linkHandlerConfiguration, null ],
            'notRecordUri' => [ 'node://foo-bar', $linkHandlerConfiguration, null ],
            'recordUri' => [ 'record://foo:test', $linkHandlerConfiguration, 'http://link-handler.com/generated-url' ],
            'recordUriButLinkhandlerNotConfigured' => [ 'record://unknown:test', $linkHandlerConfiguration, null ],
            'recordUriWithNodeWorkaround' => [ 'node://record:foo:test', $linkHandlerConfiguration, 'http://link-handler.com/generated-url' ],
            'recordUriButLinkHandlerCannotBuild' => [
                'record://foo:test',
                array_replace_recursive(
                    $linkHandlerConfiguration,
                    [ 'recordTypes' => [ 'foo' => [ 'linkToGenerate' => null ] ] ]
                ),
                null,
            ],
            'noLinkhandlerConfiguration' => [ 'node://foo-bar', null, null ],
        ];
    }

    private function injectLinkhandlerConfiguration(array $configuration): void
    {
        $service = $this->objectManager->get(LinkingService::class);
        $factory = $this->objectManager->get(LinkHandlerFactory::class);
        $this->inject($factory, 'configuration', $configuration);
        $this->inject($service, 'linkHandlerFactory', $factory);
        $this->inject($this->subject, 'linkingService', $service);
    }

}