<?php declare(strict_types=1);

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

use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\Http\Request;
use Neos\Flow\Http\Response;
use Neos\Flow\Http\Uri;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Tests\FunctionalTestCase;
use Newland\NeosTestingHelpers\InteractsWithNodes;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Poi\Neos\Controller\FilteredListsController;
use Newland\Toubiz\Poi\Neos\Tests\Unit\ViewHelpers\Widget\Controller\Mock\MockView;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Newland\Toubiz\Sync\Neos\Tests\Factory\ArticleFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\AttributeFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\CategoryFactory;

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

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

    /** @var FilteredListsController */
    protected $subject;

    /** @var ArticleFactory */
    protected $factory;

    /** @var AttributeFactory */
    protected $attributeFactory;

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

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

        $this->factory = new ArticleFactory($this->objectManager);
        $this->attributeFactory = new AttributeFactory($this->objectManager);

        $this->view = new MockView();
        $this->subject = $this->objectManager->get(FilteredListsController::class);
        $this->inject($this->subject, 'view', $this->view);

        $this->node = $this->initializeNode('/sites/foo', null, null, [], 'de');
    }

    public function testAssignsAllToursForConfiguredMainTypeToView(): void
    {
        $germanMatching = $this->factory->create([ 'language' => 'de', 'mainType' => 2 ]);
        $germanNonMatching = $this->factory->create([ 'language' => 'de', 'mainType' => 1 ]);
        $englishMatching = $this->factory->create([ 'language' => 'en', 'mainType' => 2 ]);
        $this->callAction('show', [ 'articleType' => 2 ]);

        $ids = array_map(
            function(Article $article) {
                return $article->getPersistenceObjectIdentifier();
            },
            $this->view->assignments['articles']
        );

        $this->assertContains($germanMatching->getPersistenceObjectIdentifier(), $ids, 'Should contain german article with matching type');
        $this->assertNotContains($germanNonMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain german article with other type');
        $this->assertNotContains($englishMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain english article with matching type');
    }

    public function testShouldFilterForPreselectedCategories(): void
    {
        $categoryFactory = new CategoryFactory($this->objectManager);
        $germanCategory = $categoryFactory->create([ 'language' => 'de' ]);
        $germanOtherCategory = $categoryFactory->create([ 'language' => 'de' ]);
        $englishCategory = $categoryFactory->create([ 'language' => 'en' ]);

        $germanMatching = $this->factory->create([ 'language' => 'de', 'mainType' => 2, 'categories' => [ $germanCategory ] ]);
        $englishMatching = $this->factory->create([ 'language' => 'en', 'mainType' => 2, 'categories' => [ $germanOtherCategory ] ]);
        $germanNonMatching = $this->factory->create([ 'language' => 'de', 'mainType' => 2, 'categories' => [ $englishCategory ] ]);

        $this->callAction('show', [ 'articleType' => 2, 'preselectedCategories' => [ $germanCategory->getPersistenceObjectIdentifier() ] ]);
        $ids = array_map(
            function(Article $article) {
                return $article->getPersistenceObjectIdentifier();
            },
            $this->view->assignments['articles']
        );

        $this->assertContains($germanMatching->getPersistenceObjectIdentifier(), $ids, 'Should contain german article with matching type');
        $this->assertNotContains($germanNonMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain german article with other type');
        $this->assertNotContains($englishMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain english article with matching type');

    }

    public function testShouldFilterForPreselectedProperties(): void
    {
        $germanMatching = $this->factory->create([
             'language' => 'de',
             'mainType' => 2,
             'attributes' => [ $this->attributeFactory->make([ 'name' => 'properties', 'data' => 'loopTour' ]) ],
         ]);
        $englishMatching = $this->factory->create([
            'language' => 'en',
            'mainType' => 2,
            'attributes' => [ $this->attributeFactory->make([ 'name' => 'properties', 'data' => 'loopTour' ]) ],
        ]);
        $germanNonMatching = $this->factory->create([
            'language' => 'de',
            'mainType' => 2,
            'attributes' => [ $this->attributeFactory->make([ 'name' => 'properties', 'data' => 'oneWayTour' ]) ],
        ]);

        $this->callAction('show', [ 'articleType' => 2, 'preselectedProperties' => [ 'properties:loopTour' ] ]);
        $ids = array_map(
            function(Article $article) {
                return $article->getPersistenceObjectIdentifier();
            },
            $this->view->assignments['articles']
        );

        $this->assertContains($germanMatching->getPersistenceObjectIdentifier(), $ids, 'Should contain german article with matching type');
        $this->assertNotContains($germanNonMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain german article with other type');
        $this->assertNotContains($englishMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain english article with matching type');
    }


    public function testShouldFilterForPreselectedTags(): void
    {
        $germanMatching = $this->factory->create([
             'language' => 'de',
             'mainType' => ArticleConstants::TYPE_ATTRACTION,
             'attributes' => [ $this->attributeFactory->make([ 'name' => 'tag', 'data' => 'foo' ]) ],
         ]);
        $englishMatching = $this->factory->create([
            'language' => 'en',
            'mainType' => ArticleConstants::TYPE_ATTRACTION,
            'attributes' => [ $this->attributeFactory->make([ 'name' => 'tag', 'data' => 'foo' ]) ],
        ]);
        $germanNonMatching = $this->factory->create([
            'language' => 'de',
            'mainType' => ArticleConstants::TYPE_ATTRACTION,
            'attributes' => [ $this->attributeFactory->make([ 'name' => 'tag', 'data' => 'bar' ]) ],
        ]);

        $this->callAction('show', [ 'articleType' => ArticleConstants::TYPE_ATTRACTION, 'preselectedTags' => [ 'tag:foo' ] ]);
        $ids = array_map(
            function(Article $article) {
                return $article->getPersistenceObjectIdentifier();
            },
            $this->view->assignments['articles']
        );

        $this->assertContains($germanMatching->getPersistenceObjectIdentifier(), $ids, 'Should contain german article with matching type');
        $this->assertNotContains($germanNonMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain german article with other type');
        $this->assertNotContains($englishMatching->getPersistenceObjectIdentifier(), $ids, 'Should not contain english article with matching type');
    }

    public function testRecoversFromTaintedRepository(): void
    {
        $german = $this->factory->create([ 'language' => 'de', 'mainType' => 2 ]);
        $english = $this->factory->create([ 'language' => 'en', 'mainType' => 2 ]);

        // Taint repository on purpose
        $this->objectManager->get(ArticleRepository::class)->setLanguage(null);

        $this->callAction('show', [ 'articleType' => 2 ]);

        $ids = array_map(
            function(Article $article) {
                return $article->getPersistenceObjectIdentifier();
            },
            $this->view->assignments['articles']
        );

        $this->assertContains($german->getPersistenceObjectIdentifier(), $ids, 'Should contain german article');
        $this->assertNotContains($english->getPersistenceObjectIdentifier(), $ids, 'Should not contain english article');
    }


    private function callAction(string $action, array $properties): Response
    {
        $request = new ActionRequest(Request::create(new Uri('http://localhost')));
        $request->setControllerActionName($action);

        foreach ($this->node->getProperties() as $key => $value) {
            $this->node->removeProperty($key);
        }
        foreach ($properties as $key => $value) {
            $this->node->setProperty($key, $value);
        }
        $this->persistNode($this->node);
        $request->setArgument('node', $this->node->getContextPath());

        $response = new Response();

        $this->subject->processRequest($request, $response);
        return $response;
    }


}
