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

namespace E4J\VikRestaurants\Cron\Jobs\Restaurant;

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

use E4J\VikRestaurants\Cron\CronJob;
use E4J\VikRestaurants\Cron\Installable;
use E4J\VikRestaurants\Cron\Status;
use E4J\VikRestaurants\Cron\Traits\NotificationMailBuilder;
use E4J\VikRestaurants\Cron\Traits\NotificationMailDispatcher;
use E4J\VikRestaurants\Cron\Traits\NotificationMailForm;
use E4J\VikRestaurants\Cron\Traits\ReminderInstallation;
use E4J\VikRestaurants\Cron\Traits\ReservationNotificationTaggableBuilder;

/**
 * This cron job can be used to send a notification e-mail to remind the customers to leave
 * a feedback some days after the check-out of the reservation.
 * 
 * @since 1.10
 */
class ReservationFeedbackMailReminder extends CronJob implements Installable
{
    use NotificationMailBuilder;
    use NotificationMailDispatcher;
    use NotificationMailForm;
    use ReminderInstallation;
    use ReservationNotificationTaggableBuilder;

    /**
     * @inheritDoc
     * 
     * @see CronJob
     */
    protected function execute(\JRegistry $args, Status $status)
    {
        // load all the reservation that should be notified
        $reservations = $this->loadReservations($args);

        if (!$reservations)
        {
            // no reservations retrieved : stop process [OK]
            return $status->appendContent('Process terminated correctly! No reservations to notify.');
        }

        // iterate the reservations one by one
        foreach ($reservations as $resId)
        {
            // load reservation details
            $reservation = \VREOrderFactory::getReservation($resId);

            // register log metadata
            $status->addMetadata(new \E4J\VikRestaurants\Metadata\Addon\RestaurantReservationDetails($resId));

            try
            {
                /**
                 * Skip reservations in case they don't have a configured email address. Otherwise the system will
                 * attempt to notify them at every execution of the cron job.
                 * 
                 * This way we can register a warning under the logs and flag the reservation as tracked.
                 * 
                 * @since 1.11.2
                 */
                if ($reservation->purchaser_mail)
                {
                    /**
                     * Construct a default e-mail template.
                     * 
                     * @see NotificationMailBuilder
                     */
                    $mailTemplate = $this->createMailTemplate($reservation->langtag, $args, $this->getId());

                    /**
                     * Configures the provided template with the current restaurant reservation details.
                     * 
                     * @see ReservationNotificationTaggableBuilder
                     */
                    $this->setupTemplate($mailTemplate, $reservation);
                
                    /**
                     * Attempt to send the notification.
                     * 
                     * @see NotificationMailDispatcher
                     */
                    $this->sendNotification($reservation->purchaser_mail, $mailTemplate);

                    // register successful log
                    $status->appendContent("The reservation #{$reservation->id} has been notified at {$reservation->purchaser_mail}.");
                }
                else
                {
                    // register warning
                    $status->appendContent(sprintf(
                        "<span style=\"color: #f60;\">It was not possible to send a notification to the reservation #%s because the email recipient is empty.</span>",
                        $reservation->id
                    ));
                }

                /**
                 * Set the reservation as notified.
                 * 
                 * @see ReminderInstallation
                 */
                $this->setNotified($this->getId(), $reservation->id);
            }
            catch (\Exception $error)
            {
                // register error log
                $status->setStatus(false)->appendContent(sprintf(
                    "<span style=\"color: #900;\">It was not possible to send a notification to the reservation #%s for the following reason. %s</span>",
                    $reservation->id,
                    $error->getMessage()
                ));
            }
        }
    }

