<?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\Administrator\Model;

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

use DigitalPeak\Component\DPCalendar\Administrator\Helper\DPCalendarHelper;
use Joomla\CMS\Application\AdministratorApplication;
use Joomla\CMS\Application\CMSWebApplicationInterface;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\QueryInterface;
use Joomla\Utilities\ArrayHelper;

class BookingsModel extends ListModel
{
	public function __construct($config = [], ?MVCFactoryInterface $factory = null)
	{
		if (empty($config['filter_fields'])) {
			$config['filter_fields'] = [
				'id',
				'a.id',
				'a.uid',
				'name',
				'a.name',
				'state',
				'a.state',
				'book_date',
				'a.book_date',
				'user_name',
				'created_by',
				'a.created_by',
				'event_id',
				'a.event_id',
				'a.price',
				'payment_provider'
			];
		}

		parent::__construct($config, $factory);
	}

	protected function populateState($ordering = null, $direction = null)
	{
		// Initialize variables.
		$app = Factory::getApplication();

		$search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search');
		$this->setState('filter.search', $search);

		$published = $this->getUserStateFromRequest($this->context . '.filter.state', 'filter_state', '', 'string');
		$this->setState('filter.state', $published);

		$this->setState('filter.my', 0);

		$params = $app instanceof SiteApplication ? $app->getParams() : ComponentHelper::getParams('com_dpcalendar');
		$this->setState('params', $params);

		parent::populateState('a.book_date', 'desc');

		$format          = $params->get('event_form_date_format', 'd.m.Y');
		$listRequestData = $this->getUserStateFromRequest($this->context . 'list', 'list', null, 'array', false);
		if (empty($listRequestData)) {
			$listRequestData = [];
		}

		// Joomla resets the start and end date
		$search = $listRequestData['date_start'] ?? '';
		if ($search || !$app instanceof AdministratorApplication) {
			try {
				DPCalendarHelper::getDateFromString($search, null, true, $format);
			} catch (\Exception $exception) {
				if ($search) {
					$app->enqueueMessage($exception->getMessage(), 'warning');
				}
				$search                        = '';
				$listRequestData['date_start'] = '';

				if ($app instanceof CMSWebApplicationInterface) {
					$app->setUserState($this->context . '.list', $listRequestData);
				}
			}
			$this->setState('list.date_start', $search);
		}

		$search = $listRequestData['date_end'] ?? '';
		if ($search) {
			try {
				DPCalendarHelper::getDateFromString($search, null, true, $format);
			} catch (\Exception $e) {
				$app->enqueueMessage($e->getMessage(), 'warning');
				$search = '';

				$listRequestData['date_end'] = '';

				if ($app instanceof CMSWebApplicationInterface) {
					$app->setUserState($this->context . '.list', $listRequestData);
				}
			}
		}
		$this->setState('list.date_end', $search);
	}

	protected function _getList($query, $limitstart = 0, $limit = 0)
	{
		$items = parent::_getList($query, $limitstart, $limit);

		if (!$items) {
			return $items;
		}

		/** @var \stdClass $item */
		foreach ($items as $item) {
			$ticketsModel = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Tickets', 'Administrator', ['ignore_request' => true]);
			$ticketsModel->setState('filter.booking_id', $item->id);
			$ticketsModel->setState('list.limit', 10000);
			$item->tickets = $ticketsModel->getItems();

			// @deprecated
			$item->processor = $item->payment_provider;

			if (!$item->country) {
				continue;
			}

			$country = $this->bootComponent('dpcalendar')->getMVCFactory()->createModel('Country', 'Administrator', ['ignore_request' => true])->getItem($item->country);
			if ($country) {
				Factory::getApplication()->getLanguage()->load(
					'com_dpcalendar.countries',
					JPATH_ADMINISTRATOR . '/components/com_dpcalendar'
				);
				$item->country_code       = $country->short_code;
				$item->country_code_value = Text::_('COM_DPCALENDAR_COUNTRY_' . $country->short_code);
			}

			if ($item->price == '0.00') {
				$item->price = 0;
			}
		}

		return $items;
	}

