<?php

namespace common\modules\event\manager;

use common\models\Event;
use common\models\EventRegistration;
use common\modules\event\models\timetable\TimeTableMonth;
use common\modules\event\models\timetable\TimeTableMonthDay;
use common\modules\event\models\timetable\TimeTableMonthWeek;
use customerapi\models\available\EventInterval;
use customerapi\models\details\EventRegistrationView;
use DateTime;
use Exception;
use yii\db\Query;
use yii\db\StaleObjectException;

class EventManager
{
    /** @noinspection PhpUnused */

    /**
     * Delete or mark deleted an event
     * @param $id
     * @throws Throwable
     * @throws \yii\base\Exception on any error
     * @throws StaleObjectException
     */
    public function deleteEvent($id)
    {
        $event = Event::findOne($id);
        if (!isset($event)) {
            throw new \yii\base\Exception("Event $id not found");
        }
        $registrations = $event->eventRegistrations;
        if (!isset($registrations) || count($registrations) === 0) {
            $event->delete();
        } else {
            $event->deleted_at = time();
            $event->save(false);
        }
    }


    /**
     * @param EventInterval $interval
     * @return TimeTableMonth
     * @throws Exception
     */
    public function loadTimeTable($interval, $activeOrDisplayInterval = "active")
    {

        $timeTable = new TimeTableMonth();
        $timeTable->interval = $interval;

        /** @var DateTime[] $displayDates */
        $displayDates = $interval->getAllDisplayDates();

        $timeTable->weeks = [];

        /** @var DateTime $displayDate */
        foreach ($displayDates as $displayDate) {
            $timeTableMonthDay = new TimeTableMonthDay();
            $timeTableMonthDay->date = $displayDate;
            $timeTableMonthDay->active = $interval->isActive($displayDate);
            $timeTable->days[] = $timeTableMonthDay;

            $weekNumber = $displayDate->format('W');

            if (!isset($timeTable->weeks[$weekNumber])) {
                $week = new TimeTableMonthWeek();
                $week->weekNumber = $weekNumber;
                $timeTable->weeks[$weekNumber] = $week;
            }

            $weekDayName = strtolower($displayDate->format('l'));
            $timeTable->weeks[$weekNumber]->$weekDayName = $timeTableMonthDay;
        }

        if ($activeOrDisplayInterval == "active") {
            $dateTimeFrom = $interval->firstActiveDate;
            $to = clone $interval->lastActiveDate;
            $dateTimeTo = $to->modify('+1 day');
        } else {
            // active
            $dateTimeFrom = $interval->firstDisplayDate;
            $to = clone $interval->lastDisplayDate;
            $dateTimeTo = $to->modify('+1 day');
        }
        // get events between active dates

        $events = $this->getEvents($dateTimeFrom, $dateTimeTo);

        // set events per day
        /** @var Event $event */
        foreach ($events as $event) {

            foreach ($timeTable->days as $date) {
                $eventDay = new DateTime();
                $eventDay->setTimestamp($event->start);
                /** @noinspection PhpUndefinedMethodInspection */
                $eventDay->setTimezone($date->date->getTimezone());
                $eventDay->setTime(0, 0);
                if ($date->date == $eventDay) {
                    $date->events[] = $event;
                    break;
                }
            }
        }
        return $timeTable;
    }

    /**
     * Get all active events between the to dates
     * @param DateTime $fromInc from date inclusive (>=)
     * @param DateTime $toExcl to date exclusive (<)
     * @return Event[]
     */
    public function getEvents($fromInc, $toExcl)
    {
        $query = Event::find();

        $query = $query
            ->innerJoinWith('trainer')
            ->innerJoinWith('eventType')
            ->innerJoinWith('room')
            ->joinWith('activeEventRegistrations')
            ->andWhere(['>=', 'event.start', $fromInc->getTimestamp()])
            ->andWhere(['<', 'event.start', $toExcl->getTimestamp()])
            ->orderBy([
                'event.start' => SORT_ASC
            ]);
        $query = $this->withEventConditions($query);
        return $query
            ->all();
    }

    /**
     * Get conditions to retrieve all valid active events
     * @param Query $query
     * @return Query
     */
    public function withEventConditions($query)
    {
        $query = $query->andWhere(['event.active' => '1'])
            ->andWhere(['event.deleted_at' => null]);
        return $query;
    }/** @noinspection PhpUnused */

    /**
     * @param DateTime $date
     * @return Event[]
     */
    public function getEventsForDay($date)
    {
        $start = clone $date;
        $start->setTime(0, 0);

        $to = clone $start;
        $to->modify('+1 day');

        return $this->getEvents($start, $to);
    }

    public function getEvent($id)
    {
        return Event::findOne($id);
    }

    public function findActiveRegistrations($idEvent, $idCustomer = null)
    {

        $registrations = EventRegistrationView::find()->andWhere(['id_event' => $idEvent, 'id_customer' => \Yii::$app->user->id])->all();
        $allActiveRegistrations = EventRegistration::filterActive($registrations);
        if (isset($idCustomer)) {
            $allActiveRegistrations = EventRegistration::filterForCustomer($allActiveRegistrations, \Yii::$app->user->id);
        }
        return $allActiveRegistrations;
    }


    public function findActiveEvent($idEvent, $interval, $withRelatedObjects = false)
    {
        $paramEventStartMax = (clone $interval->lastActiveDate);
        $paramEventStartMax = $paramEventStartMax->modify('+1 day');
        $paramEventStartMax = $paramEventStartMax->getTimestamp();
        $query = Event::find()
            ->andWhere(['event.id' => $idEvent])
            ->andWhere(['>=', 'event.start', $interval->firstActiveDate->getTimestamp()])
            ->andWhere(['<', 'event.start', $paramEventStartMax])
            ->andWhere(['event.active' => '1']);

        if ($withRelatedObjects) {
            $query = $query->innerJoinWith('trainer')
                ->innerJoinWith('eventType')
                ->innerJoinWith('room');
        }


        return $query->one();
    }

    public function findActiveEvents($interval)
    {
        $paramEventStartMax = (clone $interval->lastActiveDate);
        $paramEventStartMax = $paramEventStartMax->modify('+1 day');
        $paramEventStartMax = $paramEventStartMax->getTimestamp();
        return Event::find()
            ->andWhere(['>=', 'event.start', $interval->firstActiveDate->getTimestamp()])
            ->andWhere(['<', 'event.start', $paramEventStartMax])
            ->andWhere(['event.active' => '1'])->all();
    }

}
