<?php

namespace AmeliaBooking\Domain\Services\Booking;

use AmeliaBooking\Domain\Collection\Collection;
use AmeliaBooking\Domain\Entity\Booking\Event\Event;
use AmeliaBooking\Domain\Entity\Booking\Event\EventPeriod;
use AmeliaBooking\Domain\Entity\Gallery\GalleryImage;
use AmeliaBooking\Domain\Factory\Gallery\GalleryImageFactory;
use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
use AmeliaBooking\Domain\Services\Settings\SettingsService;
use AmeliaBooking\Domain\ValueObjects\DateTime\DateTimeValue;
use AmeliaBooking\Domain\ValueObjects\Number\Integer\Id;
use AmeliaBooking\Domain\ValueObjects\Recurring;
use AmeliaBooking\Domain\ValueObjects\String\Cycle;

/**
 * Class EventDomainService
 *
 * @package AmeliaBooking\Domain\Services\Booking
 */
class EventDomainService
{
    /**
     * @param Recurring  $recurring
     * @param Collection $eventPeriods
     *
     * @return Collection
     *
     * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
     */
    public function getRecurringEventsPeriods($recurring, $eventPeriods)
    {
        $recurringPeriods = new Collection();

        if (!($recurring && $recurring->getCycle() && $recurring->getUntil())) {
            return $recurringPeriods;
        }

        $dateNew = null;
        $modifyCycle = 'days';
        $modifyBaseValue = $recurring->getCycleInterval()->getValue();

        switch ($recurring->getCycle()->getValue()) {
            case (Cycle::DAILY):
                $modifyCycle = 'days';
                break;

            case (Cycle::WEEKLY):
                $modifyCycle     = 'days';
                $modifyBaseValue = 7*$modifyBaseValue;
                break;

            case (Cycle::MONTHLY):
                if ($recurring->getMonthlyRepeat() === 'on') {
                    $repeatPeriod = $recurring->getMonthlyOnRepeat() . ' ' . $recurring->getMonthlyOnDay();
                } else {
                    $dateNew = $recurring->getMonthDate();
                }
                $modifyCycle = 'months';
                break;

            case (Cycle::YEARLY):
                $modifyCycle = 'years';
                break;
        }

        $hasMoreRecurringPeriods = true;

        $recurringOrder = 1;

        while ($hasMoreRecurringPeriods) {
            $periods = new Collection();

            $modifyValue = $recurringOrder * $modifyBaseValue;

            $periodStartDate0 = DateTimeService::getCustomDateTimeObject($eventPeriods->getItem(0)->getPeriodStart()->getValue()->format('Y-m-d H:i:s'));
            $periodEndDate0   = DateTimeService::getCustomDateTimeObject($eventPeriods->getItem(0)->getPeriodEnd()->getValue()->format('Y-m-d H:i:s'));

            if (isset($repeatPeriod)) {
                $periodStart0  = $this->getNextPeriodStartDate($periodStartDate0, $modifyValue, $repeatPeriod);
                $dayDifference = $periodStartDate0->diff($periodStart0);
            } else if ($dateNew) {
                $periodStartNew = DateTimeService::getCustomDateTimeObject($dateNew->getValue()->format('Y-m-d H:i:s'));
                $dayDifference  = $periodStartDate0->diff($periodStartNew);
            }

            for ($i = 0; $i < count($eventPeriods->getItems()); $i++) {
                $periodStartDate = DateTimeService::getCustomDateTimeObject($eventPeriods->getItem($i)->getPeriodStart()->getValue()->format('Y-m-d H:i:s'));
                $periodEndDate   = DateTimeService::getCustomDateTimeObject($eventPeriods->getItem($i)->getPeriodEnd()->getValue()->format('Y-m-d H:i:s'));

                if (isset($repeatPeriod)) {
                    if ($i === 0) {
                        $periodStart = $periodStart0;
                        $periodEnd   = $periodEndDate0->add($dayDifference);
                    } else {
                        $periodStart = $periodStartDate->add($dayDifference);
                        $periodEnd   = $periodEndDate->add($dayDifference);
                    }
                } else {
                    $periodStart = $periodStartDate->modify("+{$modifyValue} {$modifyCycle}");
                    $periodEnd   = $periodEndDate->modify("+{$modifyValue} {$modifyCycle}");
                    if ($dateNew) {
                        $periodStart = $periodStart->add($dayDifference);
                        $periodEnd   = $periodEnd->add($dayDifference);
                    }
                }

                $newEventPeriod = new EventPeriod();

                $newEventPeriod->setPeriodStart(new DateTimeValue($periodStart));
                $newEventPeriod->setPeriodEnd(new DateTimeValue($periodEnd));

                $periods->addItem($newEventPeriod);

                if ($periodStart > $recurring->getUntil()->getValue()) {
                    $hasMoreRecurringPeriods = false;
                }
            }

            if ($hasMoreRecurringPeriods) {
                $recurringPeriods->addItem($periods);
                $recurringOrder++;
            }
        }

        return $recurringPeriods;
    }