	protected function getListQuery()
	{
		$user = $this->getCurrentUser();

		$db    = $this->getDatabase();
		$query = $db->getQuery(true);

		$query->select($this->getState('list.select', 'a.*'));
		$query->from('#__dpcalendar_bookings AS a');

		// Join over the tickets
		$query->select('count(t.id) as amount_tickets');
		$query->join('LEFT', '#__dpcalendar_tickets AS t ON t.booking_id = a.id');

		// Join over the users for the author.
		$query->select('ua.name AS user_name');
		$query->join('LEFT', '#__users AS ua ON ua.id = a.user_id');

		$search = $this->getState('filter.search');
		if (!empty($search)) {
			if (stripos((string)$search, 'id:') === 0) {
				$query->where('a.id = ' . (int)substr((string)$search, 3));
			} elseif (stripos((string)$search, 'author:') === 0) {
				$search = $db->quote('%' . $db->escape(substr((string)$search, 7), true) . '%');
				$query->where('(a.name LIKE ' . $search . ' OR ua.name LIKE ' . $search . ' OR ua.username LIKE ' . $search . ')');
			} else {
				$search = $db->quote('%' . $db->escape($search, true) . '%');
				$query->where('(a.name LIKE ' . $search . ' OR a.email LIKE ' . $search . ' OR a.uid LIKE ' . $search . ')');
			}
		}

		// Filter by published state
		$published = $this->getState('filter.state');
		if (is_numeric($published)) {
			$query->where('a.state = ' . (int)$published);
		} elseif (\is_array($published)) {
			$query->where('a.state IN (' . implode(',', ArrayHelper::toInteger($published)) . ')');
		} elseif ($published === '') {
			$query->where('a.state IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 10)');
		}

		// Filter by author
		$authorId = $this->getState('filter.created_by');
		if (is_numeric($authorId)) {
			$type = $this->getState('filter.created_by.include', true) ? '= ' : '<>';
			$query->where('a.user_id ' . $type . (int)$authorId);
		}

		$eventId = $this->getState('filter.event_id');
		if ($eventId && is_numeric($eventId)) {
			$eventId = [$eventId];
		}
		if (\is_array($eventId)) {
			$eventId = ArrayHelper::toInteger($eventId);

			// Also search in original events and instances
			$this->getDatabase()->setQuery(
				'select id,original_id from #__dpcalendar_events where (id in (' . implode(',', $eventId) . ') and original_id > 0) or original_id in (' . implode(',', $eventId) . ')'
			);
			foreach ($this->getDatabase()->loadObjectList() as $e) {
				$id = 0;
				if ($e->original_id > 0 && \in_array($e->id, $eventId)) {
					$id = $e->original_id;
				}

				if ($e->id > 0 && \in_array($e->original_id, $eventId)) {
					$id = $e->id;
				}

				if (!$id) {
					continue;
				}

				$eventId[] = $id;

				if (Associations::isEnabled()) {
					$associations = Associations::getAssociations('com_dpcalendar', '#__dpcalendar_events', 'com_dpcalendar.item', $id);
					$eventId      = array_merge($eventId, array_column($associations, 'id'));
				}
			}

			$query->where('t.event_id in (' . implode(',', array_unique(ArrayHelper::toInteger($eventId))) . ')');
		}

		// Access rights
		if ($user->guest !== 0) {
			// Don't allow to list bookings as guest
			$query->where('1 > 1');
		}

		if ($this->getState('filter.my', 0) == 1) {
			$query->where('a.user_id = ' . (int)$user->id);
		}

		if ($payment_provider = $this->getState('filter.payment_provider')) {
			$query->where('a.payment_provider like ' . $this->getDatabase()->quote($payment_provider . '%'));
		}

		$catId = 0;
		// Get the category id for access check
		if ($this->getState('filter.event_id')) {
			$this->getDatabase()->setQuery(
				'select catid from #__dpcalendar_events where id = ' . (int)$this->getState('filter.event_id')
			);
			$catId = $this->getDatabase()->loadRow();

			if ($catId) {
				$catId = $catId[0];
			}
		}

		// On front end if we are not an admin only bookings are visible where we are the author of the event
		if ($this->getState('filter.my', 0) != 1
			&& Factory::getApplication() instanceof SiteApplication
			&& !$user->authorise('dpcalendar.admin.book', 'com_dpcalendar' . ($catId ? '.category.' . $catId : ''))
			&& $this->getState('ignore.access') !== true
		) {
			// Join over the events
			$query->join('LEFT', '#__dpcalendar_events AS e ON e.id = t.event_id');
			$query->where('e.created_by = ' . (int)$user->id);
		}

		$search = $this->getState('list.date_start');
		if (!empty($search)) {
			$search = DPCalendarHelper::getDateFromString(
				$search,
				null,
				true,
				DPCalendarHelper::getComponentParameter('event_form_date_format', 'd.m.Y')
			);
			$search->setTime(0, 0);
			$search = $db->quote($db->escape($search->toSql(), true));
			$query->where('a.book_date >= ' . $search);
		}

		$search = $this->getState('list.date_end');
		if (!empty($search)) {
			$search = DPCalendarHelper::getDateFromString(
				$search,
				null,
				true,
				DPCalendarHelper::getComponentParameter('event_form_date_format', 'd.m.Y')
			);
			$search = $db->quote($db->escape($search->toSql(), true));
			$query->where('a.book_date <= ' . $search);
		}

		// Add the list ordering clause.
		$query->order($db->escape($this->getState('list.ordering', 'a.book_date')) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));

		$query->group('a.id');

		return $query;
	}

	protected function _getListCount($query): int
	{
		if ($query instanceof QueryInterface) {
			$query = clone $query;
			$query->clear('select')
				->clear('order')
				->clear('limit')
				->clear('offset')
				->clear('group')
				->select('COUNT(distinct a.id)');
		}

		$this->getDatabase()->setQuery($query);

		return (int)$this->getDatabase()->loadResult();
	}

	public function updateFirstNameFromField(int $fieldId): int
	{
		$values = $this->getDatabase()
			->setQuery("select * from #__fields_values where value > '' and field_id = " . $fieldId)->loadObjectList();
		foreach ($values as $value) {
			$this->getDatabase()->setQuery(
				"update #__dpcalendar_bookings set name = trim(concat(COALESCE(first_name, ''), ' ', name)), first_name = " . $this->getDatabase()->quote($value->value)
				. ' where id = ' . $value->item_id
			)->execute();
			$this->getDatabase()->setQuery('delete from #__fields_values where field_id = ' . $value->field_id . ' and item_id = ' . $value->item_id)->execute();
		}

		return \count($values);
	}
}
