<?php
/**
 * @package   DPCalendar
 * @copyright Copyright (C) 2014 Digital Peak GmbH. <https://www.digital-peak.com>
 * @license   https://www.gnu.org/licenses/gpl-3.0.html GNU/GPL
 */

namespace DigitalPeak\Component\DPCalendar\Site\Model;

\defined('_JEXEC') or die();

use DigitalPeak\Component\DPCalendar\Administrator\Helper\DPCalendarHelper;
use DigitalPeak\Component\DPCalendar\Administrator\Mail\MustacheMailTemplate;
use DigitalPeak\Component\DPCalendar\Administrator\Model\EventModel;
use Joomla\CMS\Access\Access;
use Joomla\CMS\Application\CMSWebApplicationInterface;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\SubformField;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Uri\Uri;

class FormModel extends EventModel
{
	/**
	 * Invites the given users or groups to the event with the given id.
	 */
	public function invite(int $eventId, array $userIds, array $groups): bool
	{
		foreach ($groups as $groupId) {
			$userIds = array_merge($userIds, Access::getUsersByGroup($groupId));
		}
		$event = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Event', 'Site')->getItem($eventId);

		if (!$event || !$event->params->get('access-invite')) {
			throw new \Exception('COM_DPCALENDAR_ALERT_NO_AUTH');
		}

		foreach (array_unique($userIds) as $uid) {
			$bookingModel = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Booking', 'Administrator', ['ignore_request' => true]);
			$ticketsModel = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Tickets', 'Administrator', ['ignore_request' => true]);

			$u = $this->getUserFactory()->loadUserById($uid);
			if ($u->guest !== 0) {
				continue;
			}

			// Don't send an invitation when the user already has a ticket
			$ticketsModel->setState('filter.ticket_holder', $u->id);
			if ($ticketsModel->getItems()) {
				continue;
			}

			$amount = [];
			if ($event->prices) {
				foreach ($event->prices as $key => $price) {
					$key               = preg_replace('/\D/', '', (string)$key);
					$amount[(int)$key] = 1;
				}
			} else {
				$amount[0] = 1;
			}

			$bookingModel->save(
				[
					'event_id' => [$event->id => ['tickets' => $amount]],
					'name'     => $u->name,
					'email'    => $u->email,
					'user_id'  => $u->id,
					'country'  => 0,
					'state'    => 5
				],
				true
			);
		}

		return true;
	}

	/**
	 * Sends a mail to the ticket holders. If the ticket holders are an array with one element
	 * and the value -1, then the mail is sent to the currently logged in user.
	 */
	public function mailtickets(int $eventId, string $subject, string $body, array $ticketIds): bool
	{
		$event = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Event', 'Site')->getItem($eventId);
		if (!$event || !$event->params->get('send-tickets-mail')) {
			throw new \Exception('COM_DPCALENDAR_ALERT_NO_AUTH');
		}

		$app = Factory::getApplication();

		// Flag if only the author should get a mail
		$onlyAuthor = $ticketIds === [-1];

		if ($app instanceof CMSWebApplicationInterface) {
			$app->setUserState('com_dpcalendar.form.event.mailticketsdata.subject', $subject);
			$app->setUserState('com_dpcalendar.form.event.mailticketsdata.message', $body);
		}

		$subject = DPCalendarHelper::renderEvents([$event], $subject);
		$body    = DPCalendarHelper::renderEvents([$event], $body);

		$mailer = new MustacheMailTemplate('event.ticketholders', ['events' => [$event], 'subject' => $subject, 'body' => $body]);
		$mailer->setReplyTo($this->getCurrentUser()->email);
		foreach ($event->tickets as $ticket) {
			if ($ticketIds && !\in_array($ticket->id, $ticketIds) && !$onlyAuthor) {
				continue;
			}

			$mailer->setRecipient($onlyAuthor ? $this->getCurrentUser()->email : $ticket->email);
			$mailer->send();

			if ($onlyAuthor) {
				break;
			}
		}

		return true;
	}

	public function getReturnPage(): string
	{
		return base64_encode((string)$this->getState('return_page', ''));
	}

