<?php declare(strict_types=1);

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

use Neos\Flow\Tests\FunctionalTestCase;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Domain\Model\EventTag;
use Newland\Toubiz\Sync\Neos\Importer\EventImporter;
use Newland\Toubiz\Sync\Neos\Tests\Factory\AddressFactory;
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\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->factory = new EventFactory($this->objectManager);
    }

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

        $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()[0]->getPersistenceObjectIdentifier(),
            $germanFromDb->getEventDates()[0]->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()[0]->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()[0]->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()[0]->getPersistenceObjectIdentifier(),
            $germanFromDb->getMedia()[0]->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()[0]->getPersistenceObjectIdentifier(),
            $imported->getMedia()[0]->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()[0]->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()[0]->getPersistenceObjectIdentifier(),
            $germanFromDb->getEventTags()[0]->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()[0]->getPersistenceObjectIdentifier(),
            $imported->getEventTags()[0]->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()[0]->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->assertNotEquals(
            $englishFromDb->getOrganizer()->getPersistenceObjectIdentifier(),
            $germanFromDb->getOrganizer()->getPersistenceObjectIdentifier(),
            'German and english organizer should not 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->assertNotEquals(
            $englishFromDb->getLocation()->getPersistenceObjectIdentifier(),
            $germanFromDb->getLocation()->getPersistenceObjectIdentifier(),
            'German and english location should not 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());
    }

}