    /**
     * Load all the restaurant reservations that should receive a notification.
     * 
     * @param   \JRegistry  $args
     * 
     * @return  int[]  A list of matching reservation IDs.
     */
    protected function loadReservations(\JRegistry $args)
    {
        $db = \JFactory::getDbo();

        $threshold = strtotime('-' . (int) $args->get('threshold') . ' hours', \VikRestaurants::now());

        // get any approved codes
        $approved = \JHtml::fetch('vrehtml.status.find', 'code', ['restaurant' => 1, 'approved' => 1]); 

        // retrieve reservations to notify
        $query = $db->getQuery(true)
            ->select($db->qn('r.id'))
            ->from($db->qn('#__vikrestaurants_reservation', 'r'))
            // exclude clusters by taking only the parent reservations
            ->where($db->qn('r.id_parent') . ' <= 0')
            ->order($db->qn('r.checkin_ts') . ' ASC');

        /**
         * Extend the query to exclude the reservations that have been already notified.
         * 
         * @see ReminderInstallation
         */
        $this->filterQuery($this->getId(), $query, $db);

        if ($approved)
        {
            // filter by approved status
            $query->where($db->qn('r.status') . ' IN (' . implode(',', array_map(array($db, 'q'), $approved)) . ')');
        }

        /**
         * Take the reservations only when the following condition is verified:
         * (check-in + duration + threshold) >= NOW, which was rebuilt into:
         * check-in + duration <= NOW - threshold
         */
        $query->where(sprintf(
            '%1$s + IF(%2$s > 0, %2$s, %3$d) <= %4$d',
            $db->qn('r.checkin_ts'),
            $db->qn('r.stay_time'),
            \VREFactory::getConfig()->getUint('averagetimestay'),
            $threshold
        ));

        $db->setQuery($query);
        return $db->loadColumn();
    }

    /**
     * @inheritDoc
     * 
     * Supported only in case the restaurant section has been enabled.
     * 
     * @see Runnable
     */
    public function isSupported()
    {
        return \VikRestaurants::isRestaurantEnabled();
    }

    /**
     * @inheritDoc
     * 
     * @see Configurable
     */
    public function getForm()
    {
        // create the default mail template
        $template = "<div>{custom_position_top}</div>\n"
            . "<p>Dear {customer_first_name}</p>\n"
            . "<p>We hope you had a pleasant experience at our restaurant and that our food and service met your expectations.</p>\n"
            . "<div>{custom_position_middle}</div>\n"
            . "<p>We'd love to hear your thoughts so we can continue improving and provide you with an even better experience. Please take a few moments to share your feedback with us. Your opinion is very important to us!</p>\n"
            . "<p>You can leave your feedback by clicking the link below:<br>[INSERT_LINK_HERE]</p>\n"
            . "<div>{custom_position_bottom}</div>\n"
            . "<p>Thank you for choosing us, and we hope to welcome you back soon!</p>\n"
            . "<p>Best regards,<br>{company_name}</p>\n"
            . "<div>{custom_position_footer}</div>";

        $options = [
            'id' => $this->getId(),
            'subject' => \VREFactory::getConfig()->get('restname') . ' - Feedback Request',
            'body' => $template,
            'tags' => [
                'logo',
                'company_name',
                'order_number',
                'order_key',
                'checkin_date',
                'checkin_time',
                'order_people',
                'order_room',
                'order_room_table',
                'customer_full_name',
                'customer_first_name',
            ],
        ];

        return array_merge(
            // custom fields
            [
                'threshold' => [
                    'type' => 'number',
                    'label' => \JText::translate('VRE_CRONJOB_SETTING_SEND_AFTER_HOURS'),
                    'description' => \JText::translate('VRE_CRONJOB_SETTING_SEND_AFTER_HOURS_HELP'),
                    'required' => true,
                    'default' => 24,
                    'min' => 1,
                    'step' => 1,
                ],
            ],
            /**
             * Mail notification default fields.
             * 
             * @see NotificationMailForm
             */
            $this->getFields($options)
        );
    }

    /**
     * @inheritDoc
     * 
     * @see ReminderInstallation
     */
    final protected function getDatabaseTable()
    {
        return '#__vikrestaurants_reservation';
    }
}
