<?php


namespace App\Cron\Job;


class AutoProcess extends \App\Cron\Job\Utils\GeneralAbstract
{
	/**
	 * Cronjob category: {system|operational|financial|reporting}
	 * @var string
	 */
	protected $category = 'operational';


	/**
	 * {@inheritDoc}
	 * @see \App\Cron\Job\Utils\GeneralAbstract::validateSettings()
	 */
	public function validateSettings(\App\Entity\Messages $msgs, array $fields)
	{

		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();
		// LIMIT THE EXECUTION OF THE PROCESS -----------------------------------------------------
		if ($fields['scheduleOptions']['type']=='interval') {
			list($h, $m, $s) = explode(':', $fields['scheduleOptions']['repeatPeriod']);
			$repeatPeriod = $h*3600 + $m*60 + $s;
			if ($repeatPeriod<$CFG['cronJobs']['settings']['AutoProcess']['minPeriod']*60) {
				$msgs->addWarningText(sprintf("The process should not run more often than %s minutes.", $CFG['cronJobs']['settings']['AutoProcess']['minPeriod']));
			}
		}

	}


	/**
	 * {@inheritDoc}
	 * @see \App\Cron\Job\Utils\GeneralAbstract::executeJob()
	 */
	protected function executeJob()
	{
		// make sure that the db tables are already created
		\Entity\Order::createMonthlyTables();

		// force always access for this cron
		\App\UserAccess\CurrentUserAccessFast::getInstance()->becomeSuperUser();

		// the current execution id
		$idExecution = uniqid();
		$this->logger->info(sprintf('Created unique execution id: %s', $idExecution));

		// GET RULES THAT MUST BE EXECUTED NOW ////////////////////////////////////////////////////
		$rulesList = $this->getRulesList();
		if ($rulesList) {
			foreach ($rulesList as $idRule) {
				try {

					$ruleEnt = \Entity\AutoProcessRule::select($idRule);

					$this->logger->info(sprintf('Start executing rule "%s" with the id #%s (priority - %s).', $ruleEnt->name, $idRule, $ruleEnt->priority));

					$ruleLogs = new \App\AutoProcessRules\Logs\Rule($idExecution, $idRule);

					$apRule = \Entity\AutoProcessRule::execute($idRule);
					$apRule->ruleLogs = $ruleLogs;
					$apRule->save();

					// write logs
					$writeLogs = new \App\AutoProcessRules\Logs\WriteLogs($ruleLogs);
					$writeLogs->write();

					$this->logger->info(sprintf('Finished executing rule #%s.', $idRule));

					$this->execute_sendEmail($idRule, $ruleLogs);

				}
				catch (\App\Entity\Exception $e) {

					// write logs
					$writeLogs = new \App\AutoProcessRules\Logs\WriteLogs($ruleLogs);
					$writeLogs->write();

					$this->importFromMessageCollector($e->getMessagesCollector());
				}
			}
		} else {
			$this->logger->idle("No rule was executed.");
		}

		\App\UserAccess\CurrentUserAccessFast::getInstance()->becomeNormalUser();
	}


	/**
	 * Returns the list of rules id in order to be executed.
	 * @return Array
	 */
	private function getRulesList()
	{

		$weekDay = \Engine\DateTime::create()->format('w');
		$time = \Engine\DateTime::create()->format('H:i');

		// check if this is the last day of the month
		if (TODAY_DAY==date('t')) {
			$isLastDom = true;
		} else {
			$isLastDom = false;
		}

		// GET THE LIST OF RULES FROM THE DB //////////////////////////////////////////////////////
		$rulesListObj = new \App\EntityList\AutoProcessRules();
		$rulesListObj->setFilter('rules.active', '=', 1);
		$rulesListObj->setFilter('rules.cronProcess', '=', 1);
		$rulesListObj->setFilter('rules.weekDays', 'LIKE', "%|{$weekDay}|%");
		if ($isLastDom) {
			$rulesListObj->setCustomFilter('rules.monthDays LIKE ? OR rules.monthDays LIKE ?', ['%|'.(Int)TODAY_DAY.'|%', '%|lastDom|%']); // type cast to cleanup the leading 0s
		} else {
			$rulesListObj->setFilter('rules.monthDays', 'LIKE', "%|".(Int)TODAY_DAY."|%"); // type cast to cleanup the leading 0s
		}
		$rulesListObj->setFilter('rules.startTime', '<=', $time);
		$rulesListObj->setFilter('rules.endTime', '>=', $time);
		$rulesListObj->setOrderBy('rules.priority', 'DESC');
		$rulesListObj->setOrderBy('rules.id', 'ASC'); // TODO: for compatibility with the previous way the rules were selected to be executeed; this can be eliinated after all clients re-prioritize their rules (added on 2018-11-12)
		$rulesListEx = $rulesListObj->getExecution();

		// LOOP THROUGH THE RULES AND FILTER THE ONES THAT ARE NOT ALLOWED TO EXCUTE AGAIN TODAY //
		$result = [];
		foreach ($rulesListEx as $rule) {

			$idRule = $rule['id'];
			$ruleEnt = \Entity\AutoProcessRule::select($idRule);

			// include only the rules that are still allowed to be executed today
			$include = true;
			if ($ruleEnt->maxDailyExecutionsCount) {
				$dt = $ruleEnt->lastExecutionDateTime;
				if (!$dt->isEmpty() and $dt->isValid() and $dt->formatDateIso()==TODAY_ISO and $ruleEnt->currentDayExecutionsCount>=$ruleEnt->maxDailyExecutionsCount) {
					$include = false;
				}
			}
			if ($include) {
				$result[] = $idRule;
			}

		}

		return $result;
	}

