<?php declare(strict_types=1);

namespace Newland\Toubiz\Poi\Neos\Tests\Integration;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Http\Request;
use Neos\Flow\Http\Response;
use Neos\Flow\Http\Uri;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Exception\NoMatchingRouteException;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Neos\Controller\Service\NodesController;
use Neos\Neos\Domain\Service\ContentContextFactory;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Tests\Factory\CategoryFactory;

class TranslatesReferencesInNodesWhenTranslatingNodeTest extends IntegrationTestCase
{
    protected $testableSecurityEnabled = true;

    /** @var ContentContextFactory */
    protected $contextFactory;

    /** @var Article */
    protected $de;

    /** @var Article */
    protected $fr;

    /** @var Article */
    protected $categoryDe;

    /** @var Article */
    protected $categoryFr;

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

        // In this test we don't care about permissions
        $this->privilegeManager->setOverrideDecision(true);
        $this->contextFactory = $this->objectManager->get(ContentContextFactory::class);

        $this->categoryDe = $this->categoryFactory->create([ 'language' => 'de', 'originalId' => 'category-foo' ]);
        $this->de = $this->articleFactory->create([
            'language' => 'de',
            'client' => 'foo-bar',
            'originalId' => 'foo-bar',
            'categories' => [ $this->categoryDe ],
        ]);
        $this->categoryFr = $this->categoryFactory->create([ 'language' => 'fr', 'originalId' => 'category-foo' ]);
        $this->fr = $this->articleFactory->create([
            'language' => 'fr',
            'client' => 'foo-bar',
            'originalId' => 'foo-bar',
            'categories' => [ $this->categoryFr ],
        ]);
    }

    public function testTranslatesTeaserReference(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:Teaser'));
        $this->node->setProperty('article', $this->de->getPersistenceObjectIdentifier());
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('article', $translatedProperties);
        $this->assertEquals($this->fr->getPersistenceObjectIdentifier(), $translatedProperties['article']);
    }

    public function testUnsetsPropertyIfNoDetailTranslationFound(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:Teaser'));
        $this->node->setProperty('article', $this->de->getPersistenceObjectIdentifier());
        $this->persistNode($this->node);

        // Italian article does not exist
        $translatedNode = $this->sendTranslationRequestToNeosBackendService('it');
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayNotHasKey('article', $translatedProperties);
    }

    public function testTranslatesDetailReference(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:Show'));
        $this->node->setProperty('article', $this->de->getPersistenceObjectIdentifier());
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('article', $translatedProperties);
        $this->assertEquals($this->fr->getPersistenceObjectIdentifier(), $translatedProperties['article']);
    }

    public function testTranslatesPreselectedCitiesInLists(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:List'));
        $this->node->setProperty('preselectedCities', [ $this->de->getPersistenceObjectIdentifier() ]);
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('preselectedCities', $translatedProperties);
        $this->assertEquals($this->fr->getPersistenceObjectIdentifier(), $translatedProperties['preselectedCities'][0]);
    }


    public function testTranslatesPreselectedCitiesInFilteredLists(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:FilteredLists'));
        $this->node->setProperty('preselectedCities', [ $this->de->getPersistenceObjectIdentifier() ]);
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('preselectedCities', $translatedProperties);
        $this->assertEquals($this->fr->getPersistenceObjectIdentifier(), $translatedProperties['preselectedCities'][0]);
    }

    public function testTranslatesCategoryReferencesInList(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:List'));
        $this->node->setProperty('preselectedCategories', [ $this->categoryDe->getPersistenceObjectIdentifier() ]);
        $this->persistNode($this->node);
        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('preselectedCategories', $translatedProperties);
        $this->assertEquals($this->categoryFr->getPersistenceObjectIdentifier(), $translatedProperties['preselectedCategories'][0]);

    }

    public function testTranslatesCategoryReferencesInFilteredList(): void
    {
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:FilteredLists'));
        $this->node->setProperty('preselectedCategories', [ $this->categoryDe->getPersistenceObjectIdentifier() ]);
        $this->persistNode($this->node);
        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('preselectedCategories', $translatedProperties);
        $this->assertEquals($this->categoryFr->getPersistenceObjectIdentifier(), $translatedProperties['preselectedCategories'][0]);
    }

    public function testRetainsArticleReferencesWithoutLanguage(): void
    {
        $untranslatedArticle = $this->articleFactory->create([ 'language' => null ]);

        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:Teaser'));
        $this->node->setProperty('article', $untranslatedArticle->getPersistenceObjectIdentifier());
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('article', $translatedProperties);
        $this->assertEquals($untranslatedArticle->getPersistenceObjectIdentifier(), $translatedProperties['article']);
    }

    public function testRetainsCitiesReferencesWithoutLanguage(): void
    {
        $untranslatedArticle = $this->articleFactory->create([ 'language' => null ]);

        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:FilteredLists'));
        $this->node->setProperty('preselectedCities', [ $untranslatedArticle->getPersistenceObjectIdentifier() ]);
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('preselectedCities', $translatedProperties);
        $this->assertEquals($untranslatedArticle->getPersistenceObjectIdentifier(), $translatedProperties['preselectedCities'][0]);
    }

    public function testRetainsCategoryReferencesWithoutLanguage(): void
    {
        $untranslatedCategory = $this->categoryFactory->create([ 'language' => null ]);

        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:FilteredLists'));
        $this->node->setProperty('preselectedCategories', [ $untranslatedCategory->getPersistenceObjectIdentifier() ]);
        $this->persistNode($this->node);

        $translatedNode = $this->sendTranslationRequestToNeosBackendService();
        $translatedProperties = $translatedNode->getProperties();

        $this->assertArrayHasKey('preselectedCategories', $translatedProperties);
        $this->assertEquals($untranslatedCategory->getPersistenceObjectIdentifier(), $translatedProperties['preselectedCategories'][0]);
    }

    private function sendTranslationRequestToNeosBackendService(string $target = 'fr'): ?NodeInterface
    {
        // Note: Not sending request through $this->browser since that seems to modify the global state causing tests
        //       after this test case to fail.
        $request = new ActionRequest(Request::create(new Uri('http://localhost/neos/service/nodes'), 'POST'));
        $request->setControllerActionName('create');
        $request->setArguments([
            'identifier' => $this->documentNode->getIdentifier(),
            'dimensions' => [ 'language' => [ $target ] ],
            'sourceDimensions' => [ 'language' => [ 'de' ] ],
            'mode' => 'adoptFromAnotherDimensionAndCopyContent',
        ]);

        /** @var NodesController $controller */
        $controller = $this->objectManager->get(NodesController::class);
        try {
            $controller->processRequest($request, new Response());
        } catch (StopActionException $e) {
            // Do nothing
        } catch (NoMatchingRouteException $e) {
            // Do nothing
        }

        return $this->getTranslatedNode($target);
    }

    private function getTranslatedNode(string $language, string $nodeId = null): ?NodeInterface
    {
        $nodeId = $nodeId ?? $this->node->getIdentifier();

        $contextProperties = [
            'workspaceName' => 'live',
            'invisibleContentShown' => true,
            'inaccessibleContentShown' => true,
            'dimensions' => [ 'language' => [ $language ] ],
            'targetDimensions' => [ 'language' => $language ],
        ];

        return $this->contextFactory->create($contextProperties)
            ->getNodeByIdentifier($nodeId);
    }
}
