<?php
namespace Newland\PageFrameProvider\Tests\Service;


use Neos\Flow\Http\Uri;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Exception\NoMatchingRouteException;
use Neos\Flow\Mvc\Routing\Route;
use Neos\Flow\Mvc\Routing\UriBuilder;
use Neos\Flow\Tests\FunctionalTestCase;
use Newland\PageFrameProvider\NodeResolution\SiteNodeResolver;
use Newland\PageFrameProvider\Service\PageFrameLinkingService;

class PageFrameLinkingServiceTest extends FunctionalTestCase
{
    /** @var PageFrameLinkingService */
    protected $pageFrameLinkingService;

    /** @var UriBuilder */
    protected $uriBuilder;

    /** @var Route */
    protected $route;

    public function setUp()
    {
        parent::setUp();
        $this->pageFrameLinkingService = $this->objectManager->get(PageFrameLinkingService::class);
        $this->inject($this->pageFrameLinkingService, 'nodeResolver', $this->objectManager->get(SiteNodeResolver::class));

        // Add example route on path `foo/bar`
        $this->route = new Route();
        $this->route->setAppendExceedingArguments(true);
        $this->route->setUriPattern('foo/bar');
        $this->route->setDefaults([
            '@package' => 'newland.pageframeprovider',
            '@controller' => 'pageframe',
            '@action' => 'show',
            '@format' => 'html',
            'pageFrame' => 'default',
            '--plugin' => [
                '@package' => 'foo.bar',
                '@controller' => 'foo',
                '@action' => 'bar',
                '@format' => 'html',
            ]
        ]);
        $this->router->addRoute($this->route);

        // Initialize required uriBuilder.
        $requestHandler = self::$bootstrap->getActiveRequestHandler();
        $httpRequest = $requestHandler->getHttpRequest();
        $httpRequest->setBaseUri(new Uri('http://neos.test/'));
        $rootRequest = new ActionRequest($httpRequest);
        $subRequest = new ActionRequest($rootRequest);
        $subRequest->setArgumentNamespace('foobar');
        $this->uriBuilder = new UriBuilder();
        $this->uriBuilder->setRequest($subRequest);
    }

    public function testShouldGenerateLinkToPageFrameController()
    {
        $url = $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar'
        );

        $this->assertEquals('/foo/bar', $url);
    }

    public function testShouldUsePageFrameToMatchAction()
    {
        $this->route->setDefaults(array_replace_recursive(
            $this->route->getDefaults(),
            [ 'pageFrame' => 'foo' ]
        ));

        // Matches route with pageFrame=foo
        $url = $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar',
            'foo'
        );
        $this->assertEquals('/foo/bar', $url);

        // Does not match route with pageFrame=bar
        $this->expectException(NoMatchingRouteException::class);
        $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar',
            'bar'
        );
    }

    public function testShouldUseDefaultsToMatchAction()
    {
        $this->route->setDefaults(array_replace_recursive(
            $this->route->getDefaults(),
            [ '--plugin' => [ '_type' => 'test' ] ]
        ));

        // Matches route with _type=foo
        $url = $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar',
            PageFrameLinkingService::DEFAULT_PAGE_FRAME,
            [ '_type' => 'test' ]
        );
        $this->assertEquals('/foo/bar', $url);

        // Does not match route without _type
        $this->expectException(NoMatchingRouteException::class);
        $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar'
        );
    }

    public function testShouldNotIncludeParametersOfRootRequestInGeneratedUrl()
    {
        $this->uriBuilder->getRequest()->setArguments([ '@controller' => 'foobar' ]);
        $url = $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar'
        );
        $this->assertNotContains(
            'foobar%5B%40controller%5D=foobar',
            $url,
            'Should not contain propagated @controller of current request'
        );
        $this->assertEquals('/foo/bar', $url);
    }

    public function testShouldNotIncludeInternalParametersInGeneratedUrl()
    {
        $url = $this->pageFrameLinkingService->build(
            $this->uriBuilder,
            'foo.bar',
            'foo',
            'bar',
            PageFrameLinkingService::DEFAULT_PAGE_FRAME,
            [ '_test' => 'foo' ]
        );

        $this->assertNotContains(
            '--plugin%5B_test%5D=foo',
            $url,
            'URL should not contain internal _test argument (--plugin namespaced)');
        $this->assertNotContains(
            '_test=foo',
            $url,
            'URL should not contain internal _test argument (non-namespaced)');
        $this->assertEquals('/foo/bar', $url);
    }

    public function testRemovesDuplicateSlashesFromUri()
    {
        $this->assertEquals(
            'http://test.com/foo/bar/baz',
            $this->pageFrameLinkingService->cleanUrl('http://test.com/foo//bar///baz')
        );
    }

    public function testShouldBeAbleToDetectIfTheGivenRequestVarsAreForAPageFrameRequest()
    {
        $this->assertTrue(
            $this->pageFrameLinkingService->isPageFrameActionLink(
                [
                    '@package' => 'newland.pageframeprovider',
                    '@controller' => 'pageframe',
                    '@action' => 'show',
                    '--plugin' => [
                        '@package' => 'foo.bar',
                        '@controller' => 'foo',
                        '@action' => 'bar'
                    ]
                ],
                'foo.bar',
                'foo',
                'bar'
            ),
            'Should match if request and sub-request matches'
        );

        $this->assertFalse(
            $this->pageFrameLinkingService->isPageFrameActionLink(
                [
                    '@package' => 'foo.bar',
                    '@controller' => 'foo',
                    '@action' => 'bar'
                ],
                'foo.bar',
                'foo',
                'bar'
            ),
            'Show not match if the request is for the controller directly (without pageframe)'
        );

        $this->assertFalse(
            $this->pageFrameLinkingService->isPageFrameActionLink(
                [
                    '@package' => 'newland.pageframeprovider',
                    '@controller' => 'pageframe',
                    '@action' => 'show',
                    '--plugin' => [
                        '@package' => 'foo.bar',
                        '@controller' => 'different',
                        '@action' => 'alsoDifferent'
                    ]
                ],
                'foo.bar',
                'foo',
                'bar'
            ),
            'Should not match if the request is for a different controller inside of the pageframe'
        );
    }

}
