<?php declare(strict_types=1);

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

use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Tests\FunctionalTestCase;
use Neos\Neos\Domain\Model\Site;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Newland\NeosTestingHelpers\InteractsWithNodes;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Poi\Neos\LinkHandler\ArticleLinkHandler;
use Newland\Toubiz\Poi\Neos\LinkHandler\ArticleRecord;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Newland\Toubiz\Sync\Neos\Tests\Factory\ArticleFactory;

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

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

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

    /** @var Site */
    protected $site;

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

    /** @var ControllerContext */
    protected $controllerContext;

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

        $this->site = $this->initializeSite('foo-bar');
        $this->node = $this->initializeNode('/sites/foo-bar', 'live', $this->site);
        $this->controllerContext = $this->objectManager->get(ControllerContextFactory::class)->initializeFakeControllerContext($this->node);

        $this->subject = $this->objectManager->get(ArticleLinkHandler::class);
        $this->factory = new ArticleFactory($this->objectManager);
    }

    public function testFetchesArticleFromRepositoryWhenFindingLinkByIdentifier(): void
    {
        $article = $this->factory->create([ 'name' => 'This is a test' ]);
        $record = $this->subject->findByIdentifier($article->getPersistenceObjectIdentifier());

        $this->assertInstanceOf(ArticleRecord::class, $record);
        $this->assertEquals($article->getPersistenceObjectIdentifier(), $record->getId());
        $this->assertEquals($article->getPersistenceObjectIdentifier(), $record->getArticle()->getPersistenceObjectIdentifier());
    }

    public function testSearchesArticlesBySearchTerm(): void
    {
        $article = $this->factory->create([ 'name' => 'This is a test' ]);
        $results = $this->subject->findBySearchTerm('This');

        $this->assertEquals(1, count($results));
        $this->assertEquals($article->getPersistenceObjectIdentifier(), $results[0]->getId());
    }

    public function testGeneratesLinkForArticle(): void
    {
        $article = $this->factory->create([ 'name' => 'This is a test', 'mainType' => 2 ]);
        $record = $this->subject->findByIdentifier($article->getPersistenceObjectIdentifier());
        $link = $this->subject->generateRecordLink($record, $this->controllerContext, $this->node, false);
        $this->assertContains('/tour/this-is-a-test-', $link);
    }

    public function testSettingLanguageDoesNotTaintRepository(): void
    {
        $repository = $this->objectManager->get(ArticleRepository::class);
        $repository->setLanguage('de');
        $german = $this->factory->create([ 'language' => 'de' ]);
        $english = $this->factory->create([ 'language' => 'en' ]);

        $this->subject->setLanguage('en');

        $ids = [];
        foreach ($repository->findAll() as $a) {
            $ids[] = $a->getPersistenceObjectIdentifier();
        }

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

    public function testGeneratingLinkDoesNotTaintRepository(): void
    {
        $repository = $this->objectManager->get(ArticleRepository::class);
        $repository->setLanguage('de');

        $article = $this->factory->create([ 'language' => 'en' ]);
        $this->subject->setLanguage('en');
        $record = $this->subject->findByIdentifier($article->getPersistenceObjectIdentifier());
        $link = $this->subject->generateRecordLink($record, $this->controllerContext, $this->node, false);

        $ids = [];
        foreach ($repository->findAll() as $a) {
            $ids[] = $a->getPersistenceObjectIdentifier();
        }

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

    public function testAppendsArticleTypeToTitleIfAvailable(): void
    {
        $article = $this->factory->create([ 'language' => 'de', 'mainType' => ArticleConstants::TYPE_TOUR ]);
        $this->subject->setLanguage('de');
        $record = $this->subject->findByIdentifier($article->getPersistenceObjectIdentifier());
        $this->assertContains('[Tour]', $record->getTitle());
    }

    public function testFindsAtMaxTenResults(): void
    {
        $this->factory->createMultiple(10, [ 'name' => 'Test', 'mainType' => ArticleConstants::TYPE_TOUR ]);
        $this->factory->createMultiple(10, [ 'name' => 'Test', 'mainType' => ArticleConstants::TYPE_ATTRACTION ]);
        $this->factory->createMultiple(10, [ 'name' => 'Test', 'mainType' => ArticleConstants::TYPE_CONGRESS_LOCATION ]);
        $records = $this->subject->findBySearchTerm('Test');
        $this->assertEquals(10, \count($records));
    }

    public function testIncludesArticlesOfMultipleTypesEvenIfMutipleWouldExist(): void
    {
        $this->factory->createMultiple(50, [ 'name' => 'Test', 'mainType' => ArticleConstants::TYPE_TOUR ]);
        $this->factory->createMultiple(50, [ 'name' => 'Test', 'mainType' => ArticleConstants::TYPE_ATTRACTION ]);
        $this->factory->createMultiple(50, [ 'name' => 'Test', 'mainType' => ArticleConstants::TYPE_CONGRESS_LOCATION ]);

        /** @var ArticleRecord[] $records */
        $records = $this->subject->findBySearchTerm('Test');
        $types = [];
        foreach ($records as $record) {
            $type = $record->getArticle()->getMainType();
            if (!in_array($type, $types)) {
                $types[] = $type;
            }
        }

        $this->assertContains(ArticleConstants::TYPE_TOUR, $types);
        $this->assertContains(ArticleConstants::TYPE_ATTRACTION, $types);
        $this->assertContains(ArticleConstants::TYPE_CONGRESS_LOCATION, $types);
    }

}
