<?php
/** 
 * @package     VikAppointments
 * @subpackage  core
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2021 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!');

VAPLoader::import('libraries.mail.template');

/**
 * Wrapper used to handle mail notifications for the options with remaining units
 * lower than the specified threshold.
 *
 * @since 1.7.7
 */
class VAPMailTemplateStock implements VAPMailTemplate
{
	/**
	 * The items list.
	 *
	 * @var object[]
	 */
	protected $items;

	/**
	 * An optional template file to use.
	 *
	 * @var string
	 */
	protected $templateFile;

	/**
	 * An array of options.
	 *
	 * @var array
	 */
	protected $options;

	/**
	 * Class constructor.
	 *
	 * @param array  $options  An array of options.
	 */
	public function __construct(array $options = array())
	{
		if (empty($options['lang']))
		{
			// always use default language in case it is not specified
			$options['lang'] = VikAppointments::getDefaultLanguage();
		}

		// register options
		$this->options = $options;

		// load given language to translate template contents
		VikAppointments::loadLanguage($this->options['lang'], JPATH_SITE);

		// fetch the products to notify
		$this->items = $this->getItems($options);
	}

	/**
	 * Finds all the items having low stocks.
	 * 
	 * @param   array     $options  A configuration array.
	 *
	 * @return 	object[]  A list of items.
	 */
	public function getItems(array $options = [])
	{
		if (!is_null($this->items))
		{
			return $this->items;
		}

		$this->items = [];

		$db = \JFactory::getDbo();

		// load all the options that support a limited purchase
		$query = $db->getQuery(true)
			->select($db->qn('o.id', 'optionId'))
			->select($db->qn('o.name', 'optionName'))
			->select($db->qn('o.stock', 'optionStock'))
			->select($db->qn('v.id', 'variationId'))
			->select($db->qn('v.name', 'variationName'))
			->select($db->qn('v.stock', 'variationStock'))
			->select(sprintf('IFNULL(%s, %s) AS %s', $db->qn('v.notify_below'), $db->qn('o.notify_below'), $db->qn('threshold')))
			->from($db->qn('#__vikappointments_option', 'o'))
			->leftjoin($db->qn('#__vikappointments_option_value', 'v') . ' ON ' . $db->qn('o.id') . ' = ' . $db->qn('v.id_option') . ' AND ' . $db->qn('v.stock') . ' = 1');

		if ($this->options['test'] ?? false)
		{
			// take only the specified items
			$start = $options['start'] ?? 0;
			$limit = $options['limit'] ?? 5;

			// prefer the options that support the stock system
			$query->order($db->qn('o.stock') . ' DESC');
			$query->order($db->qn('v.stock') . ' DESC');
		}
		else
		{
			// take only the products that haven't been already notified
			$query->where(sprintf('IFNULL(%s, %s) = 0', $db->qn('v.stock_notified'), $db->qn('o.stock_notified')));
			
			// take only the products that actually support the stock system
			$query->andWhere([
				$db->qn('o.stock') . ' = 1',
				$db->qn('v.stock') . ' = 1',
			], 'OR');

			// load all the products
			$start = 0;
			$limit = null;
		}

		$query->order($db->qn('o.ordering') . ' ASC');
		$query->order($db->qn('v.ordering') . ' ASC');

		$db->setQuery($query, $start, $limit);
		$stockItems = $db->loadObjectList();

		if (!$stockItems)
		{
			// there are no items supporing the stocks
			return $this->items;
		}

		$optionModel = JModelVAP::getInstance('option');

		foreach ($stockItems as $item)
		{
			// calculate the actual remaining number of units
			$item->remaining = $optionModel->getStock($item->optionId, $item->variationId);

			if ($item->remaining >= $item->threshold && empty($this->options['test']))
			{
				continue;
			}

			$this->items[] = $item;
		}
			
		// get translator
		$translator = VAPFactory::getTranslator();

		$option_ids = [];
		$optvar_ids = [];

		foreach ($this->items as $item)
		{
			$option_ids[] = $item->optionId;

			if ($item->variationId)
			{
				$optvar_ids[] = $item->variationId;
			}
		}

		// pre-load options translations
		$optLang = $translator->load('option', array_unique($option_ids), $this->options['lang']);
		// pre-load options variations translations
		$varLang = $translator->load('optionvar', array_unique($optvar_ids), $this->options['lang']);

		foreach ($this->items as $item)
		{
			// translate option name for the given language
			$opt_tx = $optLang->getTranslation($item->optionId, $this->options['lang']);

			if ($opt_tx)
			{
				$item->optionName = $opt_tx->name;
			}

			if ($item->variationId > 0)
			{
				// translate variation name for the given language
				$var_tx = $varLang->getTranslation($item->variationId, $this->options['lang']);

				if ($var_tx)
				{
					$item->variationName = $var_tx->name;
				}
			}

			$item->fullName = $item->optionName . ($item->variationName ? ' - ' . $item->variationName : '');
		}

		return $this->items;
	}