    /**
     * @param Collection $eventPeriods
     * @param bool       $setId
     *
     * @return Collection
     *
     * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
     */
    public function getClonedEventPeriods($eventPeriods, $setId = false)
    {
        $clonedPeriods = new Collection();

        /** @var EventPeriod $eventPeriod **/
        foreach ($eventPeriods->getItems() as $eventPeriod) {
            $periodStart = DateTimeService::getCustomDateTimeObject(
                $eventPeriod->getPeriodStart()->getValue()->format('Y-m-d H:i:s')
            );

            $periodEnd = DateTimeService::getCustomDateTimeObject(
                $eventPeriod->getPeriodEnd()->getValue()->format('Y-m-d H:i:s')
            );

            $newEventPeriod = new EventPeriod();

            $newEventPeriod->setPeriodStart(new DateTimeValue($periodStart));
            $newEventPeriod->setPeriodEnd(new DateTimeValue($periodEnd));

            if ($eventPeriod->getZoomMeeting()) {
                $newEventPeriod->setZoomMeeting($eventPeriod->getZoomMeeting());
            }

            if ($setId) {
                $newEventPeriod->setId(new Id($eventPeriod->getId()->getValue()));
            }

            $clonedPeriods->addItem($newEventPeriod);
        }

        return $clonedPeriods;
    }

