<?php
/** 
 * @package     VikBooking
 * @subpackage  core
 * @author      Alessio Gaggii - E4J s.r.l.
 * @copyright   Copyright (C) 2022 E4J s.r.l. All Rights Reserved.
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 * @link        https://vikwp.com
 */

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

/**
 * Browser notifications scheduler handler.
 * 
 * @since 	1.15.0 (J) - 1.5.0 (WP)
 */
final class VBONotificationScheduler
{
	/**
	 * @var  mixed
	 */
	private $data = null;

	/**
	 * @var  JDocument
	 */
	private $doc = null;

	/**
	 * Proxy to immediately access the object.
	 * 
	 * @param 	mixed 		$data 		optional data to set.
	 * @param 	JDocument 	$doc 	optional document object.
	 * 
	 * @return 	self
	 */
	public static function getInstance($data = null, $doc = null)
	{
		return new static($data, $doc);
	}

	/**
	 * Class constructor.
	 * 
	 * @param 	mixed 	$data
	 */
	public function __construct($data = null, $doc = null)
	{
		// bind data
		$this->data = $data;

		// set the document object
		if (!$doc) {
			$doc = JFactory::getDocument();
		}
		$this->doc = $doc;
	}

	/**
	 * Given a reminder record object, composes the notification
	 * data object to schedule a browser notification.
	 * 
	 * @param 	object 	$reminder 			the reminder record object.
	 * @param 	int 	$max_future_hours 	optional value to check if the reminder
	 * 										is too far ahead in the future.
	 * 
	 * @return 	null|object
	 */
	public function getReminderDataObject($reminder, $max_future_hours = 0)
	{
		if (!is_object($reminder) || empty($reminder->id) || empty($reminder->duedate)) {
			return null;
		}

		if ($max_future_hours > 0) {
			// this reminder requires a validation of the future time
			$now_info = getdate();
			$next_hours = ($now_info['hours'] + $max_future_hours);
			$max_future_ts = mktime($next_hours, $now_info['minutes'], 0, $now_info['mon'], $now_info['mday'], $now_info['year']);
			$scheduled_ts = strtotime($reminder->duedate);
			if ($scheduled_ts > $max_future_ts) {
				// no need to schedule a notification for this reminder as it's too far ahead
				return null;
			}
		}

		// get a new notification data object
		$notification = VBONotificationDataReminder::getInstance();

		// immediately parse the reminder record to check its validity and importance
		$notification->parseReminderImportance($reminder);

		// set data
		$notification->setReminderId($reminder->id);
		$notification->setDateTime($reminder->duedate);
		if (!$reminder->usetime) {
			$notification->setNoTime(true);
		}

		return $notification->toDataObject();
	}

	/**
	 * Builds a notification display data object for a recent history
	 * event involving a reservation. This kind of notification will
	 * be actually dispatched immediately with no scheduling, and so
	 * so it won't provide a build-URL, it will provide display data.
	 * 
	 * @param 	object 	$h_event 	history record object to parse.
	 * 
	 * @return 	null|object 
	 */
	public function getHistoryDataObject($h_event)
	{
		if (!is_object($h_event) || empty($h_event->idorder)) {
			return null;
		}

		// get a new notification data object
		$notification = VBONotificationDataHistory::getInstance();

		// inject all record properties as "reserved" properties
		foreach ($h_event as $prop => $val) {
			// properties starting with an underscore are "reserved"
			$notification->set("_{$prop}", $val);
		}

		// the notification will be dispatched immediately
		$notification->setDateTime('now');

		// let the notification build the display data
		$notification->buildDisplayData();

		// return the notification data-object
		return $notification->toDataObject();
	}

	/**
	 * Given a list of recent worthy-of-notification history events,
	 * builds a list of notification data objects of type "history".
	 * Such notifications will be actually dispatched immediately,
	 * with no needs to schedule a notification timer. Moreover,
	 * these notifications will contain immediately the necessary
	 * display data without needing to build them through a callback.
	 * 
	 * @param 	array 	$events 	list of recent history record objects.
	 * 
	 * @return 	array 				list of browser notification display data objects.
	 */
	public function buildHistoryDataObjects($events = [])
	{
		if (!is_array($events) || !$events) {
			return [];
		}

		// container of notification display data objects
		$data_objects = [];

		foreach ($events as $history) {
			$data_object = $this->getHistoryDataObject($history);
			if ($data_object) {
				$data_objects[] = $data_object;
			}
		}

		return $data_objects;
	}

	/**
	 * Builds a notification display data object for a recent guest
	 * review involving a reservation. This kind of notification will
	 * be actually dispatched immediately with no scheduling, and so
	 * so it won't provide a build-URL, it will provide display data.
	 * 
	 * @param 	object 	$review 	guest activity record object to parse.
	 * 
	 * @return 	null|object 
	 */
	public function getReviewDataObject($review)
	{
		if (!is_object($review) || empty($review->id_review) || empty($review->idorder)) {
			return null;
		}

		// get a new notification data object
		$notification = VBONotificationDataReview::getInstance();

		// inject all record properties as "reserved" properties
		foreach ($review as $prop => $val) {
			// properties starting with an underscore are "reserved"
			$notification->set("_{$prop}", $val);
		}

		// the notification will be dispatched immediately
		$notification->setDateTime('now');

		// let the notification build the display data
		$notification->buildDisplayData();

		// return the notification data-object
		return $notification->toDataObject();
	}

