<?php

namespace Newland\Toubiz\Poi\Neos\Tests\Unit\ViewHelpers\Widget\Controller;

use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\Http\Request;
use Neos\Flow\Http\Uri;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Http\Response;
use Neos\Flow\Tests\FunctionalTestCase;
use Neos\FluidAdaptor\Core\Widget\WidgetContext;
use Neos\Neos\Domain\Model\Site;
use Neos\Neos\Service\LinkingService;
use Newland\NeosTestingHelpers\InteractsWithNodes;
use Newland\Toubiz\Poi\Neos\Tests\Factory\AddressFactory;
use Newland\Toubiz\Poi\Neos\Tests\Unit\ViewHelpers\Widget\Controller\Mock\MockView;
use Newland\Toubiz\Poi\Neos\ViewHelpers\Widget\Controller\LinkListController;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Domain\Model\CityData;
use Newland\Toubiz\Sync\Neos\Tests\Factory\ArticleFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\CityDataFactory;

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

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

    /** @var MockView */
    private $view;

    /** @var Article */
    private $article;

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

    public function setUp(): void
    {
        parent::setUp();

        $this->node = $this->initializeNode('/sites/foo-bar', null, new Site('foo-bar'), [], 'en');
        $this->article = (new ArticleFactory($this->objectManager))->make();
        $this->view = new MockView();
        $this->subject = $this->objectManager->get(LinkListController::class);
        $this->inject($this->subject, 'view', $this->view);
    }

    public function testShouldNotGenerateLinksIfDisabled(): void
    {
        $this->callIndexAction(
            [
                'links' => [
                    'enabled' => false,
                    'data' => [
                        'foo' => [ 'target' => 'foo' ],
                    ],
                ],
            ]
        );

        $this->assertCount(0, $this->view->assignments['links']);
    }

    public function testFillsPlaceholdersInUris(): void
    {
        $city = new CityData();
        $city->setIdTomas('id_tomas');
        $city->setIdToubiz('id_toubiz');
        $this->article->setCityData($city);

        $this->callIndexAction(
            [
                'links' => [
                    'enabled' => true,
                    'data' => [
                        'foo' => [ 'target' => 'foo/{idToubiz}/bar/{idTomas}/baz/{unknown}' ],
                    ],
                ],
            ]
        );

        $this->assertCount(1, $this->view->assignments['links']);
        $this->assertEquals(
            'foo/id_toubiz/bar/id_tomas/baz/{unknown}',
            $this->view->assignments['links']['foo']['url']
        );
    }

    public function testUsesLinkingServiceToResolveNodeUris(): void
    {
        $linkingService = $this->createMock(LinkingService::class);
        $linkingService->method('convertUriToObject')->willReturn($this->node);
        $linkingService
            ->expects($this->once())
            ->method('createNodeUri')
            ->with(
                $this->anything(),
                $this->equalTo($this->node),
                $this->equalTo($this->node),
                null,
                false,
                [
                    '--some-plugin-namespace' => [
                        'query' => [
                            'city' => $this->article->getPersistenceObjectIdentifier(),
                        ],
                    ],
                ]
            )->willReturn('generated/uri');

        $this->inject($this->subject, 'linkingService', $linkingService);
        $this->callIndexAction(
            [
                'links' => [
                    'enabled' => true,
                    'data' => [
                        'foo' => [
                            'target' => 'node://' . $this->node->getIdentifier(),
                            'pluginNamespace' => '--some-plugin-namespace',
                        ],
                    ],
                ],
            ]
        );

        $this->assertEquals('generated/uri', $this->view->assignments['links']['foo']['url']);
    }

    public function testHideLinkForEmptyResult(): void
    {
        $cityData = (new CityDataFactory($this->objectManager))->make([ 'zipCodes' => [ 10245 ] ]);
        $articleFactory = new ArticleFactory($this->objectManager);
        $this->article = $articleFactory->make([ 'cityData' => $cityData ]);

        // attraction outside region
        $articleFactory->create(
            [
                'mainType' => 0,
                'language' => 'en',
                'mainAddress' => AddressFactory::make(null, [ 'zip' => '99999' ]),
            ]
        );

        // tour within region
        $articleFactory->create(
            [
                'mainType' => 2,
                'language' => 'en',
                'mainAddress' => AddressFactory::make(null, [ 'zip' => '10245' ]),
            ]
        );

        $this->callIndexAction(
            [
                'links' => [
                    'enabled' => true,
                    'data' => [
                        'attraction' => [
                            'target' => 'node://' . $this->node->getIdentifier(),
                            'pluginNamespace' => '--some-plugin-namespace',
                            'type' => 'article',
                            'mainType' => 0,
                        ],
                        'tours' => [
                            'target' => 'node://' . $this->node->getIdentifier(),
                            'pluginNamespace' => '--some-plugin-namespace',
                            'type' => 'article',
                            'mainType' => 2,
                        ],
                    ],
                ],
            ]
        );

        $this->assertFalse(
            $this->view->assignments['links']['attraction']['hasResults'],
            'The attraction is outside the region and should not be found.'
        );
        $this->assertTrue(
            $this->view->assignments['links']['tours']['hasResults'],
            'The tour is within the region and should be found.'
        );
    }

    private function callIndexAction(array $cityDetailConfiguration): Response
    {
        $request = new ActionRequest(Request::create(new Uri('http://localhost')));

        $context = new WidgetContext();
        $context->setNonAjaxWidgetConfiguration(
            [
                'article' => $this->article,
                'node' => $this->node,
            ]
        );
        $request->setArgument('__widgetContext', $context);
        $request->setControllerActionName('index');

        $response = new Response();

        $this->inject($this->subject, 'cityDetailsConfiguration', $cityDetailConfiguration);
        $this->subject->processRequest($request, $response);
        return $response;
    }

}