    /**
     * @param Event      $followingEvent
     * @param Event      $originEvent
     * @param Collection $clonedOriginEventPeriods
     *
     * @return void
     *
     * @throws \Slim\Exception\ContainerValueNotFoundException
     * @throws \Interop\Container\Exception\ContainerException
     * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
     */
    public function buildFollowingEvent($followingEvent, $originEvent, $clonedOriginEventPeriods)
    {
        $followingEvent->setName($originEvent->getName());
        $followingEvent->setPrice($originEvent->getPrice());
        $followingEvent->setMaxCapacity($originEvent->getMaxCapacity());
        $followingEvent->setTags($originEvent->getTags());
        $followingEvent->setProviders($originEvent->getProviders());
        $followingEvent->setBringingAnyone($originEvent->getBringingAnyone());
        $followingEvent->setBookMultipleTimes($originEvent->getBookMultipleTimes());

        if ($originEvent->getTranslations()) {
            $followingEvent->setTranslations($originEvent->getTranslations());
        }

        if ($originEvent->getDeposit()) {
            $followingEvent->setDeposit($originEvent->getDeposit());
        }

        if ($originEvent->getDepositPayment()) {
            $followingEvent->setDepositPayment($originEvent->getDepositPayment());
        }

        if ($originEvent->getDepositPerPerson()) {
            $followingEvent->setDepositPerPerson($originEvent->getDepositPerPerson());
        }

        $followingEventGallery = new Collection();

        /** @var GalleryImage $image **/
        foreach ($originEvent->getGallery()->getItems() as $image) {
            $followingEventGallery->addItem(
                GalleryImageFactory::create([
                    'id'               => null,
                    'entityId'         => $followingEvent->getId() ? $followingEvent->getId()->getValue() : null,
                    'entityType'       => $image->getEntityType()->getValue(),
                    'pictureFullPath'  => $image->getPicture()->getFullPath(),
                    'pictureThumbPath' => $image->getPicture()->getThumbPath(),
                    'position'         => $image->getPosition()->getValue(),
                ])
            );
        }

        $followingEvent->setGallery($followingEventGallery);

        if ($originEvent->getSettings()) {
            $followingEvent->setSettings($originEvent->getSettings());
        }

        $followingEvent->setBookingOpens($originEvent->getBookingOpens());

        $followingEvent->setBookingCloses($originEvent->getBookingCloses());

        $followingEvent->setBookingOpensRec($originEvent->getBookingOpensRec());

        $followingEvent->setBookingClosesRec($originEvent->getBookingClosesRec());

        if ($originEvent->getLocationId()) {
            $followingEvent->setLocationId($originEvent->getLocationId());
        }

        if ($originEvent->getCustomLocation()) {
            $followingEvent->setCustomLocation($originEvent->getCustomLocation());
        }

        if ($originEvent->getTags()) {
            $followingEvent->setTags($originEvent->getTags());
        }

        if ($originEvent->getDescription()) {
            $followingEvent->setDescription($originEvent->getDescription());
        }

        if ($originEvent->getColor()) {
            $followingEvent->setColor($originEvent->getColor());
        }

        if ($originEvent->getShow()) {
            $followingEvent->setShow($originEvent->getShow());
        }

        if ($originEvent->getZoomUserId()) {
            $followingEvent->setZoomUserId($originEvent->getZoomUserId());
        }

        $modifyCycle = 'days';
        $modifyBaseValue = $originEvent->getRecurring()->getCycleInterval()->getValue();
        $dateNew = null;

        switch ($originEvent->getRecurring()->getCycle()->getValue()) {
            case (Cycle::DAILY):
                $modifyCycle = 'days';
                break;

            case (Cycle::WEEKLY):
                $modifyCycle     = 'days';
                $modifyBaseValue = 7*$modifyBaseValue;
                break;

            case (Cycle::MONTHLY):
                if ($followingEvent->getRecurring()->getMonthlyRepeat() === 'on') {
                    $repeatPeriod = $followingEvent->getRecurring()->getMonthlyOnRepeat() . ' ' . $followingEvent->getRecurring()->getMonthlyOnDay();
                } else {
                    $dateNew = $followingEvent->getRecurring()->getMonthDate();
                }
                $modifyCycle = 'months';
                break;

            case (Cycle::YEARLY):
                $modifyCycle = 'years';
                break;
        }

        $modifyValue = $modifyBaseValue * ($followingEvent->getRecurring()->getOrder()->getValue() - 1);

        $periodStartDate0 = DateTimeService::getCustomDateTimeObject($clonedOriginEventPeriods->getItem(0)->getPeriodStart()->getValue()->format('Y-m-d H:i:s'));
        $periodEndDate0   = DateTimeService::getCustomDateTimeObject($clonedOriginEventPeriods->getItem(0)->getPeriodEnd()->getValue()->format('Y-m-d H:i:s'));

        if (isset($repeatPeriod)) {
            $periodStart0  = $this->getNextPeriodStartDate($periodStartDate0, $modifyValue, $repeatPeriod);
            $dayDifference = $periodStartDate0->diff($periodStart0);
        } else if ($dateNew) {
            $thisPeriodStart = DateTimeService::getCustomDateTimeObject($originEvent->getPeriods()->getItem(0)->getPeriodStart()->getValue()->format('Y-m-d H:i:s'));
            $periodStartNew  = DateTimeService::getCustomDateTimeObject($dateNew->getValue()->format('Y-m-d H:i:s'));
            $dayDifference   = $thisPeriodStart->diff($periodStartNew);
        }


        /** @var EventPeriod $followingEventPeriod */
        foreach ($followingEvent->getPeriods()->getItems() as $key => $followingEventPeriod) {
            if ($clonedOriginEventPeriods->keyExists($key)) {
                /** @var EventPeriod $clonedOriginEventPeriod */
                $clonedOriginEventPeriod = $clonedOriginEventPeriods->getItem($key);

                $periodStartDate = DateTimeService::getCustomDateTimeObject($clonedOriginEventPeriod->getPeriodStart()->getValue()->format('Y-m-d H:i:s'));
                $periodEndDate   = DateTimeService::getCustomDateTimeObject($clonedOriginEventPeriod->getPeriodEnd()->getValue()->format('Y-m-d H:i:s'));

                if (isset($repeatPeriod)) {
                    if ($key === 0) {
                        $periodStart = $periodStart0;
                        $periodEnd   = $periodEndDate0->add($dayDifference);
                    } else {
                        $periodStart = $periodStartDate->add($dayDifference);
                        $periodEnd   = $periodEndDate->add($dayDifference);
                    }
                } else {
                    $periodStart = $periodStartDate->modify("+{$modifyValue} {$modifyCycle}");
                    $periodEnd   = $periodEndDate->modify("+{$modifyValue} {$modifyCycle}");
                    $toEndTime   = $periodStart->diff($periodEnd);
                    if ($dateNew) {
                        $periodStart = DateTimeService::getCustomDateTimeObject($periodStart->format('Y-m-'. $periodStartNew->format('d') .' H:i:s'));
                        $periodStartClone = clone $periodStart;
                        $periodEnd = $periodStartClone->add($toEndTime);
                    }
                }

                $followingEventPeriod->setPeriodStart(new DateTimeValue($periodStart));
                $followingEventPeriod->setPeriodEnd(new DateTimeValue($periodEnd));
            } else {
                $followingEvent->getPeriods()->deleteItem($key);
            }
        }

        /** @var EventPeriod $originEventPeriod */
        foreach ($originEvent->getPeriods()->getItems() as $key => $originEventPeriod) {
            if (!$followingEvent->getPeriods()->keyExists($key)) {

                /** @var EventPeriod $followingEventPeriod */
                $newFollowingEventPeriod = new EventPeriod();

                $newFollowingEventPeriod->setPeriodStart(
                    new DateTimeValue(
                        DateTimeService::getCustomDateTimeObject(
                            $originEventPeriod->getPeriodStart()->getValue()->format('Y-m-d H:i:s')
                        )->add($dayDifference)
                    )
                );

                $newFollowingEventPeriod->setPeriodEnd(
                    new DateTimeValue(
                        DateTimeService::getCustomDateTimeObject(
                            $originEventPeriod->getPeriodEnd()->getValue()->format('Y-m-d H:i:s')
                        )->add($dayDifference)
                    )
                );

                $newFollowingEventPeriod->setEventId(new Id($followingEvent->getId()->getValue()));

                $followingEvent->getPeriods()->addItem($newFollowingEventPeriod);
            }
        }
    }

