<?php
/**
 * @version        4.3.0
 * @package        Joomla
 * @subpackage     Helpdesk Pro
 * @author         Tuan Pham Ngoc
 * @copyright      Copyright (C) 2013 - 2021 Ossolution Team
 * @license        GNU/GPL, see LICENSE.php
 */

namespace OSSolution\HelpdeskPro\Admin\Model;

use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\User;
use Joomla\CMS\User\UserHelper;
use OSL\Input\Input;
use OSL\Model\AdminModel;
use OSSolution\HelpdeskPro\Site\Helper\Helper as HelpdeskproHelper;
use OSSolution\HelpdeskPro\Site\Helper\Html as HelpdeskproHelperHtml;


defined('_JEXEC') or die;

class Ticket extends AdminModel
{
	/**
	 * Initialize the model, insert extra model state
	 */
	protected function initialize()
	{
		$this->state->insert('ticket_code', 'string', '');
	}

	/**
	 * Override getData method to allow getting ticket details from ticket code
	 *
	 * @return object
	 */
	public function getData()
	{
		if ($this->state->ticket_code && empty($this->state->id))
		{
			$db    = $this->getDbo();
			$query = $db->getQuery(true);
			$query->select('id')
				->from('#__helpdeskpro_tickets')
				->where('ticket_code = ' . $db->quote($this->state->ticket_code));
			$db->setQuery($query);

			$this->state->set('id', (int) $db->loadResult());
		}

		return parent::getData();
	}

	/**
	 * Override loadData method to allow getting more information about the ticket
	 */
	protected function loadData()
	{
		$fieldSuffix = '';

		if ($this->container->app->isClient('site'))
		{
			$fieldSuffix = HelpdeskproHelper::getFieldSuffix();
		}

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

		$query->select('a.*, b.username, c.title' . $fieldSuffix . ' AS category_title')
			->select('s.title' . $fieldSuffix . ' AS `status`, p.title' . $fieldSuffix . ' AS `priority`')
			->from('#__helpdeskpro_tickets AS a')
			->leftJoin('#__users AS b ON a.user_id = b.id')
			->leftJoin('#__helpdeskpro_categories AS c ON a.category_id = c.id')
			->leftJoin('#__helpdeskpro_statuses AS s ON a.status_id = s.id')
			->leftJoin('#__helpdeskpro_priorities AS p ON a.priority_id = p.id')
			->where('a.id = ' . $this->state->id);


		$db->setQuery($query);

		$this->data = $db->loadObject();

		if ($this->state->ticket_code)
		{
			$this->data->is_ticket_code = 1;
		}
		else
		{
			$this->data->is_ticket_code = 0;
		}
	}

	/**
	 * Store the support ticket
	 *
	 * @param   \OSL\Input\Input  $input
	 *
	 * @return bool
	 */
	public function store($input, $ignore = [])
	{
		jimport('joomla.user.helper');

		$db    = $this->getDbo();
		$query = $db->getQuery(true);
		$user  = $this->container->user;

		if ($user->authorise('core.admin', 'com_helpdeskpro'))
		{
			$data = $input->post->getData(Input::INPUT_ALLOWRAW);
		}
		else
		{
			$data = $input->post->getData(Input::INPUT_ALLOWHTML);
		}

		// Basic data filtering
		$data['subject'] = trim($input->getString('subject'));

		// Special case for message, we need to get it from input instead of from $input->post

		$data['message'] = trim($input->getHtml('message'));

		if (isset($data['name']))
		{
			$data['name'] = trim($input->getString('name'));
		}

		if (isset($data['email']))
		{
			$data['email'] = trim($input->getString('email'));
		}

		$config = HelpdeskproHelper::getConfig();

		$allowedFileTypes = explode('|', $config->allowed_file_types);

		for ($i = 0, $n = count($allowedFileTypes); $i < $n; $i++)
		{
			$allowedFileTypes[$i] = trim(strtoupper($allowedFileTypes[$i]));
		}

		$row = $this->getTable();
		$row->bind($data);

		if ($user->get('id') && !isset($data['name']))
		{
			$row->name    = $user->get('name');
			$row->email   = $user->get('email');
			$row->user_id = $user->get('id');
		}
		else
		{
			$query->select('id')
				->from('#__users')
				->where('email = ' . $db->quote($data['email']));
			$db->setQuery($query);
			$row->user_id = $db->loadResult();

			$role = HelpdeskproHelper::getUserRole();

			if ($role == 'staff')
			{
				$row->staff_id = $user->id;
			}
		}

		$uploadedFiles = $this->storeAttachment($input, $allowedFileTypes);

		if (count($uploadedFiles['names']))
		{
			$row->attachments        = implode('|', $uploadedFiles['names']);
			$row->original_filenames = implode('|', $uploadedFiles['original_names']);
		}
		elseif ($attachments = $this->container->session->get('hdp_uploaded_files'))
		{
			$row->attachments        = $attachments;
			$row->original_filenames = $this->container->session->get('hdp_uploaded_files_original_names');
		}

		$row->status_id = $config->new_ticket_status_id;

		$ticketCode = '';

		while (true)
		{
			$ticketCode = strtolower(UserHelper::genRandomPassword(10));
			$query->clear()
				->select('COUNT(*)')
				->from('#__helpdeskpro_tickets')
				->where('ticket_code = ' . $db->quote($ticketCode));
			$db->setQuery($query);
			$total = $db->loadResult();

			if (!$total)
			{
				break;
			}
		}

		$row->ticket_code  = $ticketCode;
		$row->created_date = $row->modified_date = gmdate('Y-m-d H:i:s');
		$row->language     = $this->container->language->getTag();
		$row->store();

		// Store ID of the ticket back to input
		$input->set('id', $row->id);

		//Store custom fields information for this ticket
		$this->saveFieldsValue($row, $input);

		//Trigger plugins
		PluginHelper::importPlugin('helpdeskpro');
		$this->container->app->triggerEvent('onAfterStoreTicket', [$row]);

		// Send notification email to admin and confirmation email to registrants
		HelpdeskproHelper::sendNewTicketNotificationEmails($row, $config);

		if (!$user->id)
		{
			$input->set('ticket_code', $ticketCode);
		}
	}

