<?php
/**
 * @package     VikRentItems
 * @subpackage  com_vikrentitems
 * @author      E4J srl
 * @copyright   Copyright (C) 2025 E4J srl. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://vikwp.com
 */

defined('ABSPATH') or die('No script kiddies please!');

/**
 * Helper Class to keep track of the events that involve a specific
 * rental order ID, from the creation till the deletion or modification.
 * 
 * @since 	1.8.0 (J) - 1.2.0 (WP)
 */
class VriOrderHistory
{
	/**
	 * @var  int
	 */
	protected $bid = null;

	/**
	 * @var  array
	 */
	protected $prevOrder = null;

	/**
	 * @var  mixed
	 */
	protected $data = null;

	/**
	 * @var  object
	 */
	protected $dbo = null;

	/**
	 * @var  array
	 */
	protected $typesMap = [];

	/**
	 * Class constructor.
	 * 
	 * @param 	int 	$bid 	optional order ID to set.
	 */
	public function __construct($bid = 0)
	{
		$this->dbo = JFactory::getDbo();
		$this->typesMap = $this->getTypesMap();

		if ($bid) {
			$this->bid = (int)$bid;
		}
	}

	/**
	 * Returns an array of types mapped to
	 * the corresponding language definition.
	 * All the history types should be listed here.
	 *
	 * @return 	array
	 */
	public function getTypesMap()
	{
		return array(
			// New order with status Confirmed
			'NC' => JText::translate('VRIBOOKHISTORYTNC'),
			// Order modified from back-end
			'MB' => JText::translate('VRIBOOKHISTORYTMB'),
			// New order from back-end
			'NB' => JText::translate('VRIBOOKHISTORYTNB'),
			// New order with status Pending
			'NP' => JText::translate('VRIBOOKHISTORYTNP'),
			// Order paid for the first time
			'P0' => JText::translate('VRIBOOKHISTORYTP0'),
			// Order paid for a second time
			'PN' => JText::translate('VRIBOOKHISTORYTPN'),
			// Cancellation request message
			'CR' => JText::translate('VRIBOOKHISTORYTCR'),
			// Order cancelled via back-end by admin
			'CB' => JText::translate('VRIBOOKHISTORYTCB'),
			// Order set to Confirmed by admin
			'TC' => JText::translate('VRIBOOKHISTORYTTC'),
			// Custom email sent to the customer by admin
			'CE' => JText::translate('VRIBOOKHISTORYTCE'),
		);
	}

	/**
	 * Sets the current order ID.
	 * 
	 * @param 	int 	$bid
	 *
	 * @return 	self
	 **/
	public function setBid($bid)
	{
		$this->bid = (int)$bid;

		return $this;
	}

	/**
	 * Alias method.
	 * 
	 * @param 	int 	$oid
	 *
	 * @return 	self
	 **/
	public function setOid($oid)
	{
		return $this->setBid($oid);
	}

	/**
	 * Sets the previous order array to calculate what has changed
	 * in the order after the modification.
	 * 
	 * @param 	array 	$order
	 *
	 * @return 	self
	 **/
	public function setPrevOrder($order)
	{
		if (is_array($order)) {
			$this->prevOrder = $order;
		}

		return $this;
	}

	/**
	 * Alias method.
	 * 
	 * @param 	array 	$order
	 *
	 * @return 	self
	 */
	public function setPrevBooking($order)
	{
		return $this->setPrevOrder($order);
	}

	/**
	 * Sets extra data for the current history log.
	 * 
	 * @param 	mixed 	$data 	array, object or string of extra data.
	 * 							Useful to store the amount paid to be invoiced
	 * 							or the transaction details of the payments.
	 *
	 * @return 	self
	 **/
	public function setExtraData($data)
	{
		$this->data = $data;

		return $this;
	}

	/**
	 * Checks whether the type for the history record is valid.
	 *
	 * @param 	string 		$type
	 * @param 	[bool] 		$returnit 	if true, the translated value is returned. Otherwise boolean is returned
	 *
	 * @return 	boolean
	 */
	public function validType($type, $returnit = false)
	{
		if ($returnit) {
			return isset($this->typesMap[strtoupper($type)]) ? $this->typesMap[strtoupper($type)] : $type;
		}

		return isset($this->typesMap[strtoupper($type)]);
	}

	/**
	 * Reads the order record.
	 * Returns false in case of failure.
	 *
	 * @param 	mixed 	
	 *
	 * @return 	array
	 */
	protected function getOrderInfo()
	{
		$q = "SELECT * FROM `#__vikrentitems_orders` WHERE `id`=".(int)$this->bid.";";
		$this->dbo->setQuery($q);

		return $this->dbo->loadAssoc() ?: [];
	}

