<?php

// TODO: [not important] adaugat la calendarEvents la entitate si "task"
// TODO: saveInsideTransaction - should be "true" instead of []
// TODO: field mailStatus should be enum type

namespace Entity;

class CalendarEvent extends \App\Entity\GeneralAbstract
{

	/**
	 * Indicate if the operations available for the current entity must be done inside a transaction or not.
	 * @var array
	 */
 	protected $saveInsideTransaction = ['insert' => false, 'update' => true, 'delete' => true, 'sendMail' => true, 'purge' => true];

	protected $addAndUpdateDateTimeFormat = 'timestamp'; // TODO: to be eliminated after the table columns are renamed and replaced with datetime


	protected $validEntities = [
		'order'=>				'\Entity\Order',
		'corporateContact'=>	'\Entity\CorporateContact',
		'corporate'=>			'\Entity\Corporate',
		'invoice'=>				'\Entity\Invoice',
		'service'=>				'\Entity\Service',
		'corporateContract'=>	'\Entity\CorporateContract',
		'supplierContract'=>	'\Entity\SupplierContract',
		'importedService' =>	'\Entity\ImportedService'

	];


	/**
	 * (non-PHPdoc)
	 * @see App\Entity.SelectAbstract::setTableName()
	 */
	protected function setTableName(): void
	{
		global $DBTCalendarEvents;
		$this->tableName = $DBTCalendarEvents;
	}

	/**
	 * Send the reminder email for the current calendar event.
	 * @param Int $id
	 */
	static public function sendMail($id) {
		return new self('sendMail', $id);
	}

	/**
		* Permanently remove the calendar event record from the database
		* @param int $idEntity
		* @return self
	*/
	static public function purge(int $idEntity)
	{
		return new self('purge', $idEntity);
	}

	/**
	 * Returns the class name of the current entity.
	 * @param String $entity
	 * @return String - the class name
	 */
	public function getEntityClassName($entity)
	{
		return $this->validEntities[$entity];
	}

	/**
	 * Returns the entity object for this kind of entity type and for this id.
	 * @param String $entityType
	 * @param Int $idEntity
	 * @throws \Exception
	 */
	public function getEntityObject($entityType, $idEntity)
	{
		$class = $this->validEntities[$entityType];
		if (!$class) {
			throw new \Exception("There is no class name defined for the calendar entity type '{$entityType}'");
		}

		return $class::select($idEntity);
	}