	/**
	 * Save custom fields value which users entered for the ticket
	 *
	 * @param   \OSSolution\HelpdeskPro\Admin\Table\Ticket  $ticket
	 * @param   \OSL\Input\Input                            $input
	 */
	protected function saveFieldsValue($ticket, $input)
	{
		$fields = HelpdeskproHelper::getFields($ticket->category_id);

		/* @var \OSSolution\HelpdeskPro\Admin\Table\Fieldvalue $row */
		$row = $this->getTable('Fieldvalue');

		foreach ($fields as $field)
		{
			if ($field->fieldtype == 'Heading' || $field->fieldtype == 'Message')
			{
				continue;
			}

			$row->id        = 0;
			$row->ticket_id = $ticket->id;
			$row->field_id  = $field->id;
			$fieldValue     = trim($input->get($field->name, null, 'raw'));

			if (is_array($fieldValue))
			{
				$fieldValue = implode(',', $fieldValue);
			}

			$row->field_value = $fieldValue;

			$row->store();
		}
	}

	/**
	 * Update ticket category
	 *
	 * @param   array  $data
	 *
	 * @return boolean
	 */
	public function updateCategory($data)
	{
		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $row */
		$row = $this->getTable();
		$row->load($data['id']);
		$row->category_id = $data['new_value'];
		$row->store();
	}

	/**
	 * Update ticket Status
	 *
	 * @param   array  $data
	 *
	 * @return boolean
	 */
	public function updateStatus($data)
	{
		$config = HelpdeskproHelper::getConfig();

		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $row */
		$row = $this->getTable();
		$row->load($data['id']);

		$oldTicketStatus = $row->status_id;
		$newTicketStatus = $data['new_value'];

		$row->status_id = $data['new_value'];
		$row->store();

		if ($newTicketStatus == $config->closed_ticket_status)
		{
			HelpdeskproHelper::sendTicketClosedEmail($row, $config);
		}
		else
		{
			HelpdeskproHelper::sendTicketStatusChangeEmail($row, $oldTicketStatus, $newTicketStatus, $config);
		}
	}

	/**
	 * Update ticket Status
	 *
	 * @param   array  $data
	 *
	 * @return boolean
	 */
	public function updatePriority($data)
	{
		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $row */
		$row = $this->getTable();
		$row->load($data['id']);
		$row->priority_id = $data['new_value'];
		$row->store();
	}

	/**
	 * Update ticket Label
	 *
	 * @param   array  $data
	 *
	 * @return boolean
	 */
	public function applyLabel($data)
	{
		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $row */
		$row = $this->getTable();
		$row->load($data['id']);
		$row->label_id = $data['new_value'];
		$row->store();
	}