	protected function populateState()
	{
		$app = Factory::getApplication();

		// Load state from the request.
		$pk = $app->getInput()->getString('e_id', '');
		$this->setState('event.id', $pk);

		// Add compatibility variable for default naming conventions.
		$this->setState('form.id', $pk);

		$categoryId = $app->getInput()->getString('catid', '');
		$this->setState('event.catid', $categoryId);

		$return = $app->getInput()->get('return', null, 'base64');
		if (!$return || !Uri::isInternal(base64_decode((string)$return))) {
			$return = null;
		}

		$this->setState('return_page', $return ? base64_decode((string)$return) : null);

		$params = $app instanceof SiteApplication ? $app->getParams() : ComponentHelper::getParams('com_dpcalendar');
		if (!$params->get('event_form_fields_order_')) {
			$params->set('event_form_fields_order_', DPCalendarHelper::getComponentParameter('event_form_fields_order_', new \stdClass()));
		}
		if (!$params->get('event_form_prices')) {
			$params->set('event_form_prices', DPCalendarHelper::getComponentParameter('event_form_prices', new \stdClass()));
		}

		$this->setState('params', $params);

		$this->setState('layout', $app->getInput()->getCmd('layout'));
	}

	public function getForm($data = [], $loadData = true)
	{
		Form::addFormPath(JPATH_ADMINISTRATOR . '/components/com_dpcalendar/forms');

		return parent::getForm($data, $loadData);
	}

	protected function preprocessForm(Form $form, $data, $group = 'content')
	{
		$app = Factory::getApplication();

		parent::preprocessForm($form, $data, $group);
		$form->setFieldAttribute('user_id', 'type', 'hidden');

		$params = $this->getState('params');
		if (!$params && $app instanceof SiteApplication) {
			$params = $app->getParams();
		}

		$form->setFieldAttribute('start_date', 'format', $params->get('event_form_date_format', 'd.m.Y'));
		$form->setFieldAttribute('start_date', 'formatTime', $params->get('event_form_time_format', 'H:i'));
		$form->setFieldAttribute('end_date', 'format', $params->get('event_form_date_format', 'd.m.Y'));
		$form->setFieldAttribute('end_date', 'formatTime', $params->get('event_form_time_format', 'H:i'));
		$form->setFieldAttribute('scheduling_end_date', 'format', $params->get('event_form_date_format', 'd.m.Y'));
		$form->setFieldAttribute('xreference', 'readonly', true);

		// Set the date format on existing subforms
		$exdates = $form->getField('exdates');
		if ($exdates instanceof SubformField) {
			$exdates->__get('input');
			$exdates->loadSubForm()->setFieldAttribute('date', 'format', $params->get('event_form_date_format', 'd.m.Y'));
			foreach (array_keys(array_filter((array)$exdates->__get('value'))) as $index => $key) {
				// Set it per value
				try {
					// @phpstan-ignore-next-line
					$subForm = Form::getInstance('subform.' . $key, '');
					$subForm->setFieldAttribute('date', 'format', $params->get('event_form_date_format', 'd.m.Y'));
				} catch (\InvalidArgumentException) {
					// Ignore as it can happen when an exdate is removed or index has changed
				}

				// Set it per index
				try {
					// @phpstan-ignore-next-line
					$subForm = Form::getInstance('subform.exdates' . $index, '');
					$subForm->setFieldAttribute('date', 'format', $params->get('event_form_date_format', 'd.m.Y'));
				} catch (\InvalidArgumentException) {
					// Ignore as it can happen when an exdate is removed or index has changed
				}
			}
		}

		$renderer = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Layout', 'Administrator');
		$form->setFieldAttribute(
			'host_ids',
			'query',
			$renderer->renderLayout('form.hosts', ['field_name' => 'hosts', 'form' => $form])
		);

		// User field doesn't work on front
		if ($app instanceof SiteApplication) {
			$form->setFieldAttribute('created_by', 'type', 'sql');
			$form->setFieldAttribute('created_by', 'class', 'dp-select');
			$form->setFieldAttribute('created_by', 'key_field', 'value');
			$form->setFieldAttribute('created_by', 'value_field', 'text');
			$form->setFieldAttribute(
				'created_by',
				'query',
				$renderer->renderLayout('form.author', ['field_name' => 'created_by', 'form' => $form])
			);
			$form->setFieldAttribute('modified_by', 'type', 'sql');
			$form->setFieldAttribute('modified_by', 'class', 'dp-select');
			$form->setFieldAttribute('modified_by', 'key_field', 'value');
			$form->setFieldAttribute('modified_by', 'value_field', 'text');
			$form->setFieldAttribute(
				'modified_by',
				'query',
				$renderer->renderLayout('form.author', ['field_name' => 'modified_by', 'form' => $form])
			);
		}
	}

	public function checkout($pk = null)
	{
		if ($this->getCurrentUser()->id === 0) {
			$table = $this->getTable();

			if (!$table->load($pk)) {
				return false;
			}

			return $this->getCurrentUser()->authorise('core.edit', 'com_dpcalendar.category.' . $table->catid);
		}

		return parent::checkout($pk);
	}
}
