<?php declare(strict_types=1);

namespace Newland\Toubiz\Events\Tests\Unit\Filter;

use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Tests\FunctionalTestCase;
use Neos\Flow\Utility\Now;
use Newland\NeosTestingHelpers\InteractsWithNodes;
use Newland\Toubiz\Events\Neos\Filter\EventDateFilterFactory;
use Newland\Toubiz\Events\Neos\Filter\Items\EventDateRoot;
use Newland\Toubiz\Sync\Neos\Domain\Model\EventDate;
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;

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

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

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

        $settings = $this->objectManager->get(ConfigurationManager::class)
            ->getConfiguration(
                ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
                'Newland.Toubiz.Events.Neos.filter'
            );
        $this->subject = (new EventDateFilterFactory(
            $this->initializeNode('/sites/foo/bar')
        ))->createEventFilter($settings);
    }

    // See TBEVENTS-133
    public function testFindsEventsThatStartedEarlierToday(): void
    {
        // Event is at 14:00 but it is 14:30 right now.
        $this->inject($this->subject, 'now', new Now('2019-02-02T14:30'));
        $date = $this->createEvent('2019-02-02T14:00', '2019-02-02T15:00');

        /** @var EventDate[] $dates */
        $dates = $this->subject->getEventQuery()->getQuery()->execute();
        $ids = array_map(function(EventDate $date) { return $date->getPersistenceObjectIdentifier(); }, $dates);

        $this->assertContains($date->getPersistenceObjectIdentifier(), $ids, 'Event that begins at 14:00 should be found if currently it is 14:30');
    }

    public function testDoesNotFindEventsThatWereTodayButHaveAlreadyFinished(): void
    {
        // Event ends at 12:00 but it is 14:30 right now.
        $this->inject($this->subject, 'now', new Now('2019-02-02T14:30'));
        $date = $this->createEvent('2019-02-02T10:00', '2019-02-02T12:00');

        /** @var EventDate[] $dates */
        $dates = $this->subject->getEventQuery()->getQuery()->execute();
        $ids = array_map(function(EventDate $date) { return $date->getPersistenceObjectIdentifier(); }, $dates);

        $this->assertNotContains($date->getPersistenceObjectIdentifier(), $ids, 'Event that ends at 12:00 should be found if currently it is 14:30');
    }

    // See TBEVENTS-133
    public function testFindsFullDayEventsToday(): void
    {
        $this->inject($this->subject, 'now', new Now('2019-02-02T14:30'));
        $date1 = $this->createEvent('2019-02-02T00:00', '2019-02-02T00:00');
        $date2 = $this->createEvent('2019-02-02T00:00', '2019-02-02T23:59');
        $date3 = $this->createEvent('2019-02-02T00:00', '2019-02-03T00:00');

        /** @var EventDate[] $dates */
        $dates = $this->subject->getEventQuery()->getQuery()->execute();
        $ids = array_map(function(EventDate $date) { return $date->getPersistenceObjectIdentifier(); }, $dates);

        $this->assertContains($date1->getPersistenceObjectIdentifier(), $ids, 'Full day event (00:00-00:00 same day) should be found');
        $this->assertContains($date2->getPersistenceObjectIdentifier(), $ids, 'Full day event (00:00-23:59 same day) should be found');
        $this->assertContains($date3->getPersistenceObjectIdentifier(), $ids, 'Full day event (00:00-00:00 next day) should be found');
    }

    public function testDoesNotFindEventsThatAreYesterday(): void
    {
        // Today is 2019-02-02 but event was on 2019-02-01
        $this->inject($this->subject, 'now', new Now('2019-02-02T14:30'));
        $date = $this->createEvent('2019-02-01T14:00', '2019-02-01T15:00');

        /** @var EventDate[] $dates */
        $dates = $this->subject->getEventQuery()->getQuery()->execute();
        $ids = array_map(function(EventDate $date) { return $date->getPersistenceObjectIdentifier(); }, $dates);

        $this->assertNotContains($date->getPersistenceObjectIdentifier(), $ids);
    }

    public function testCountsFilteredArticles(): void
    {
        $this->inject($this->subject, 'now', new Now('2017-02-02T14:30'));
        $foo = (new EventTagFactory($this->objectManager))->create([ 'keyword' => 'foo' ]);
        $bar = (new EventTagFactory($this->objectManager))->create([ 'keyword' => 'bar' ]);

        // 1 with foo
        // 2 with bar
        // 3 with both
        $this->createEvent('2019-02-02T00:00', '2019-02-02T00:00', [ 'eventTags' => [ $foo ] ]);
        $this->createEvent('2019-02-02T00:00', '2019-02-02T23:59', [ 'eventTags' => [ $bar ] ]);
        $this->createEvent('2019-02-02T00:00', '2019-02-02T23:59', [ 'eventTags' => [ $bar ] ]);
        $this->createEvent('2019-02-02T00:00', '2019-02-03T00:00', [ 'eventTags' => [ $foo, $bar ] ]);
        $this->createEvent('2019-02-02T00:00', '2019-02-03T00:00', [ 'eventTags' => [ $foo, $bar ] ]);
        $this->createEvent('2019-02-02T00:00', '2019-02-03T00:00', [ 'eventTags' => [ $foo, $bar ] ]);

        $this->subject->setState([ 'tags' => [ $foo->getPersistenceObjectIdentifier() ] ]);
        $this->assertEquals(4, $this->subject->countResults());

        $this->subject->setState([ 'tags' => [ $bar->getPersistenceObjectIdentifier() ] ]);
        $this->assertEquals(5, $this->subject->countResults());
    }

    private function createEvent(string $from, string $to, array $properties = []): EventDate
    {
        $properties['beginsAt'] = new \DateTime($from);
        $properties['endsAt'] = new \DateTime($to);
        $properties['event'] = (new EventFactory($this->objectManager))->create($properties);
        return (new EventDateFactory($this->objectManager))->create($properties);
    }
}
