<?php declare(strict_types=1);

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

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Neos\Flow\Tests\FunctionalTestCase;
use Newland\NeosTestingHelpers\ChecksEntities;
use Newland\Toubiz\Sync\Neos\Command\CategoryCommandController;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Domain\Model\Category;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Domain\Model\RelatedLists\ArticleList;
use Newland\Toubiz\Sync\Neos\Domain\Model\RelatedLists\RelatedLists;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Newland\Toubiz\Sync\Neos\Domain\Repository\CategoryRepository;
use Newland\Toubiz\Sync\Neos\Tests\Factory\ArticleFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\CategoryFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\EventFactory;

class CategoryCommandControllerTest extends FunctionalTestCase
{
    use ChecksEntities;
    protected static $testablePersistenceEnabled = true;

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

    /** @var CategoryRepository */
    private $categoryRepository;

    /** @var CategoryFactory */
    private $categoryFactory;

    /** @var ArticleFactory */
    private $articleFactory;

    /** @var EventFactory */
    private $eventFactory;

    public function setUp(): void
    {
        parent::setUp();
        $this->subject = $this->objectManager->get(CategoryCommandController::class);
        $this->categoryRepository = $this->objectManager->get(CategoryRepository::class);
        $this->articleFactory = new ArticleFactory($this->objectManager);
        $this->categoryFactory = new CategoryFactory($this->objectManager);
        $this->eventFactory = new EventFactory($this->objectManager);
    }

    public function testDeletesCategoriesNotRelatedToArticlesOrEvents()
    {
        [ $onlyRelatedToArticles, $onlyRelatedToEvents, $relatedToBoth, $relatedToNone ] = $this->categoryFactory->createMultiple(4);
        $onlyRelatedToArticlesId = $onlyRelatedToArticles->getPersistenceObjectIdentifier();
        $onlyRelatedToEventsId = $onlyRelatedToEvents->getPersistenceObjectIdentifier();
        $relatedToBothId = $relatedToBoth->getPersistenceObjectIdentifier();
        $relatedToNoneId = $relatedToNone->getPersistenceObjectIdentifier();

        /** @var Article $article */
        $article = $this->articleFactory->create();
        $article->setCategories(new ArrayCollection([ $onlyRelatedToArticles, $relatedToBoth ]));

        /** @var Event $event */
        $event = $this->eventFactory->create();
        $event->setCategories(new ArrayCollection([ $onlyRelatedToEvents, $relatedToBoth ]));
        $this->flushToDatabaseAndClearCaches([ $onlyRelatedToEvents, $onlyRelatedToArticles, $relatedToBoth, $event, $article ]);

        $this->callRemoveUnusedCommand();
        $this->flushToDatabaseAndClearCaches([ ]);

        $this->assertEntityExists(Category::class, $onlyRelatedToArticlesId, 'Category with relations to article should not be deleted');
        $this->assertEntityExists(Category::class, $onlyRelatedToEventsId, 'Category with relations to event should not be deleted');
        $this->assertEntityExists(Category::class, $relatedToBothId, 'Category with relations to article & events should not be deleted');
        $this->assertEntityNotExists(Category::class, $relatedToNoneId, 'Category with relations to neither articles nor events should be deleted');
    }

    public function testDeletesCategoriesOfAllLanguages(): void
    {
        [ $usedGerman, $unusedGerman ] = $this->categoryFactory->createMultiple(2, [ 'language' => 'de' ]);
        [ $usedEnglish, $unusedEnglish ] = $this->categoryFactory->createMultiple(2, [ 'language' => 'en' ]);

        $this->articleFactory->create([ 'language' => 'de', 'categories' => new ArrayCollection([ $usedGerman ]) ]);
        $this->eventFactory->create([ 'language' => 'en', 'categories' => new ArrayCollection([ $usedEnglish ]) ]);
        $this->flushToDatabaseAndClearCaches();

        $this->callRemoveUnusedCommand();

        $this->categoryRepository->setLanguage('de');
        $this->assertIsNotNullWithoutLengthyErrorMessage(
            $this->categoryRepository->findByIdentifier($usedGerman->getPersistenceObjectIdentifier()),
            'Used german category should not be deleted.'
        );
        $this->assertIsNullWithoutLengthyErrorMessage(
            $this->categoryRepository->findByIdentifier($unusedGerman->getPersistenceObjectIdentifier()),
            'Unused german category should be deleted.'
        );

        $this->categoryRepository->setLanguage('en');
        $this->assertIsNotNullWithoutLengthyErrorMessage(
            $this->categoryRepository->findByIdentifier($usedEnglish->getPersistenceObjectIdentifier()),
            'Used english category should not be deleted.'
        );
        $this->assertIsNullWithoutLengthyErrorMessage(
            $this->categoryRepository->findByIdentifier($unusedEnglish->getPersistenceObjectIdentifier()),
            'Unused english category should be deleted'
        );
    }

    private function callRemoveUnusedCommand(): void
    {
        $this->subject->removeUnusedCommand(true);
        $this->flushToDatabaseAndClearCaches();
    }

    private function flushToDatabaseAndClearCaches($entitiesToUpdate = []): void
    {
        foreach ($entitiesToUpdate as $entity) {
            $this->persistenceManager->update($entity);
        }
        $this->persistenceManager->persistAll();
        $this->persistenceManager->clearState();
    }
}