	/**
	 * Save rating for the ticket
	 *
	 * @param $data
	 *
	 * @return bool
	 */
	public function saveRating($data)
	{
		$config = HelpdeskproHelper::getConfig();
		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $row */
		$row = $this->getTable();
		$row->load($data['id']);
		$row->rating    = $data['new_value'];
		$row->status_id = $config->closed_ticket_status;

		// Send ticket closed email
		HelpdeskproHelper::sendTicketClosedEmail($row, $config);

		$row->store();
	}

	/**
	 * Method to add new ticket, call from API
	 *
	 * @param   array  $data
	 * @param   array  $attachments
	 */
	public function addNewTicket($data, $attachments)
	{
		jimport('joomla.user.helper');

		$config = HelpdeskproHelper::getConfig();
		$db     = $this->getDbo();
		$query  = $db->getQuery(true);

		$row = $this->getTable();
		$row->bind($data, ['id']);

		$query->select('*')
			->from('#__users')
			->where('email = ' . $db->quote($data['email']));
		$db->setQuery($query);
		$user = $db->loadObject();

		if ($user)
		{
			$row->name    = $user->name;
			$row->email   = $user->email;
			$row->user_id = $user->id;
		}

		if (!empty($attachments['names']))
		{
			$row->attachments        = implode('|', $attachments['names']);
			$row->original_filenames = implode('|', $attachments['original_names']);
		}

		$row->status_id = $config->new_ticket_status_id;

		$ticketCode = '';

		while (true)
		{
			$ticketCode = strtolower(UserHelper::genRandomPassword(10));
			$query->clear()
				->select('COUNT(*)')
				->from('#__helpdeskpro_tickets')
				->where('ticket_code = ' . $db->quote($ticketCode));
			$db->setQuery($query);
			$total = $db->loadResult();

			if (!$total)
			{
				break;
			}
		}

		$row->ticket_code  = $ticketCode;
		$row->created_date = $row->modified_date = gmdate('Y-m-d H:i:s');
		$row->language     = $this->container->language->getTag();
		$row->store();

		//Trigger plugins
		PluginHelper::importPlugin('helpdeskpro');
		$this->container->app->triggerEvent('onAfterStoreTicket', [$row]);

		// Send notification email to admin and confirmation email to registrants
		HelpdeskproHelper::sendNewTicketNotificationEmails($row, $config);
	}

	/**
	 * Method to add comment to a ticket by API
	 *
	 * @param   array  $data
	 * @param   array  $attachments
	 */
	public function addTicketComment($data, $attachments)
	{
		$config = HelpdeskproHelper::getConfig();

		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $ticket */
		$ticket = $this->getTable();

		/* @var \OSSolution\HelpdeskPro\Admin\Table\Message $row */
		$row = $this->getTable('Message');

		$row->message    = $data['message'];
		$row->user_id    = $data['user_id'];
		$row->date_added = gmdate('Y-m-d H:i:s');
		$row->ticket_id  = $data['ticket_id'];

		if (!empty($attachments['names']))
		{
			$row->attachments        = implode('|', $attachments['names']);
			$row->original_filenames = implode('|', $attachments['original_names']);
		}

		$row->store();

		if (!$ticket->load($data['ticket_id']))
		{
			// Invalid ticket, do not process it further
			return;
		}

		// Do not allow adding comment to closed ticket
		if ($ticket->status_id == $config->closed_ticket_status)
		{
			return;
		}

		if ($row->user_id == $ticket->user_id)
		{
			$isCustomerAddComment = true;
		}
		else
		{
			$isCustomerAddComment = false;
		}

		if ($isCustomerAddComment)
		{
			$ticket->status_id = $config->ticket_status_when_customer_add_comment;
		}
		else
		{
			$ticket->status_id = $config->ticket_status_when_admin_add_comment;
		}

		$ticket->modified_date = gmdate('Y-m-d H:i:s');

		$ticket->store();

		//Trigger plugins
		PluginHelper::importPlugin('helpdeskpro');
		$this->container->app->triggerEvent('onAfterStoreComment', [$row, $ticket]);

		//Need to send email to users
		if ($isCustomerAddComment)
		{
			HelpdeskproHelper::sendTicketUpdatedEmailToManagers($row, $ticket, $config);
		}
		else
		{
			HelpdeskproHelper::sendTicketUpdatedEmailToCustomer($row, $ticket, $config);
		}
	}