    /**
     * @param Collection $eventPeriods
     * @param bool       $setId
     *
     * @return Collection
     *
     * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
     */
    public function getShortcodeForEventList($container, $events)
    {

        /** @var SettingsService $settingsService */
        $settingsService = $container->get('domain.settings.service');
        $dateFormat = $settingsService->getSetting('wordpress', 'dateFormat');

        for ($i=0; $i < count($events); $i++) {
            $dateString = explode(" ", $events[$i]['periods'][0]['periodStart'])[0];
            $newDate = date_i18n($dateFormat, strtotime($dateString));
            $events[$i]['formattedPeriodStart'] = $newDate;
        }
        return $events;
    }

    /**
     * @param \DateTime $periodStartDate0
     * @param int $modifyValue
     * @param string $repeatPeriod
     *
     * @return \DateTime
     *
     * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
     * @throws \Exception
     */
    public function getNextPeriodStartDate($periodStartDate0, $modifyValue, $repeatPeriod)
    {
        $month = (int)$periodStartDate0->format('m') + $modifyValue;
        $year  = (int)$periodStartDate0->format('Y');

        $year  += floor($month / 12);
        $month -= 12 * floor($month / 12);
        $time   = (int)$periodStartDate0->format('H')*60 + (int)$periodStartDate0->format('i');

        $periodStart0 = DateTimeService::getCustomDateTimeObject($repeatPeriod . " of $year-$month");
        return $periodStart0->add(new \DateInterval('PT' . $time . 'M'));
    }
}