	/**
	 * Stores a new history record for the order.
	 * 
	 * @param 	string 		$type 	the char-type of store we are making for the history
	 * @param 	[string] 	$descr 	the description of this order record (optional)
	 *
	 * @return 	boolean
	 **/
	public function store($type, $descr = '')
	{
		if (is_null($this->bid) || !$this->validType($type)) {
			return false;
		}

		if (!$order_info = $this->getOrderInfo()) {
			return false;
		}

		if (empty($descr) && is_array($this->prevOrder)) {
			// load VRI language
			$lang = JFactory::getLanguage();
			if (defined('JPATH_ADMINISTRATOR')) {
				// j
				$lang->load('com_vikrentitems', JPATH_ADMINISTRATOR, $lang->getTag(), true);
				$lang->load('joomla', JPATH_ADMINISTRATOR, $lang->getTag(), true);
			} else {
				// wp
				$lang->load('com_vikrentitems', VIKRENTITEMS_ADMIN_LANG, $lang->getTag(), true);
			}
			/**
			 * @todo  build $descr by comparing the previour rental order against the new one.
			 */
		}

		/**
		 * Store the extra data for this history log. Useful to store
		 * information about the amount paid and its invoicing status.
		 */
		$extra_data = $this->data;
		if (!is_null($extra_data) && !is_scalar($extra_data)) {
			$extra_data = json_encode($extra_data);
		}

		/**
		 * We do not use JFactory::getDate()->toSql(true) because we
		 * store the time in UTC, then we format it using JHtmlDate.
		 */
		$q = "INSERT INTO `#__vikrentitems_orderhistory` (`idorder`, `dt`, `type`, `descr`, `totpaid`, `total`, `data`) VALUES (".$this->bid.", ".$this->dbo->quote(JFactory::getDate()->toSql()).", ".$this->dbo->quote($type).", ".(empty($descr) ? "NULL" : $this->dbo->quote($descr)).", ".(float)$order_info['totpaid'].", ".(float)$order_info['order_total'].", ".(is_null($extra_data) ? 'NULL' : $this->dbo->quote($extra_data)).");";
		$this->dbo->setQuery($q);
		$this->dbo->execute();
		$rid = $this->dbo->insertid();

		return ((int)$rid > 0);
	}

	/**
	 * Loads all the history records for this order
	 *
	 * @return 	array
	 */
	public function loadHistory()
	{
		if (empty($this->bid)) {
			return [];
		}

		$q = "SELECT * FROM `#__vikrentitems_orderhistory` WHERE `idorder`=".(int)$this->bid." ORDER BY `dt` DESC;";
		$this->dbo->setQuery($q);
		$history = $this->dbo->loadAssocList();

		return $history;
	}

	/**
	 * Checks whether this order has an event of the given type.
	 * 
	 * @param 	string 	$type 	the type of the event.
	 *
	 * @return 	mixed 			last date on success, false otherwise.
	 */
	public function hasEvent($type)
	{
		if (empty($type) || empty($this->bid)) {
			return false;
		}

		$q = "SELECT `dt` FROM `#__vikrentitems_orderhistory` WHERE `idorder`=" . (int)$this->bid . " AND `type`=" . $this->dbo->quote($type) . " ORDER BY `dt` DESC";
		$this->dbo->setQuery($q, 0, 1);
		$last_dt = $this->dbo->loadResult();
		if (is_null($last_dt)) {
			return false;
		}

		return $last_dt;
	}

	/**
	 * Returns a list of records with data defined for the given event type.
	 * Useful to get a list of transactions data for the refund operations.
	 * 
	 * @param 	mixed 		$type 		string or array event type(s).
	 * @param 	callable 	$callvalid 	callback for the data validation.
	 * @param 	bool 		$onlydata 	whether to get just the event data.
	 *
	 * @return 	mixed 					false or array with data records.
	 */
	public function getEventsWithData($type, $callvalid = null, $onlydata = true)
	{
		if (empty($type) || empty($this->bid)) {
			return false;
		}

		if (!is_array($type)) {
			$type = array($type);
		}

		// quote all given types
		$types = array_map(array($this->dbo, 'quote'), $type);

		$q = "SELECT * FROM `#__vikrentitems_orderhistory` WHERE `idorder`=" . (int)$this->bid . " AND `type` IN (" . implode(', ', $types) . ") ORDER BY `dt` ASC;";
		$this->dbo->setQuery($q);
		$events = $this->dbo->loadAssocList();
		if ($events) {
			$datas  = array();
			foreach ($events as $k => $e) {
				$data = json_decode($e['data']);
				if (!empty($data)) {
					$events[$k]['data'] = $data;
				}
				$valid_data = true;
				if (is_callable($callvalid)) {
					$valid_data = call_user_func($callvalid, $events[$k]['data']);
				}
				if ($valid_data) {
					array_push($datas, $events[$k]['data']);
				}
			}

			return $onlydata ? $datas : $events;
		}

		return false;
	}

}
