<?php declare(strict_types=1);

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

use Doctrine\ORM\EntityManagerInterface;
use Neos\Flow\Http\Uri;
use Neos\Neos\Service\LinkingService;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Newland\Toubiz\Api\ObjectAdapter\Concern\EventConstants;
use Newland\Toubiz\Events\Neos\ConfigurationProvider;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Domain\Model\EventDate;
use Newland\Toubiz\Sync\Neos\Domain\Repository\EventDateRepository;
use Newland\Toubiz\Sync\Neos\Domain\Repository\RecordConfigurationRepository;

class IndexListTest extends IntegrationTestCase
{
    public function setUp(): void
    {
        parent::setUp();
        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Events.Neos:List'));
        $this->persistNode($this->node);
        $this->nodeService->createChildNodes($this->node);
        $this->persistenceManager->persistAll();
    }

    public function testDoesNotThrowError(): void
    {
        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);
    }

    public function testOnlyDisplaysEventsThatHaveDatesInTheFuture(): void
    {
        $this->eventFactory->create([ 'title' => 'NO_DATES' ]);

        $tomorrow = (new \DateTime())->add(new \DateInterval('P1D'));
        $this->createEventWithDate([ 'title' => 'DATE_TOMORROW' ], [
            'beginsAt' => $tomorrow,
            'endsAt' => (clone $tomorrow)->add(new \DateInterval('PT1H')),
        ]);

        $yesterday = (new \DateTime())->sub(new \DateInterval('P1D'));
        $this->createEventWithDate([ 'title' => 'DATE_YESTERDAY' ], [
            'beginsAt' => $yesterday,
            'endsAt' => (clone $yesterday)->add(new \DateInterval('PT1H')),
        ]);

        $dayAfterTomorrow = (new \DateTime())->add(new \DateInterval('P2D'));
        $this->createEventWithDate([ 'title' => 'DATE_DAY_AFTER_TOMORROW' ], [
            'beginsAt' => $dayAfterTomorrow,
            'endsAt' => (clone $dayAfterTomorrow)->add(new \DateInterval('PT1H')),
        ]);

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);

        $this->assertResponseContains('DATE_TOMORROW', $response);
        $this->assertResponseContains('DATE_DAY_AFTER_TOMORROW', $response);
        $this->assertResponseNotContains('NO_DATES', $response);
        $this->assertResponseNotContains('DATE_YESTERDAY', $response);
    }


    public function testOnlyDisplaysNodesOfCertainScope(): void
    {
        $tomorrow = (new \DateTime())->add(new \DateInterval('P1D'));
        $dateProperties = [
            'beginsAt' => $tomorrow,
            'endsAt' => (clone $tomorrow)->add(new \DateInterval('PT1H')),
        ];

        $this->createEventWithDate(
            [ 'scope' => EventConstants::SCOPE_LOCAL, 'title' => 'SCOPE_LOCAL' ],
            $dateProperties
        );
        $this->createEventWithDate(
            [ 'scope' => EventConstants::SCOPE_REGIONAL, 'title' => 'SCOPE_REGIONAL' ],
            $dateProperties
        );
        $this->createEventWithDate(
            [ 'scope' => EventConstants::SCOPE_TRANSREGIONAL, 'title' => 'SCOPE_TRANSREGIONAL' ],
            $dateProperties
        );
        $this->createEventWithDate(
            [ 'scope' => EventConstants::SCOPE_UNDEFINED, 'title' => 'SCOPE_UNDEFINED' ],
            $dateProperties
        );


        $this->node->setProperty('eventScope', [ EventConstants::SCOPE_LOCAL ]);
        $this->persistNode($this->node);

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);


        $this->assertResponseContains('SCOPE_LOCAL', $response);
        $this->assertResponseNotContains('SCOPE_REGIONAL', $response);
        $this->assertResponseNotContains('SCOPE_TRANSREGIONAL', $response);
        $this->assertResponseNotContains('SCOPE_UNDEFINED', $response);
    }

    public function testFindsEventDatesInSpecificDateRange(): void
    {
        $first = md5(random_bytes(32)) . '__first';
        $second = md5(random_bytes(32)) . '__second';
        $third = md5(random_bytes(32)) . '__third';
        $this->createEventWithDate([ 'title' => $first ], [
            'beginsAt' => new \DateTime('2044-01-01T15:00'),
            'endsAt' => new \DateTime('2044-01-01T19:00')
        ]);
        $this->createEventWithDate([ 'title' => $second ], [
            'beginsAt' => new \DateTime('2044-01-08T15:00'),
            'endsAt' => new \DateTime('2044-01-08T19:00')
        ]);
        $this->createEventWithDate([ 'title' => $third ], [
            'beginsAt' => new \DateTime('2044-01-10T00:00'),
            'endsAt' => new \DateTime('2044-01-10T23:59')
        ]);

        $response = $this->browser->request(
            $this->nodeUri([ 'date[from]' => '2044-01-05', 'date[to]' => '2044-01-09' ])
        );
        $this->assertResponseOk($response);
        $this->assertResponseNotContains($first, $response);
        $this->assertResponseContains($second, $response);
        $this->assertResponseNotContains($third, $response);


        $response = $this->browser->request(
            $this->nodeUri([ 'date[from]' => '2044-01-07', 'date[to]' => '2044-01-14' ])
        );
        $this->assertResponseOk($response);
        $this->assertResponseNotContains($first, $response);
        $this->assertResponseContains($second, $response);
        $this->assertResponseContains($third, $response);
    }

    public function testRendersNoTimeIfThereIsNoSpecificTime(): void
    {
        $event = $this->eventFactory->create();
        $this->eventDateFactory->create([
            'event' => $event,
            'beginsAt' => new \DateTime('2055-01-01T00:00'),
            'beginsAtSpecificTime' => false,
            'endsAt' => new \DateTime('2055-01-01T23:59'),
            'endsAtSpecificTime' => false,
        ]);

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);

        $body = preg_replace('/\s+/', ' ', (string) $response->getBody());
        $this->assertStringNotContainsString('00:00', $body);
        $this->assertStringNotContainsString('23:59', $body);
    }

    public function testRendersNoEndTimeIfItIsNotSpecific(): void
    {
        $event = $this->eventFactory->create();
        $this->eventDateFactory->create([
            'event' => $event,
            'beginsAt' => new \DateTime('2055-01-01T14:00'),
            'beginsAtSpecificTime' => true,
            'endsAt' => new \DateTime('2055-01-01T23:59'),
            'endsAtSpecificTime' => false,
        ]);

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);

        $body = preg_replace('/\s+/', ' ', (string) $response->getBody());
        $this->assertStringContainsString('14:00', $body);
        $this->assertStringNotContainsString('23:59', $body);
    }

    public function testDoesNotRenderAnyTimeIfStartTimeIsNotSpecific(): void
    {
        $event = $this->eventFactory->create();
        $this->eventDateFactory->create([
            'event' => $event,
            'beginsAt' => new \DateTime('2055-01-01T00:00'),
            'beginsAtSpecificTime' => false,
            'endsAt' => new \DateTime('2055-01-01T15:00'),
            'endsAtSpecificTime' => true,
        ]);

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);

        $body = preg_replace('/\s+/', ' ', (string) $response->getBody());
        $this->assertStringNotContainsString('00:00', $body);
        $this->assertStringNotContainsString('15:00', $body);
    }

    public function testFindsDatesInSpecificDateRangeIfTheyDoNotBeginAtSpecificTime(): void
    {
        $first = md5(random_bytes(32)) . '__first';
        $second = md5(random_bytes(32)) . '__second';
        $third = md5(random_bytes(32)) . '__third';
        $this->createEventWithDate([ 'title' => $first ], [
            'beginsAt' => new \DateTime('2044-01-01T00:00'),
            'beginsAtSpecificTime' => false,
            'endsAt' => new \DateTime('2044-01-01T19:00')
        ]);
        $this->createEventWithDate([ 'title' => $second ], [
            'beginsAt' => new \DateTime('2044-01-08T00:00'),
            'beginsAtSpecificTime' => false,
            'endsAt' => new \DateTime('2044-01-08T19:00')
        ]);
        $this->createEventWithDate([ 'title' => $third ], [
            'beginsAt' => new \DateTime('2044-01-10T00:00'),
            'beginsAtSpecificTime' => false,
            'endsAt' => new \DateTime('2044-01-10T19:00')
        ]);

        $response = $this->browser->request(
            $this->nodeUri([ 'date[from]' => '2044-01-05', 'date[to]' => '2044-01-09' ])
        );
        $this->assertResponseOk($response);
        $this->assertResponseNotContains($first, $response);
        $this->assertResponseContains($second, $response);
        $this->assertResponseNotContains($third, $response);


        $response = $this->browser->request(
            $this->nodeUri([ 'date[from]' => '2044-01-07', 'date[to]' => '2044-01-14' ])
        );
        $this->assertResponseOk($response);
        $this->assertResponseNotContains($first, $response);
        $this->assertResponseContains($second, $response);
        $this->assertResponseContains($third, $response);
    }

    public function testDoesNotListHiddenEntries(): void
    {
        $title =  md5(random_bytes(32));
        /** @var Event $event */
        [ $event ] = $this->createEventWithDate([ 'title' => $title ], [
            'beginsAt' => new \DateTime('2044-01-01T12:00'),
            'endsAt' => new \DateTime('2044-01-01T19:00')
        ]);
        $this->persistenceManager->clearState();
        $event = $this->persistenceManager->getObjectByIdentifier($event->getPersistenceObjectIdentifier(), Event::class);

        $this->objectManager->get(RecordConfigurationRepository::class)->updateConfiguration(
            $event,
            function() { return [ 'hidden' => true ]; }
        );
        $this->persistenceManager->persistAll();

        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);
        $this->assertResponseNotContains($title, $response);
    }

    public function testDoesNotDisplayFreshlyHiddenEvent(): void
    {
        $title =  md5(random_bytes(32));
        [ $event ] = $this->createEventWithDate([ 'title' => $title ], [
            'beginsAt' => new \DateTime('2044-01-01T12:00'),
            'endsAt' => new \DateTime('2044-01-01T19:00')
        ]);

        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);
        $this->assertResponseContains($title, $response);

        // Hiding through the configuration provider as though someone did it in the backend.
        $event = $this->persistenceManager->getObjectByIdentifier($event->getPersistenceObjectIdentifier(), Event::class);
        $provider = $this->objectManager->get(ConfigurationProvider::class);
        $this->objectManager->get(RecordConfigurationRepository::class)->updateConfiguration(
            $event,
            [ $provider->actions($event)['hide'], 'invoke' ]
        );
        $this->persistenceManager->update($event);
        $this->persistenceManager->persistAll();

        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);
        $this->assertResponseNotContains($title, $response);
    }

    public function testRendersSlots(): void
    {
        $this->createEventWithDate([  ], [
            'beginsAt' => new \DateTime('2044-01-01T12:00'),
            'endsAt' => new \DateTime('2044-01-01T19:00')
        ]);

        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);

        foreach ($this->provideSlotsThatShouldExist() as [ $slot ]) {
            $this->assertResponseContains(sprintf('Slot(%s)', $slot), $response, null, false);
        }
    }

    public function provideSlotsThatShouldExist(): array
    {
        return [
            [ 'aboveAll' ],
            [ 'belowAll' ],
            [ 'aboveTitle' ],
            [ 'belowTitle' ],
            [ 'aboveList' ],
            [ 'belowList' ],
            [ 'aboveListResults' ],
            [ 'belowListResults' ],
            [ 'aboveListHighlights' ],
            [ 'belowListHighlights' ],
            [ 'aboveListToday' ],
            [ 'belowListToday' ],
            [ 'aboveListItem' ],
            [ 'belowListItem' ],
            [ 'aboveListItemTitle' ],
            [ 'belowListItemTitle' ],
        ];
    }

    // TODO Add test for preselectedRegions
    // TODO Add test for topics
    // TODO Add test for preselectedTopics
}