	/**
	 * Returns the code of the template before being parsed.
	 *
	 * @param 	string  $file  An optional template file to use. If not specified,
	 *                         the one set in configuration will be used.
	 *
	 * @return 	void
	 */
	public function setFile($file)
	{
		// use specified template file
		$this->templateFile = $file;

		// check if a filename or a path was passed
		if ($file && !is_file($file))
		{
			// make sure we have a valid file path
			$this->templateFile = VAPHELPERS . DIRECTORY_SEPARATOR . 'mail_tmpls' . DIRECTORY_SEPARATOR . $file;
		}
	}

	/**
	 * Returns the code of the template before being parsed.
	 *
	 * @return 	string
	 */
	public function getTemplate()
	{
		// copy items details in a local variable for being used directly
		// within the template file
		$items = $this->items;

		if (!$this->templateFile)
		{
			// get template file from configuration
			$file = VAPFactory::getConfig()->get('stockmailtmpl');

			// build template path
			$this->templateFile = VAPHELPERS . DIRECTORY_SEPARATOR . 'mail_tmpls' . DIRECTORY_SEPARATOR . $file;
		}

		// make sure the file exists
		if (!is_file($this->templateFile))
		{
			// missing file, return empty string
			return '';
		}

		// start output buffering 
		ob_start();
		// include file to catch its contents
		include $this->templateFile;
		// write template contents within a variable
		$content = ob_get_contents();
		// clear output buffer
		ob_end_clean();

		// free space
		unset($items);

		return $content;
	}

	/**
	 * Fetches the subject to be used in the e-mail.
	 *
	 * @return 	string
	 */
	public function getSubject()
	{
		// get company name
		$fromname = VAPFactory::getConfig()->getString('agencyname');

		/**
		 * Fetch subject.
		 * 
		 * @since 1.7.7  Added the possibility to use a custom subject.
		 */
		$subject = !empty($this->options['subject']) ? $this->options['subject'] : 'VAPLOWSTOCKEMAILSUBJECT';
		$subject = JText::sprintf($subject, $fromname);

		// let plugins manipulate the subject for this e-mail template
		$res = VAPMailFactory::letPluginsManipulateMail('stock', 'subject', $subject, $this->items);

		if ($res === false)
		{
			// a plugin prevented the e-mail sending
			return '';
		}

		return $subject;
	}

	/**
	 * Parses the HTML of the template and returns it.
	 *
	 * @return 	string
	 */
	public function getHtml()
	{
		$config = VAPFactory::getConfig();

		// load template HTML
		$tmpl = $this->getTemplate();

		// fetch company logo image
		$logo_str = $config->get('companylogo');

		if ($logo_str && is_file(VAPMEDIA . DIRECTORY_SEPARATOR . $logo_str))
		{
			$logo_str = JHtml::fetch('vaphtml.media.display', $logo_str, [
				'alt'   => $config->get('agencyname'),
				'small' => false,
				'style' => 'max-width: 150px;',
			]);
		}
		else
		{
			$logo_str = '';
		}

		// build placeholders lookup
		$placeholders = array(
			'logo'           => $logo_str,
			'company_name'   => $config->get('agencyname'),
			'stocks_content' => \JText::translate('VAPLOWSTOCKCONTENT'),
		    'stocks_help'    => \JText::translate('VAPLOWSTOCKHELP'),
		);

		if ($this->options['test'] ?? false)
		{
			$placeholders['stocks_content'] .= '<p><small><em>The following items are displayed for test purposes only.</em></small></p>';
		}

		// parse e-mail template placeholders
		foreach ($placeholders as $tag => $value)
		{
			$tmpl = str_replace("{{$tag}}", $value, $tmpl);
		}

		// let plugins manipulate the content for this e-mail template
		$res = VAPMailFactory::letPluginsManipulateMail('stock', 'content', $tmpl, $this->items);

		if ($res === false)
		{
			// a plugin prevented the e-mail sending
			return '';
		}

		return $tmpl;
	}

	/**
	 * Sends the HTML contents via e-mail.
	 *
	 * @return 	boolean
	 */
	public function send()
	{
		$config = VAPFactory::getConfig();

		// get administrators e-mail
		$adminmails = VikAppointments::getAdminMailList();
		// get sender e-mail address
		$sendermail = VikAppointments::getSenderMail();
		// get company name
		$fromname = $config->getString('agencyname');
		
		// fetch subject
		$subject = $this->getSubject();
			
		// parse e-mail template
		$html = $this->getHtml();

		if (empty($subject) || empty($html))
		{
			// do not send e-mail in case the subject or
			// the content are empty
			return false;
		}

		$sent = false;

		foreach ($adminmails as $recipient)
		{
			// send the e-mail notification
			$sent = VAPApplication::getInstance()->sendMail(
				$sendermail,
				$fromname,
				$recipient,
				$recipient,
				$subject,
				$html
			) || $sent;
		}

		return $sent;
	}

	/**
	 * Checks whether the notification should be sent.
	 *
	 * @return 	boolean
	 */
	public function shouldSend()
	{
		// make sure there is at least a product to notify
		return (bool) $this->items;
	}
}