	/**
	 * Add comment to the ticket
	 *
	 * @param   \OSL\Input\Input  $input
	 * @param   bool              $closeTicket
	 *
	 */
	public function addComment($input, $closeTicket = false)
	{
		$user   = $this->container->user;
		$config = HelpdeskproHelper::getConfig();

		/* @var \OSSolution\HelpdeskPro\Admin\Table\Ticket $ticket */
		$ticket = $this->getTable();

		/* @var \OSSolution\HelpdeskPro\Admin\Table\Message $row */
		$row              = $this->getTable('Message');
		$allowedFileTypes = explode('|', $config->allowed_file_types);

		for ($i = 0, $n = count($allowedFileTypes); $i < $n; $i++)
		{
			$allowedFileTypes[$i] = trim(strtoupper($allowedFileTypes[$i]));
		}

		$row->message = $input->getHtml('message');

		$ticket->load($input->post->getInt('id'));

		if ($user->id)
		{
			$row->user_id = $user->get('id');
		}
		else
		{
			if (isset($data['ticket_code']))
			{
				$row->user_id = $ticket->user_id;
			}
		}

		$row->date_added = gmdate('Y-m-d H:i:s');
		$row->ticket_id  = $ticket->id;
		$uploadedFiles   = $this->storeAttachment($input, $allowedFileTypes);

		if (count($uploadedFiles['names']))
		{
			$row->attachments        = implode('|', $uploadedFiles['names']);
			$row->original_filenames = implode('|', $uploadedFiles['original_names']);
		}
		elseif ($attachments = $this->container->session->get('hdp_uploaded_files'))
		{
			$row->attachments        = $attachments;
			$row->original_filenames = $this->container->session->get('hdp_uploaded_files_original_names');
		}

		$row->store();

		if ($row->user_id == $ticket->user_id || isset($data['ticket_code']))
		{
			$isCustomerAddComment = true;
		}
		else
		{
			$isCustomerAddComment = false;
		}

		if ($closeTicket)
		{
			$ticket->status_id = $config->closed_ticket_status;
		}
		elseif ($isCustomerAddComment)
		{
			$ticket->status_id = $config->ticket_status_when_customer_add_comment;
		}
		else
		{
			$ticket->status_id = $config->ticket_status_when_admin_add_comment;
		}

		$ticket->modified_date = gmdate('Y-m-d H:i:s');

		$ticket->store();

		//Trigger plugins
		PluginHelper::importPlugin('helpdeskpro');
		$this->container->app->triggerEvent('onAfterStoreComment', [$row, $ticket]);

		//Need to send email to users
		if ($isCustomerAddComment)
		{
			HelpdeskproHelper::sendTicketUpdatedEmailToManagers($row, $ticket, $config);
		}
		else
		{
			HelpdeskproHelper::sendTicketUpdatedEmailToCustomer($row, $ticket, $config);
		}

		if ($closeTicket)
		{
			HelpdeskproHelper::sendTicketClosedEmail($ticket, $config);
		}
	}

	/**
	 * Get all comments of the current ticket
	 *
	 * @return array
	 */
	public function getMessages()
	{
		if ($this->state->id)
		{
			$db    = $this->getDbo();
			$query = $db->getQuery(true);

			$query->select('a.*, b.name')
				->from('#__helpdeskpro_messages AS a')
				->leftJoin('#__users AS b ON a.user_id = b.id')
				->where('a.ticket_id = ' . $this->state->id)
				->order('id DESC');

			$db->setQuery($query);

			return $db->loadObjectList();

		}

		return [];
	}

	/**
	 * Get all custom fields value for a ticket
	 *
	 * @return array
	 */
	public function getFieldsValue()
	{
		$fieldValues = [];

		if ($this->state->id)
		{
			$db    = $this->getDbo();
			$query = $db->getQuery(true);
			$query->select('field_id, field_value')
				->from('#__helpdeskpro_field_value')
				->where('ticket_id = ' . $this->state->id);
			$db->setQuery($query);
			$rows = $db->loadObjectList();
			foreach ($rows as $row)
			{
				$fieldValues[$row->field_id] = $row->field_value;
			}
		}

		return $fieldValues;
	}