	/**
	 * Send the notification email
	 * @return Boolean
	 */
	private function execute_sendEmail($idRule, \App\AutoProcessRules\Logs\Rule $ruleLogs)
	{
		$apRule = \Entity\AutoProcessRule::select($idRule);

		$logsList = $ruleLogs->getLogs();

		// get recipients
		$responsibleUsersList = $apRule->responsibleUsersList;

		if (!$responsibleUsersList) {
			$this->logger->info(sprintf("You did not configure any responsible users for rule '%s'. It is recommended that you do that.", $apRule->name));
			return false;
		}

		// get users emails
		$recipients = [];
		foreach ($responsibleUsersList as $idUser) {
			$userEntity = \Entity\User::select($idUser);
			if ($userEntity->email) {
				$recipients[] = $userEntity->email;
			}
		}

		if (!$recipients) {
			$this->logger->info("You did not configure any email address to receive the cron output. It is highly recommended that you do that.");
			return false;
		}

		$sender = \App\Config\AutoEmailSender::getInstance()->get('cronJobs');

		// SEND THE EMAIL /////////////////////////////////////////////////////////////////////////
		try {
			\DB::startTransaction();

			$subjectContent = "Automatic process cron output";

			$bodyContent = "<div>Logs for the execution of the auto-process rule #{$idRule}".($apRule->name?" ({$apRule->name})":'').":</div><br />";
			foreach ($logsList as $log) {
				// determine readable date/time
				$time = \Engine\DateTime::createFromTimestamp((Int)$log->time)->formatDatetimeIso();
				// determine message type (formatted with color)
				switch ($log->messageType) {
					case 'success': $color = 'green'; break;
					case 'error': $color = 'red'; break;
					case 'warning': $color = 'orange'; break;
					case 'info': $color = 'blue'; break;
					default: $color = 'black';
				}
				$messageType = sprintf("<span style='color:%s'>[%s]</span>", $color, $log->messageType);
				// add new body row
				$bodyContent .= '<div>';
				$bodyContent .= sprintf("%s: %s %s", $time, $messageType, $log->messageText);
				if ($log->entityType or $log->idEntity or $log->entityOperation) {
					$bodyContent .= sprintf(" Entity: %s %s #%s", $log->entityType, $log->entityOperation, $log->idEntity);
				}
				$bodyContent .= '</div>';
			}

			// Create email entity
			$mail = \Entity\Email::insert();
			
			// Set recipients
			$mail->to = $recipients;
			
			// Set sender information
			$mail->fromEmail = $sender['email'];
			$mail->fromName = $sender['name'];
			
			// Set subject
			$mail->subject = $subjectContent;
			
			// Set body content
			$htmlContent = $this->createHtmlLogContentForEmail($subjectContent, $bodyContent);
			$mail->body = $htmlContent;
			
			// Set email properties
			$mail->priority = 3; // high priority
			$mail->category = 'AutoProcess';

			// Save email to queue - cron will handle sending
			$mail->save();
			
			\DB::commitTransaction();
			
			$this->logger->info('A notification email was sent.');
			return true;
		}
		catch (\App\Entity\Exception $e) {
			\DB::rollbackTransaction();
			$this->logger->warning(sprintf("The email could not be sent due to a technical problem: %s", $e->getMessage()));
			return false;
		}
	}
}


