<?php

namespace App\ReportsData;

/**
 * Class that manages the params of the RD queue management and other operations.
 * The most important param is "status" - it can have these values:
 * "running" (cron is running),
 * "pending" (cron is not running but everything is ok),
 * "stopped" (manually stopped),
 * "blocked" (blocked on demand for a max period of time),
 * "disabled" (none of the RD functionality is active, and the queue is not being populated; upon first use, when the field is blank it is equivalent to disabled).
 * @author Gabriel Grosu
 */
class Params extends \App\Config\DbUniAssocVarsAbstract
{

	/**
	 * The vars prefix in systemVars db table. Must be set; blank is valid, but it must not be null.
	 * @var String
	 */
	protected $dbVarsPrefix = '';

	/**
	 * The 'var' column name for this table.
	 * @var String
	 */
	protected $dbTablePkCol = 'var';

	/**
	 * The column that contains the actual value for this association.
	 * @var String
	 */
	protected $dbTableValueCol = 'value';

	/**
	 * Indicates the table column name which stores the last update time for the current record;
	 * if nothing is set, the info is not saved in the db.
	 * @var String
	 */
	protected $dbTableLastUpdateTimeCol = 'lastUpdate';


	/**
	 * The list of vars managed by this class - if the array is empty, anything is allowed; if set
	 * to a list of values, only these values are allowed.
	 * @var array
	 */
	protected $dbVarsList = [ 'status', 'wsAuthToken', 'blockedUntil', 'blockingApp' ];

	/**
	 * Resets the cache type - we need to always load the details from the db.
	 * @var string
	 */
	protected $cacheType = 'dummy';

	/**
	 * The max period of time [seconds] that one is allowed to block the RD.
	 * @var integer
	 */
	private $maxBlockTime = 3600;

	/**
	 * {@inheritDoc}
	 * @see \App\Config\DbUniAssocVarsAbstract::getDbTableName()
	 */
	protected function getDbTableName()
	{
		global $DBTRdParams;
		return $DBTRdParams;
	}


	/**
	 * {@inheritDoc}
	 * @see \App\Config\BaseAbstract::save_check()
	 */
	protected function save_check()
	{
		// check parent validations - dbVarsList
		parent::save_check();

		// check fields length
		if ($this->itemsList) {
			foreach($this->itemsList as $var=>$value) {
				// check all values length
				if (mb_strlen($value)>32) {
					$this->addErrorMessage("The var `{$var}` in SystemVars must not have a length greater than 32 chars.");
				}
				// check status values
				if ($var=='status' and !in_array($value, ['running', 'pending', 'stopped', 'blocked', 'disabled'])) {
					$this->addErrorMessage("The var `{$var}` in SystemVars must not have a length greater than 32 chars.");
				}
				if ($var=='wsAuthToken' and mb_strlen($value)!=32) {
					$this->addErrorMessage("The var `{$var}` in SystemVars must have a length of 32 chars.");
				}
			}
		}
	}


	/**
	 * Returns the current status of the RD queue.
	 * @throws \Exception - for invalid instantiation
	 * @return string
	 */
	public function getStatus()
	{

		// check constructor type
		if ($this->constructType!='instance') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `instance`.");
		}

		// get the status from db
		$currentStatus = $this->get('status');
		// force blank as "disabled"
		if (!$currentStatus) {
			$currentStatus = 'disabled';
		}

		// if blocked but the blockedUntil expired
		if ($currentStatus=='blocked') {
			if ($this->get('blockedUntil') < \Engine\DateTime::create()->formatDatetimeIso()) {
				$currentStatus = 'pending';
			}
		}

		return $currentStatus;

	}

	/**
	 * Returns the max block time
	 * @return number
	 */
	public function getMaxBlockTime()
	{
		// check constructor type
		if ($this->constructType!='instance') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `instance`.");
		}

		return $this->maxBlockTime;
	}

	/**
	 * Sets the status to 'running'.
	 * @throws \Exception
	 */
	public function saveStatusToRunning()
	{

		// check constructor type
		if ($this->constructType!='admin') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `admin`.");
		}

		// CHECK INPUT ----------------------------------------------------------------------------
		$currentStatus = self::getInstance()->getStatus();
		// if already "running" - skip this method
		if ($currentStatus=='running') {
			return true;
		}
		if (!in_array($currentStatus, ['pending', 'running'])) {
			$this->addErrorMessage("The RD queue cannot be set to running as the current status is `{$currentStatus}`.");
		}

		// UPDATE STATUS --------------------------------------------------------------------------
		$this->set('status', 'running');
		$this->save();

	}

	public function saveStatusToPending()
	{

		// check constructor type
		if ($this->constructType!='admin') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `admin`.");
		}


		$this->set('status', 'pending');
		$this->save();

	}


	public function saveStatusToDisabled()
	{
		// check constructor type
		if ($this->constructType!='admin') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `admin`.");
		}

		$this->set('status', 'disabled');
		$this->save();
	}


	public function saveStatusToStopped()
	{
		// check constructor type
		if ($this->constructType!='admin') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `admin`.");
		}

		$this->set('status', 'stopped');
		$this->save();
	}


	/**
	 * Blocks the RQ queue.
	 * @param int $blockTime - number of seconds to block the RD.
	 * @param string $blockingApp - the name of the app that blocks the RD.
	 */
	public function saveStatusToBlocked($blockTime, $blockingApp)
	{
		// check constructor type
		if ($this->constructType!='admin') {
			throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `admin`.");
		}

		// CHECK INPUT ----------------------------------------------------------------------------
		if (!is_numeric($blockTime) or $blockTime<=0) {
			$this->addErrorMessage('You did not set a valid block time.');
		}
		if ($blockTime>$this->maxBlockTime) {
			$this->addErrorMessage("The block time must be smaller than {$this->maxBlockTime} seconds.");
		}
		if (!$blockingApp) {
			$this->addErrorMessage("The blocking app is not set.");
		}
		$currentStatus = self::getInstance()->getStatus();
		if ($currentStatus!='running' and $currentStatus!='pending' and $currentStatus!='blocked') {
			$this->addErrorMessage("The RD queue cannot be blocked as the current status is `{$currentStatus}`.");
		}

		// if the current blocking does not increase the actual blockUntil - do not do aything
		$blockedUntil = \Engine\DateTime::create("+{$blockTime} seconds")->formatDateTimeIso();
		if ($currentStatus=='blocked') {
			$currentBlockTime = self::getInstance()->get('blockedUntil');
			if ($blockedUntil<=$currentBlockTime) {
				return ;
			}
		}

		// SAVE THE DATA IN THE DB ----------------------------------------------------------------
		$this->set('status', 'blocked');
		$this->set('blockedUntil', $blockedUntil);
		$this->set('blockingApp', $blockingApp);
		$this->save();

	}

}