	/**
	 * Convert a support ticket to a knowledge base article
	 *
	 * @throws \Exception
	 */
	public function convertTicketToArticle()
	{
		$item     = $this->getData();
		$messages = $this->getMessages();

		$messages = array_reverse($messages);

		if (!$item)
		{
			throw new \Exception(Text::_('HDP_INVALID_TICKET'));
		}

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

		$rootUri = Uri::root(true);
		$config  = HelpdeskproHelper::getConfig();
		$user    = new User;

		$item->date_added = $item->created_date;

		array_unshift($messages, $item);

		$layoutData = [
			'messages' => $messages,
			'user'     => $user,
			'rootUri'  => $rootUri,
			'config'   => $config,
		];

		$row = $this->getTable('article');

		$query->select('id')
			->from('#__helpdeskpro_articles')
			->where('ticket_id = ' . $item->id);
		$db->setQuery($query);
		$id = (int) $db->loadResult();

		if ($id)
		{
			$row->load($id);
		}

		$row->category_id = $item->category_id;
		$row->ticket_id   = $item->id;
		$row->title       = '[#' . $item->id . '] - ' . $item->subject;
		$row->alias       = ApplicationHelper::stringURLSafe($row->title);
		$row->text        = '<table class="adminform">' . HelpdeskproHelperHtml::loadCommonLayout('common/tmpl/ticket_comments.php', $layoutData) . '</table>';
		$row->published   = 1;

		$query->clear()
			->select('MAX(ordering)')
			->from('#__helpdeskpro_articles')
			->where('category_id = ' . $row->category_id);
		$db->setQuery($query);
		$row->ordering = (int) $db->loadResult() + 1;
		$row->store();
	}

	/**
	 * Delete all the tickets related data before tickets deleted
	 *
	 * @param   array  $cid  Ids of deleted record
	 */
	protected function beforeDelete($cid)
	{
		$db    = $this->getDbo();
		$query = $db->getQuery(true);

		$attachmentsPath = JPATH_ROOT . '/media/com_helpdeskpro/attachments/';

		$row = $this->getTable();

		foreach ($cid as $ticketId)
		{
			$row->load($ticketId);

			// Delete ticket attachments
			if ($row->attachments)
			{
				$files = explode('|', $row->attachments);
				foreach ($files as $file)
				{
					if ($file && File::exists($attachmentsPath . $file))
					{
						File::delete($attachmentsPath . $file);
					}
				}
			}

			// Delete attachments in messages/comments of the ticket
			$query->clear()
				->select('attachments')
				->from('#__helpdeskpro_messages')
				->where('ticket_id = ' . $ticketId);
			$db->setQuery($query);
			$attachments = $db->loadColumn();

			foreach ($attachments as $attachment)
			{
				if ($attachment)
				{
					$files = explode('|', $attachment);
					foreach ($files as $file)
					{
						if ($file && File::exists($attachmentsPath . $file))
						{
							File::delete($attachmentsPath . $file);
						}
					}
				}
			}

			// Delete ticket message
			$query->clear()
				->delete('#__helpdeskpro_messages')
				->where('ticket_id IN (' . implode(',', $cid) . ')');
			$db->setQuery($query);
			$db->execute();
		}
	}

	/**
	 * Store attachments which user uploaded
	 *
	 * @param   \OSL\Input\Input  $input
	 * @param   array             $allowedFileTypes
	 *
	 * @return array
	 */
	private function storeAttachment($input, $allowedFileTypes)
	{
		$app = $this->container->app;

		$attachmentsPath = JPATH_ROOT . '/media/com_helpdeskpro/attachments';

		$uploadedFiles = [
			'names'          => [],
			'original_names' => [],
		];

		$attachments = $input->files->get('attachment', [], 'raw');

		foreach ($attachments as $attachment)
		{
			$name = File::makeSafe($attachment['name']);

			if (empty($name))
			{
				continue;
			}

			$fileExt = strtoupper(File::getExt($name));

			if (in_array($fileExt, $allowedFileTypes))
			{
				if (File::exists($attachmentsPath . '/' . $name))
				{
					$fileName = File::stripExt($name) . '_' . uniqid() . '.' . $fileExt;
				}
				else
				{
					$fileName = $name;
				}

				// Fix upload attachments causes by change in Joomla 3.4.4
				$uploaded = File::upload($attachment['tmp_name'], $attachmentsPath . '/' . $fileName, false, true);

				if ($uploaded)
				{
					$uploadedFiles['names'][]          = $fileName;
					$uploadedFiles['original_names'][] = $name;
				}
				else
				{
					$app->enqueueMessage(Text::sprintf('HDP_UPLOAD_FILE_FAILED', $attachment['name']), 'warning');
				}
			}
			else
			{
				$app->enqueueMessage(Text::sprintf('HDP_FILETYPE_NOT_ALLOWED', $attachment['name'], implode(',', $allowedFileTypes)), 'warning');
			}
		}

		return $uploadedFiles;
	}
}