	/**
	 * (non-PHPdoc)
	 * @see App\Entity.SelectAbstract::getAttribute()
	 */
	protected function getAttribute($var)
	{
		switch ($var) {

			case 'viewedByUser': {
				return (bool)$this->fields[$var];
			}

			// start date in ISO format
			case 'startDate': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['startDateTime'])->formatDateIso();
				}
				return $this->fields[$var];
			}

			// end date in ISO format
			case 'endDate': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['endDateTime'])->formatDateIso();
				}
				return $this->fields[$var];
			}

			// alert date in ISO format
			case 'alertDate': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['alertDateTime'])->formatDateIso();
				}
				return $this->fields[$var];
			}

			// dismiss date in ISO format
			case 'dismissDate': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['dismissDateTime'])->formatDateIso();
				}
				return $this->fields[$var];
			}

			// start time (H:i:s)
			case 'startTime': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['startDateTime'])->formatTimeIso();
				}
				return $this->fields[$var];
			}

			// end time (H:i:s)
			case 'endTime': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['endDateTime'])->formatTimeIso();
				}
				return $this->fields[$var];
			}

			// alert time (H:i:s)
			case 'alertTime': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['alertDateTime'])->formatTimeIso();
				}
				return $this->fields[$var];
			}

			// dismiss time (H:i:s)
			case 'dismissTime': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['dismissDateTime'])->formatTimeIso();
				}
				return $this->fields[$var];
			}

			// the timestamp for start date and time
			case 'startDateTimestamp': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['startDateTime'])->getTimestamp();
				}
				return $this->fields[$var];
			}

			// the timestamp for end date and time
			case 'endDateTimestamp': {
				if (!array_key_exists($var, $this->fields)) {
					$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['endDateTime'])->getTimestamp();
				}
				return $this->fields[$var];
			}

			// the timestamp for alert date and time
			case 'alertDateTimestamp': {
				if (!array_key_exists($var, $this->fields)) {
					if ($this->fields['alertDateTime'] == DATETIME_LIMIT_MIN) {
						$this->fields[$var] = 0;
					}
					else {
						$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['alertDateTime'])->getTimestamp();
					}
				}
				return $this->fields[$var];
			}

			// the timestamp for dismiss date and time
			case 'dismissDateTimestamp': {
				if (!array_key_exists($var, $this->fields)) {
					if ($this->fields['dismissDateTime'] == DATETIME_LIMIT_MIN) {
						$this->fields[$var] = 0;
					}
					else {
						$this->fields[$var] = \Engine\DateTime::createFromFormat('Y-m-d H:i:s', $this->fields['dismissDateTime'])->getTimestamp();
					}
				}
				return $this->fields[$var];
			}

			case 'extraInfo': {
				if (!is_array($this->fields[$var])) {
					if ($this->fields[$var]) {
						$this->fields[$var] = unserialize($this->fields[$var]);
					}
					else {
						$this->fields[$var] = [];
					}
				}
				return $this->fields[$var];
			}

			// extract the entities array
			case 'entities': {
				if (!array_key_exists($var, $this->fields)) {
					$extraInfo = $this->getAttribute('extraInfo');
					if ($extraInfo['entities']) {
						$this->fields[$var] = $extraInfo['entities'];
					}
					else {
						$this->fields[$var] = [];
					}
				}
				return $this->fields[$var];
			}

			// return the mail status: 0 = no email, 1 = yes email (not sent), 2 = yes email (already sent)
			case 'mailStatus': {
				return $this->fields[$var];
			}

			// user
			case 'user': {
				return \Entity\User::select($this->fields['idUser'], $this->selectForUpdate);
			}

			// add user
			case 'addUser': {
				return \Entity\User::select($this->fields['idAddUser'], $this->selectForUpdate);
			}

			// update user
			case 'updateUser': {
				return \Entity\User::select($this->fields['idUpdateUser'], $this->selectForUpdate);
			}

			// the rest of the fields (most of them are just copied)
			default: {
				return parent::getAttribute($var);
			}
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see App\Entity.GeneralAbstract::setAttribute()
	 */
	protected function setAttribute($var, $value)
	{
		// check that operation is okay
		if ($this->operation != 'insert' and $this->operation != 'update') {
			throw new \Exception('You can use __set magic method only with insert/update operation in `'.__CLASS__.'` class.');
		}

		// CHECK EACH POSSIBLE FIELD VALUE --------------------------------------------------------
		switch ($var) {
			case 'title': case 'description': case 'location':	case 'allDay': case 'mailStatus': case 'viewedByUser':
			case 'startDateTime': case 'endDateTime': case 'alertDateTime': case 'access': {
				$this->fields[$var] = $value;
				break;
 			}

 			 /**
 			  * TODO: the idAddUser field should not be allowed to be set
 			  * This field is checked if its set, and should be assumed to always be unset, and the validator should behave differently.
 			  * The validator should allow adding an event from an non-logged user(FluX, CronJobs, etc), and for this, a virtual field should exists: "_dontCheckAddUser"
 			  */
 			case 'idAddUser': {
				if ($this->operation !='insert') {
					throw new \Exception('The calendar event `idAddUser` can only be set on `insert` operation.');
				}
				$this->fields[$var] = $value;
				break;
			}


			case 'dismissDateTime': {
				if ($this->operation != 'update') {
					throw new \Exception('The calendar event `alertDateTime` can only be set on `update` operation.');
				}

				$this->fields[$var] = $value;
				break;
			}

			case 'extraInfo': {
				// check if value is array
				if (!is_array($value)) {
					throw new \Exception('The calendar event `extraInfo` can only be set as an array.');
				}

				$this->fields[$var] = $value;
				break;
			}

			// stores the entities list - an array of 'type' and 'id'
			case 'entities': {
				if (!is_array($value)) {
					throw new \Exception('The calendar event `entities` field must be set as an array.');
				}

				$this->fields[$var]=$value;
				break;
			}

			// the event user
			case 'idUser': {
				if ($this->operation!='insert') {
					throw new \Exception('The calendar event `idUser` field can be set only on `insert`.');
				}

				$this->fields[$var]=$value;
				break;
			}


			// the field is not valid
			default: {
				throw new \Exception("The field '$var' is not valid for the `".__CLASS__."` entity.");
			}
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see App\Entity.GeneralAbstract::save_filterSaveData()
	 */
	protected function save_filterSaveData(Array $saveData)
	{
		if (array_key_exists('allDay', $saveData)) {
			$saveData['allDay'] = (Bool)$saveData['allDay'];
		}

		// if allDay event - startData will have 00:00:00 time and endDate 23:59:59 time
		if ($saveData['allDay']) {
			$saveData['startDateTime'] = \Engine\DateTime::create($saveData['startDateTime'])->formatDateIso();
			$saveData['endDateTime'] = \Engine\DateTime::create($saveData['endDateTime'])->formatDateIso();
		}

		// clean extra info
		if (($this->fields['extraInfo'] or $this->fields['entities']) and ($this->operation == 'insert' or $this->operation == 'update')) {
			if ($this->operation == 'update') {
				$extraInfo = \Entity\CalendarEvent::selectForUpdate($this->id)->extraInfo;
			}
			else {
				$extraInfo = [];
			}

			if ($this->fields['extraInfo']) {
				foreach ($this->fields['extraInfo'] as $key => $val) {
					if ($val === NULL) {
						unset($extraInfo[$key]);
					}
					else {
						$extraInfo[$key] = $val;
					}
				}
			}

			if ($this->fields['entities']) {
				$extraInfo['entities'] = $this->fields['entities'];
				unset($saveData['entities']);
			}

			$saveData['extraInfo'] = serialize($extraInfo);

		}

		// For action sendEmail set field mailStatus 2 (mail sent)
		if ($this->operation == 'sendMail'){
			$saveData['mailStatus'] = 2;
		}

		return $saveData;
	}

	/**
	 * Process operations and DB writes
	 * @param array $saveData
	 * @return void
	 * @throws \DBException
	 */
	protected function save_process(array $saveData) : void
	{
		if ($this->operation == 'purge') {
			$this->save_process_purge($saveData);
			return;
		}

		parent::save_process($saveData);
	}

	/**
	 * Permanently delete the calendar event record
	 * @param array $saveData
	 * @return void
	 */
	private function save_process_purge(array $saveData) : void
	{
		if ($this->save_executeSql_delete($this->tableName, 'id', $this->id)) {
			$this->messages->addSuccessCode('delete_ok');
		}
		else {
			$this->messages->addErrorText('Could not purge the calendar event.');
		}
	}


	protected function save_postProcess(Array $saveData)
	{
		if ($this->operation == 'sendMail') {
			// load  LANG
			$_LANG = \App\Loader_Lang::getInstance()->getAll();

			// load CFG
			$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();

			// init current event
			$calendarEvent = \Entity\CalendarEvent::selectForUpdate($this->id);

			// message subject
			if ($_LANG['events']['reminderEmail']['subject']) {
				$mailSubject = $_LANG['events']['reminderEmail']['subject'];
			}
			else {
				$mailSubject = $CFG['events']['reminderEmail']['subject'];
			}

			// message template
			if ($_LANG['events']['reminderEmail']['messageTemplate']) {
				$messageTemplate = $_LANG['events']['reminderEmail']['messageTemplate'];
			}
			else {
				$messageTemplate = $CFG['events']['reminderEmail']['messageTemplate'];
			}

			// add user info
			$addUserInfo = '-';

			if ($calendarEvent->idAddUser) {
				$addUserInfo = $calendarEvent->addUser->name. ' - '.$calendarEvent->addUser->office->name;
			}

			// compose message
			$messageBody = sprintf($messageTemplate, $calendarEvent->title, $calendarEvent->description, $calendarEvent->startDateTime, $calendarEvent->endDateTime, $addUserInfo);
			$messageBody = nl2br($messageBody);

			// user emai address
			$userEmailAddress = $calendarEvent->user->email;

			// if not exists extract from alternative email
			if (!$userEmailAddress) {
				$userEmailAddress = $calendarEvent->user->emailAlternative;
			}

			// SEND THE ACTUAL EMAIL ///////////////////////////////////////////////////////////////
			if ($userEmailAddress) {
				// set the sender email address
				$senderInfo = \App\Config\AutoEmailSender::getInstance()->get('calendarEvents');

				try {
					// Create email entity
					$mail = \Entity\Email::insert();
					
					// Set recipient
					$mail->to = [$userEmailAddress];
					
					// Set sender information
					$mail->fromEmail = $senderInfo['email'];
					$mail->fromName = $senderInfo['name'];
					
					// Set subject and body
					$mail->subject = $mailSubject;
					$mail->body = $messageBody;
					
					// Set email properties
					$mail->priority = 1; // low priority
					$mail->category = 'CalendarEvent';

					// Save email to queue - cron will handle sending
					$mail->save();
					
				} catch (\App\Entity\Exception $ex) {
					$this->messages->removeSuccess();
					$this->messages->addErrorCode('sendMail_fail', '', $ex->getMessagesCollector()->getMessageBulk());
				}
			}
		}
	}
}