	/**
	 * Given a list of recent guest activity records, builds a list
	 * of notification data objects of type "review".
	 * Such notifications will be actually dispatched immediately,
	 * with no needs to schedule a notification timer. Moreover,
	 * these notifications will contain immediately the necessary
	 * display data without needing to build them through a callback.
	 * 
	 * @param 	array 	$reviews 	list of recent guest activity record objects.
	 * 
	 * @return 	array 				list of browser notification display data objects.
	 */
	public function buildReviewDataObjects($reviews = [])
	{
		if (!is_array($reviews) || !$reviews) {
			return [];
		}

		// container of notification display data objects
		$data_objects = [];

		foreach ($reviews as $review) {
			$data_object = $this->getReviewDataObject($review);
			if ($data_object) {
				$data_objects[] = $data_object;
			}
		}

		return $data_objects;
	}

	/**
	 * Builds a notification display data object for a recent guest
	 * message involving a reservation. This kind of notification will
	 * be actually dispatched immediately with no scheduling, and so
	 * so it won't provide a build-URL, it will provide display data.
	 * 
	 * @param 	object 	$message 	guest activity record object to parse.
	 * 
	 * @return 	null|object 
	 */
	public function getGuestMessageDataObject($message)
	{
		if (!is_object($message) || empty($message->idorder)) {
			return null;
		}

		// get a new notification data object
		$notification = VBONotificationDataMessage::getInstance();

		// inject all record properties as "reserved" properties
		foreach ($message as $prop => $val) {
			// properties starting with an underscore are "reserved"
			$notification->set("_{$prop}", $val);
		}

		// the notification will be dispatched immediately
		$notification->setDateTime('now');

		// let the notification build the display data
		$notification->buildDisplayData();

		// return the notification data-object
		return $notification->toDataObject();
	}

	/**
	 * Given a list of recent guest activity records, builds a list
	 * of notification data objects of type (guest) "message".
	 * Such notifications will be actually dispatched immediately,
	 * with no needs to schedule a notification timer. Moreover,
	 * these notifications will contain immediately the necessary
	 * display data without needing to build them through a callback.
	 * 
	 * @param 	array 	$messages 	list of recent guest activity record objects.
	 * 
	 * @return 	array 				list of browser notification display data objects.
	 */
	public function buildGuestMessageDataObjects($messages = [])
	{
		if (!is_array($messages) || !$messages) {
			return [];
		}

		// container of notification display data objects
		$data_objects = [];

		foreach ($messages as $message) {
			$data_object = $this->getGuestMessageDataObject($message);
			if ($data_object) {
				$data_objects[] = $data_object;
			}
		}

		return $data_objects;
	}

	/**
	 * Builds a notification display data object for a recent operator
	 * chat message for an administrator. This kind of notification will
	 * be actually dispatched immediately with no scheduling, and so
	 * so it won't provide a build-URL, it will provide display data.
	 * 
	 * @param 	object 	$message 	Operator chat message record object to parse.
	 * 
	 * @return 	?object
	 * 
	 * @since  	1.18.0 (J) - 1.8.0 (WP)
	 */
	public function getOperatorsChatDataObject($message)
	{
		if (!$message instanceof VBOChatMessage) {
			return null;
		}

		// get a new notification data object
		$notification = VBONotificationDataOperatormessage::getInstance();

		// inject all record properties as "reserved" properties
		foreach ($message->jsonSerialize() as $prop => $val) {
			// properties starting with an underscore are "reserved"
			$notification->set("_{$prop}", $val);
		}

		// the notification will be dispatched immediately
		$notification->setDateTime('now');

		// let the notification build the display data
		$notification->buildDisplayData();

		// return the notification data-object
		return $notification->toDataObject();
	}

	/**
	 * Given a list of recent operators chat messages for the administrators,
	 * builds a list of notification data objects of type (operator) "message".
	 * Such notifications will be immediately dispatched, and they will contain
	 * the necessary data for display without being built through a callback.
	 * 
	 * @param 	array 	$messages 	List of recent operators chat message objects for the admin.
	 * 
	 * @return 	array 				List of browser notification display data objects.
	 * 
	 * @since 	1.18.0 (J) - 1.8.0 (WP)
	 */
	public function buildOperatorsChatDataObjects(array $messages)
	{
		// container of notification display data objects
		$data_objects = [];

		foreach ($messages as $message) {
			if ($data_object = $this->getOperatorsChatDataObject($message)) {
				$data_objects[] = $data_object;
			}
		}

		return $data_objects;
	}

	/**
	 * Enqueues a list of notification objects for the overdue reminders.
	 * This should be called upon a regular page loading, not during AJAX
	 * requests, as it attachs a script declaration to the document.
	 * 
	 * @param 	array 	$reminders 	list of reminder record objects.
	 * 
	 * @return 	void
	 */
	public function enqueueReminders(array $reminders)
	{
		// build the queue of reminders to be scheduled
		$queue = [];

		foreach ($reminders as $reminder) {
			if (empty($reminder->duedate)) {
				continue;
			}

			// compose reminder data object
			$notification = $this->getReminderDataObject($reminder);
			if ($notification) {
				// push reminder to queue
				$queue[] = $notification;
			}
		}

		if (!$queue) {
			return;
		}

		$js_queue = json_encode($queue);

		$this->doc->addScriptDeclaration(
<<<JS
jQuery(function() {
	setTimeout(() => {
		VBOCore.enqueueNotifications($js_queue);
	}, 300);
});
JS
		);
	}

	/**
	 * Registers a list of widgets watching data.
	 * 
	 * @param 	array  $watch_data  associative list of widgets
	 * 								and preloaded watch data.
	 * 
	 * @return 	void
	 */
	public function registerWatchData($watch_data = [])
	{
		if (!is_array($watch_data) || !$watch_data) {
			return;
		}

		$js_watch_data = json_encode($watch_data);

		$this->doc->addScriptDeclaration(
<<<JS
jQuery(function() {
	setTimeout(() => {
		VBOCore.registerWatchData($js_watch_data);
	}, 300);
});
JS
		);
	}
}
