<?php declare(strict_types=1);

namespace Newland\Toubiz\Sync\Neos\Tests\Unit\Importer;

use Doctrine\ORM\EntityManagerInterface;
use Neos\Flow\Tests\FunctionalTestCase;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Importer\EventImporter;
use Newland\Toubiz\Sync\Neos\Tests\Factory\AddressFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\CategoryFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\EventDateFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\EventFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\EventTagFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\MediaFactory;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\AddressAdapterMock;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\CategoryAdapterMock;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\EventAdapterMock;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\EventDateAdapterMock;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\EventTagAdapterMock;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\MediaAdapterMock;

class EventImporterTest extends FunctionalTestCase
{
    protected static $testablePersistenceEnabled = true;

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

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

    public function setUp(): void
    {
        parent::setUp();
        $this->subject = $this->objectManager->get(EventImporter::class);
        $this->inject($this->subject, 'packageConfig', [ 'downloadImages' => [ 'events' => false ] ]);
        $this->factory = new EventFactory($this->objectManager);
    }

    public function testImportsEventDates(): void
    {
        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'eventDates' => [
                        new EventDateAdapterMock([ 'externalId' => 'foo' ]),
                        new EventDateAdapterMock([ 'externalId' => 'bar' ]),
                    ],
                ]
            )
        );

        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals(2, $fromDb->getEventDates()->count(), 'Should have two event dates');
    }

    public function testEventDatesAreNotOverriddenEvenIfMultipleLanguagesUseSameExternalId(): void
    {
        $german = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'de',
                    'eventDates' => [ new EventDateAdapterMock([ 'originalId' => '__BAR__' ]), ],
                ]
            )
        );
        $english = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'en',
                    'eventDates' => [ new EventDateAdapterMock([ 'originalId' => '__BAR__' ]), ],
                ]
            )
        );

        $englishFromDb =
            $this->persistenceManager->getObjectByIdentifier($english->getPersistenceObjectIdentifier(), Event::class);
        $germanFromDb =
            $this->persistenceManager->getObjectByIdentifier($german->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals(1, $englishFromDb->getEventDates()->count(), 'English should have one date');
        $this->assertEquals(1, $germanFromDb->getEventDates()->count(), 'German should have one date');
        $this->assertNotEquals(
            $englishFromDb->getEventDates()->first()->getPersistenceObjectIdentifier(),
            $germanFromDb->getEventDates()->first()->getPersistenceObjectIdentifier(),
            'German and english event dates should not be same'
        );
    }

    public function testEventDateModelsAreReusedIfSameExternalIdIsSpecified(): void
    {
        $date = (new EventDateFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'eventDates' => [ $date ] ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'eventDates' => [ new EventDateAdapterMock([ 'externalId' => $date->getOriginalId() ]) ],
                ]
            )
        );

        $this->assertEquals($event->getPersistenceObjectIdentifier(), $imported->getPersistenceObjectIdentifier());
        $this->assertEquals(1, $event->getEventDates()->count(), 'Original event should have one event date');
        $this->assertEquals(1, $imported->getEventDates()->count(), 'Imported event should have one event date');
        $this->assertEquals(
            $date->getPersistenceObjectIdentifier(),
            $imported->getEventDates()->first()->getPersistenceObjectIdentifier(),
            'Event date should have been reused'
        );
    }

    public function testEventDateModelsAreNotReusedIfDifferentExeternalIdSpecified(): void
    {
        $date = (new EventDateFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'eventDate' => $date ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'eventDates' => [ new EventDateAdapterMock([ 'externalId' => '__FOO__' ]) ],
                ]
            )
        );

        $this->assertEquals($event->getPersistenceObjectIdentifier(), $imported->getPersistenceObjectIdentifier());
        $this->assertEquals(1, $event->getEventDates()->count(), 'Original event should have one event date');
        $this->assertEquals(1, $imported->getEventDates()->count(), 'Imported event should have one event date');
        $this->assertNotEquals(
            $date->getPersistenceObjectIdentifier(),
            $imported->getEventDates()->first()->getPersistenceObjectIdentifier(),
            'Event date should not have been reused'
        );
    }

    public function testImportsMedia(): void
    {
        $imported = $this->subject->import(
            new EventAdapterMock(
                [ 'media' => [ new MediaAdapterMock([]), new MediaAdapterMock([]) ] ]
            )
        );

        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals(2, $fromDb->getMedia()->count(), 'Should have two media elements');
    }

    public function testMediasAreNotOverriddenEvenIfMultipleLanguagesUseSameExternalId(): void
    {
        $german = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'de',
                    'media' => [ new MediaAdapterMock([ 'originalId' => '__BAR__' ]), ],
                ]
            )
        );
        $english = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'en',
                    'media' => [ new MediaAdapterMock([ 'originalId' => '__BAR__' ]), ],
                ]
            )
        );

        $englishFromDb =
            $this->persistenceManager->getObjectByIdentifier($english->getPersistenceObjectIdentifier(), Event::class);
        $germanFromDb =
            $this->persistenceManager->getObjectByIdentifier($german->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals(1, $englishFromDb->getMedia()->count(), 'English should have one media element');
        $this->assertEquals(1, $germanFromDb->getMedia()->count(), 'German should have one media element');
        $this->assertNotEquals(
            $englishFromDb->getMedia()->first()->getPersistenceObjectIdentifier(),
            $germanFromDb->getMedia()->first()->getPersistenceObjectIdentifier(),
            'German and english media elements should not be same'
        );
    }

    public function testMediaModelsAreReusedIfSameExternalIdIsSpecified(): void
    {
        $media = (new MediaFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'media' => [ $media ] ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'media' => [ new MediaAdapterMock([ 'externalId' => $media->getOriginalId() ]) ],
                ]
            )
        );

        $this->assertEquals($event->getPersistenceObjectIdentifier(), $imported->getPersistenceObjectIdentifier());
        $this->assertEquals(1, $event->getMedia()->count(), 'Original event should have one media item');
        $this->assertEquals(1, $imported->getMedia()->count(), 'Imported event should have one media item');
        $this->assertEquals(
            $event->getMedia()->first()->getPersistenceObjectIdentifier(),
            $imported->getMedia()->first()->getPersistenceObjectIdentifier(),
            'media should have been reused'
        );
    }

    public function testMediaModelsAreNotReusedIfDifferentExeternalIdSpecified(): void
    {
        $media = (new MediaFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'media' => [ $media ] ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'media' => [ new MediaAdapterMock([ 'externalId' => '__FOO__' ]) ],
                ]
            )
        );

        $this->assertEquals($event->getPersistenceObjectIdentifier(), $imported->getPersistenceObjectIdentifier());
        $this->assertEquals(1, $event->getMedia()->count(), 'Original event should have one media item');
        $this->assertEquals(1, $imported->getMedia()->count(), 'Imported event should have one media item');
        $this->assertNotEquals(
            $media->getPersistenceObjectIdentifier(),
            $imported->getMedia()->first()->getPersistenceObjectIdentifier(),
            'media item date should not have been reused'
        );
    }

    public function testImportsEventTags(): void
    {
        $imported = $this->subject->import(
            new EventAdapterMock(
                [ 'eventTags' => [ new EventTagAdapterMock([]), new EventTagAdapterMock([]) ] ]
            )
        );

        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals(2, $fromDb->getEventTags()->count(), 'Should have two event tags');
    }

    public function testEventTagsAreNotOverriddenEvenIfMultipleLanguagesUseSameExternalId(): void
    {
        $german = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'de',
                    'eventTags' => [ new EventTagAdapterMock([ 'originalId' => '__BAR__' ]), ],
                ]
            )
        );
        $english = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'en',
                    'eventTags' => [ new EventTagAdapterMock([ 'originalId' => '__BAR__' ]), ],
                ]
            )
        );

        $englishFromDb =
            $this->persistenceManager->getObjectByIdentifier($english->getPersistenceObjectIdentifier(), Event::class);
        $germanFromDb =
            $this->persistenceManager->getObjectByIdentifier($german->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals(1, $englishFromDb->getEventTags()->count(), 'English should have one tag');
        $this->assertEquals(1, $germanFromDb->getEventTags()->count(), 'German should have one tag');
        $this->assertNotEquals(
            $englishFromDb->getEventTags()->first()->getPersistenceObjectIdentifier(),
            $germanFromDb->getEventTags()->first()->getPersistenceObjectIdentifier(),
            'German and english tags should not be same'
        );
    }

    public function testEventTagModelsAreReusedIfSameExternalIdIsSpecified(): void
    {
        $tag = (new EventTagFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'eventTags' => [ $tag ] ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'eventTags' => [ new EventTagAdapterMock([ 'externalId' => $tag->getOriginalId() ]) ],
                ]
            )
        );

        $this->assertEquals($event->getPersistenceObjectIdentifier(), $imported->getPersistenceObjectIdentifier());
        $this->assertEquals(1, $event->getEventTags()->count(), 'Original event should have one tag');
        $this->assertEquals(1, $imported->getEventTags()->count(), 'Imported event should have one tag');
        $this->assertEquals(
            $event->getEventTags()->first()->getPersistenceObjectIdentifier(),
            $imported->getEventTags()->first()->getPersistenceObjectIdentifier(),
            'tag should have been reused'
        );
    }

    public function testEventTagModelsAreNotReusedIfDifferentExeternalIdSpecified(): void
    {
        $tag = (new EventTagFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'eventTags' => [ $tag ] ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'eventTags' => [ new EventTagAdapterMock([ 'externalId' => '__FOO__' ]) ],
                ]
            )
        );

        $this->assertEquals($event->getPersistenceObjectIdentifier(), $imported->getPersistenceObjectIdentifier());
        $this->assertEquals(1, $event->getEventTags()->count(), 'Original event should have one taga');
        $this->assertEquals(1, $imported->getEventTags()->count(), 'Imported event should have one taga');
        $this->assertNotEquals(
            $tag->getPersistenceObjectIdentifier(),
            $imported->getEventTags()->first()->getPersistenceObjectIdentifier(),
            'tag date should not have been reused'
        );
    }


    public function testImportsOrganizer(): void
    {
        $imported = $this->subject->import(
            new EventAdapterMock(
                [ 'organizer' => new AddressAdapterMock([]) ]
            )
        );

        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertNotNull($fromDb->getOrganizer(), 'Event should have organizer');
    }

    public function testOrganizerIsNotOverriddenEvenIfMultipleLanguagesUseSameExternalId(): void
    {
        $german = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'de',
                    'organizer' => new AddressAdapterMock([ 'originalId' => '__BAR__' ]),
                ]
            )
        );
        $english = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'en',
                    'organizer' => new AddressAdapterMock([ 'originalId' => '__BAR__' ]),
                ]
            )
        );

        $englishFromDb =
            $this->persistenceManager->getObjectByIdentifier($english->getPersistenceObjectIdentifier(), Event::class);
        $germanFromDb =
            $this->persistenceManager->getObjectByIdentifier($german->getPersistenceObjectIdentifier(), Event::class);
        $this->assertNotNull($englishFromDb->getOrganizer(), 'English should have organizer');
        $this->assertNotNull($germanFromDb->getOrganizer(), 'German should have organizer');
        $this->assertEquals(
            $englishFromDb->getOrganizer()->getPersistenceObjectIdentifier(),
            $germanFromDb->getOrganizer()->getPersistenceObjectIdentifier(),
            'German and english organizer should be same'
        );
    }

    public function testOrganizerIsReused(): void
    {
        $address = (new AddressFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'organizer' => $address ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'organizer' => new AddressAdapterMock(
                        [
                            'title' => 'foo bar test',
                        ]
                    ),
                ]
            )
        );

        $this->assertEquals($imported->getPersistenceObjectIdentifier(), $event->getPersistenceObjectIdentifier());
        $this->assertNotNull($event->getOrganizer(), 'Original event should have organizer');
        $this->assertNotNull($imported->getOrganizer(), 'Imported event should have organizer');
        $this->assertEquals(
            $imported->getOrganizer()->getPersistenceObjectIdentifier(),
            $event->getOrganizer()->getPersistenceObjectIdentifier(),
            'Organizer model should have been reused'
        );
    }

    public function testImportsLocation(): void
    {
        $imported = $this->subject->import(
            new EventAdapterMock(
                [ 'location' => new AddressAdapterMock([]) ]
            )
        );

        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertNotNull($fromDb->getLocation(), 'Event should have location');
    }

    public function testLocationIsNotOverriddenEvenIfMultipleLanguagesUseSameExternalId(): void
    {
        $german = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'de',
                    'location' => new AddressAdapterMock([ 'originalId' => '__BAR__' ]),
                ]
            )
        );
        $english = $this->subject->import(
            new EventAdapterMock(
                [
                    'originalId' => '__FOO__',
                    'language' => 'en',
                    'location' => new AddressAdapterMock([ 'originalId' => '__BAR__' ]),
                ]
            )
        );

        $englishFromDb =
            $this->persistenceManager->getObjectByIdentifier($english->getPersistenceObjectIdentifier(), Event::class);
        $germanFromDb =
            $this->persistenceManager->getObjectByIdentifier($german->getPersistenceObjectIdentifier(), Event::class);

        $this->assertNotNull($englishFromDb->getLocation(), 'English should have location');
        $this->assertNotNull($germanFromDb->getLocation(), 'German should have location');
        $this->assertEquals(
            $englishFromDb->getLocation()->getPersistenceObjectIdentifier(),
            $germanFromDb->getLocation()->getPersistenceObjectIdentifier(),
            'German and english location should be same'
        );
    }

    public function testLocationIsReused(): void
    {
        $address = (new AddressFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'location' => $address ]);

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                    'location' => new AddressAdapterMock(
                        [
                            'title' => 'foo bar test',
                        ]
                    ),
                ]
            )
        );

        $this->assertEquals($imported->getPersistenceObjectIdentifier(), $event->getPersistenceObjectIdentifier());
        $this->assertNotNull($event->getLocation(), 'Original event should have location');
        $this->assertNotNull($imported->getLocation(), 'Imported should have location');
        $this->assertEquals(
            $imported->getLocation()->getPersistenceObjectIdentifier(),
            $event->getLocation()->getPersistenceObjectIdentifier(),
            'Model should have been reused'
        );
    }

    public function testSetsUpdatedAt(): void
    {
        $imported =
            $this->subject->import(new EventAdapterMock([ 'updatedAt' => new \DateTime('2019-01-01T15:55:00') ]));
        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertInstanceOf(\DateTime::class, $fromDb->getUpdatedAt());
        $this->assertEquals('2019-01-01T15:55:00', $fromDb->getUpdatedAt()->format('Y-m-d\TH:i:s'));
    }

    public function testSavesAttributes(): void
    {
        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'attributes' => [ 'foo', 'bar', 'baz' ],
                ]
            )
        );
        $fromDb =
            $this->persistenceManager->getObjectByIdentifier($imported->getPersistenceObjectIdentifier(), Event::class);
        $this->assertEquals([ 'foo', 'bar', 'baz' ], $fromDb->getAttributes());
    }

    public function testTryingToImportExistingCategoryOnEventDoesNotTryToDuplicateIt(): void
    {
        $category = (new CategoryFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'categories' => [ $category ] ]);

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $category) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_event_categories_join WHERE neos_event = ? AND neos_category = ?',
                [ $event->getPersistenceObjectIdentifier(), $category->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'categories' => [
                        new CategoryAdapterMock(
                            [ 'externalId' => $category->getOriginalId(), 'name' => $category->getTitle() ]
                        ),
                        new CategoryAdapterMock(
                            [ 'externalId' => $category->getOriginalId(), 'name' => $category->getTitle() ]
                        ),
                    ],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(1, $countRelationRecords());
    }

    public function testDisassociatesCategoryFromEventIfNotInData(): void
    {
        $category = (new CategoryFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'categories' => [ $category ] ]);

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $category) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_event_categories_join WHERE neos_event = ? AND neos_category = ?',
                [ $event->getPersistenceObjectIdentifier(), $category->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'categories' => [],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(0, $countRelationRecords());
    }


    public function testTryingToImportExistingEventDateOnEventDoesNotTryToDuplicateIt(): void
    {
        $event = $this->factory->create();
        $date = (new EventDateFactory($this->objectManager))->create();
        $date->setEvent($event);
        $event->getEventDates()->add($date);
        $this->persistenceManager->update($date);
        $this->persistenceManager->persistAll();

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $date) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_eventdate WHERE event = ? AND persistence_object_identifier = ?',
                [ $event->getPersistenceObjectIdentifier(), $date->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'eventDates' => [
                        new EventDateAdapterMock([ 'externalId' => $date->getOriginalId() ]),
                        new EventDateAdapterMock([ 'externalId' => $date->getOriginalId() ]),
                    ],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(1, $countRelationRecords());
    }

    public function testDisassociatesDateFromEventIfNotInData(): void
    {
        $event = $this->factory->create();
        $date = (new EventDateFactory($this->objectManager))->create();
        $date->setEvent($event);
        $event->getEventDates()->add($date);
        $this->persistenceManager->update($date);
        $this->persistenceManager->persistAll();

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $date) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_eventdate WHERE event = ? AND persistence_object_identifier = ?',
                [ $event->getPersistenceObjectIdentifier(), $date->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'eventDates' => [],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(0, $countRelationRecords());
    }


    public function testTryingToImportExistingEventTagOnEventDoesNotTryToDuplicateIt(): void
    {
        $tag = (new EventTagFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'eventTags' => [ $tag ] ]);

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $tag) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_event_eventtags_join WHERE neos_event = ? AND neos_eventtag = ?',
                [ $event->getPersistenceObjectIdentifier(), $tag->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'eventTags' => [
                        new EventTagAdapterMock(
                            [ 'externalId' => $tag->getOriginalId(), 'keywordTypeId' => $tag->getOriginalId() ]
                        ),
                        new EventTagAdapterMock(
                            [ 'externalId' => $tag->getOriginalId(), 'keywordTypeId' => $tag->getOriginalId() ]
                        ),
                    ],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(1, $countRelationRecords());
    }

    public function testDisassociatesTagFromEventIfNotInData(): void
    {
        $tag = (new EventTagFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'eventTags' => [ $tag ] ]);

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $tag) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_event_eventtags_join WHERE neos_event = ? AND neos_eventtag = ?',
                [ $event->getPersistenceObjectIdentifier(), $tag->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'eventTags' => [],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(0, $countRelationRecords());
    }

    public function testTryingToImportExistingMediaOnEventDoesNotTryToDuplicateIt(): void
    {
        $media = (new MediaFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'media' => [ $media ] ]);

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $media) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_event_media_join WHERE neos_event = ? AND neos_medium = ?',
                [ $event->getPersistenceObjectIdentifier(), $media->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'media' => [
                        new MediaAdapterMock([ 'externalId' => $media->getOriginalId() ]),
                        new MediaAdapterMock([ 'externalId' => $media->getOriginalId() ]),
                    ],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(1, $countRelationRecords());
    }

    public function testDisassociatesMediaFromEventIfNotInData(): void
    {
        $media = (new MediaFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'media' => [ $media ] ]);

        /** @var EntityManagerInterface $em */
        $em = $this->objectManager->get(EntityManagerInterface::class);
        $countRelationRecords = function () use ($em, $event, $media) {
            return (int) $em->getConnection()->executeQuery(
                'SELECT COUNT(*) FROM newland_toubiz_sync_neos_domain_model_event_media_join WHERE neos_event = ? AND neos_medium = ?',
                [ $event->getPersistenceObjectIdentifier(), $media->getPersistenceObjectIdentifier() ]
            )->fetchColumn();
        };

        $this->assertEquals(1, $countRelationRecords());
        $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'media' => [],
                ]
            )
        );
        $em->flush();
        $this->assertEquals(0, $countRelationRecords());
    }


    public function testMediaModelIsReused(): void
    {
        $media = (new MediaFactory($this->objectManager))->create();
        $event = $this->factory->create([ 'media' => [ $media ] ]);
        $mediaId = $media->getPersistenceObjectIdentifier();

        $imported = $this->subject->import(
            new EventAdapterMock(
                [
                    'externalId' => $event->getOriginalId(),
                    'media' => [
                        new MediaAdapterMock([ 'externalId' => $media->getOriginalId() ]),
                    ],
                ]
            )
        );

        $this->assertEquals(
            $mediaId,
            $imported->getMedia()->first()->getPersistenceObjectIdentifier()
        );
    }

    public function testMapsAdditionalSearchString(): void
    {
        $imported = $this->subject->import(new EventAdapterMock([
            'externalId' => '12345',
            'additionalSearchString' => 'foo bar',
        ]));

        $this->assertEquals('foo bar', $imported->getAdditionalSearchString());
    }

    public function testMapsMissingAdditionalSearchString(): void
    {
        $imported = $this->subject->import(new EventAdapterMock([
            'externalId' => '12345',
            'additionalSearchString' => null,
        ]));

        $this->assertEquals('', $imported->getAdditionalSearchString());
    }

}
