<?php
namespace JExtstore\Component\JChat\Site\Model;
/** 
 * @package JCHAT::FORM::components::com_jchat
 * @subpackage models
 * @author Joomla! Extensions Store
 * @Copyright (C) 2015 - Joomla! Extensions Store
 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html   
 */
defined( '_JEXEC' ) or die( 'Restricted access' );
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Filter\InputFilter;
use JExtstore\Component\JChat\Administrator\Framework\Model as JChatModel;
use JExtstore\Component\JChat\Administrator\Framework\Exception as JChatException;

/**
 * Group users chat model
 * 
 * @package JCHAT::FORM::components::com_jchat
 * @subpackage models
 * @since 1.0
 */ 
class MessagingModel extends JChatModel {
	/**
	 * Reference to the stream model
	 * 
	 * @access private
	 * @var Object &
	 */
	private $streamModel;
	
	/**
	 * User Object
	 * @access private
	 * @var Object &
	 */
	private $myUser;
	
	/**
	 * User field name
	 * @access private
	 * @var string
	 */
	private $userFieldName;
	
	/**
	 * Restituisce la query string costruita per ottenere il wrapped set richiesto in base
	 * allo userstate, opzionalmente seleziona i campi richiesti
	 *
	 * @access private
	 * @return string
	 */
	protected function buildListQuery() {
		$queryParts = array();
		$selectLastSentMessages = '';
      	$queryParts['SELECT'] = '';
     	$queryParts['JOIN'] = '';
  		$accessLevels['JOIN'] = '';
  		$myUsersGroups['JOIN'] = '';
  		$orderByConversations['JOIN'] = '';
		$accessLevelsAND = null;
		$myUsersGroupsAND = null;
		$queryOrder = "\n ORDER BY u.{$this->userFieldName} ASC";
		
		// get current time to evaluate if users are online or offline, AKA no more session refresh after maxinactivitytime seconds = users stripped out from buddylist
		$time = time();
	       	
		// Evaluate ONLY FRIENDS 3PD integration option
		if($this->componentParams->get('3pdintegration', null) && $this->componentParams->get('filter_friendship', false)) {
			$queryPartsFriends = $this->streamModel->getQueryParts('buddylist');
		} else {
			$queryPartsFriends['JOIN'] = '';
			$queryPartsFriends['WHERE'] = '';
  		}
       		
		// Manage live support mode filtering 
		list($queryParts['JOIN'], $additionalAND) = $this->streamModel->getQueryLiveSupport ('u.id');

		// Manage chat filtering by access levels
		list($accessLevels['JOIN'], $accessLevelsAND) = $this->streamModel->getQueryAccessLevels('u.id');

		// Manage chat filtering by same users groups of the current user
		if($this->componentParams->get('limit_my_users_groups', 0)) {
			list($myUsersGroups['JOIN'], $myUsersGroupsAND) = $this->streamModel->getQueryMyUsersGroups('u.id');
		}

		// Manage ordering of users list
		if($this->componentParams->get('pm_order_by_clause', 'alpha') == 'chrono') {
			$selectLastSentMessages = ', innerselect.lastsentmessage';
			$orderByConversations['JOIN'] = "\n LEFT JOIN (SELECT innerchat.fromuser, MAX(innerchat.sent) AS lastsentmessage" .
			 								"\n FROM #__jchat AS innerchat GROUP BY innerchat.fromuser) innerselect ON u.id = innerselect.fromuser";
			$queryOrder = "\n ORDER BY innerselect.lastsentmessage DESC";
		}

		$queryLimit = '';
		$queryLimitValue = (int)$this->componentParams->get('limit_users_number_value', 1000);
		if($this->componentParams->get('limit_users_number', 0) && $queryLimitValue) {
			$queryLimit = "\n LIMIT 0, " . $queryLimitValue;
		}

		// Evaluate the shared session option for SQL queries on Joomla 3.7+
		$sharedSession = (int)$this->app->get('shared_session', null);
		if($sharedSession == 1 && $this->componentParams->get('shared_session_support', 1)) {
			$sessClientId = "\n AND (sess.client_id = 0 OR ISNULL(sess.client_id))";
		} else {
			$sessClientId = "\n AND sess.client_id = 0";
		}
		
  		$query = "SELECT DISTINCT(u.id), MAX(sess.time)," .
				 "\n u.{$this->userFieldName} AS " . $this->dbInstance->quoteName('username') . "," .
				 "\n sess.session_id" .
  				 $selectLastSentMessages .
				 "\n FROM #__users AS u" .
				 "\n LEFT JOIN #__session AS sess" .
				 "\n ON u.id = sess.userid" .
				 $sessClientId .
				 $orderByConversations['JOIN'] .
  				 $queryPartsFriends['JOIN'] .
  				 $queryParts['JOIN'] .
  				 $accessLevels['JOIN'] .
  				 $myUsersGroups['JOIN'] .
				 "\n WHERE u.id <> " . (int)$this->myUser->id .
				 "\n AND u.block = 0" .
				 $additionalAND .
				 $accessLevelsAND .
				 $myUsersGroupsAND .
				 $queryPartsFriends['WHERE'] .
				 "\n GROUP BY u.id" .
				 $queryOrder .
				 $queryLimit;

		return $query;
	}
	
	/**
	 * Main get data method to retrieve the users list
	 *
	 * @access public
	 * @return Object[]
	 */
	public function getData(): array {
		// Build query
		$query = $this->buildListQuery ();
		try {
			$this->dbInstance->setQuery ( $query );
			$result = $this->dbInstance->loadObjectList ();
		} catch ( JChatException $e ) {
			$this->app->enqueueMessage ( $e->getMessage (), $e->getErrorLevel () );
			$result = array ();
		} catch ( \Exception $e ) {
			$jchatException = new JChatException ( $e->getMessage (), 'error' );
			$this->app->enqueueMessage ( $jchatException->getMessage (), $jchatException->getErrorLevel () );
			$result = array ();
		}
		return $result;
	}
	
	/**
	 * Main get data method to retrieve the users list
	 *
	 * @access public
	 * @return Object[]
	 */
	public function getPendingMessages() {
		// Build query
		$query = "SELECT COUNT(*) AS newmessages, cchat.fromuser" .
				 "\n FROM #__jchat AS cchat" .
				 "\n WHERE cchat.touser = " . (int)$this->myUser->id .
				 "\n AND cchat.read = 0" .
				 "\n GROUP BY cchat.fromuser";
		
		try {
			$this->dbInstance->setQuery ( $query );
			$result = $this->dbInstance->loadAssocList ('fromuser');
		} catch ( JChatException $e ) {
			$this->app->enqueueMessage ( $e->getMessage (), $e->getErrorLevel () );
			$result = array ();
		} catch ( \Exception $e ) {
			$jchatException = new JChatException ( $e->getMessage (), 'error' );
			$this->app->enqueueMessage ( $jchatException->getMessage (), $jchatException->getErrorLevel () );
			$result = array ();
		}
		return $result;
	}
	
	/**
	 * Load entity from ORM table and manages session data post error redirect
	 *
	 * @access public
	 * @param int $id The target user id in the conversation
	 * @return Object&
	 */
	public function loadEntity($id) {
		$fromLoggedID = $id->fromLoggedId;
		$limitQueryMessages = null;
		
		if(isset($id->oldestMsgId)) {
			$oldestMessageID = $id->oldestMsgId;
			$limitQueryMessages = $oldestMessageID ? "\n AND cchat.id < " . (int)$oldestMessageID  : null;
		}
		
		$limitMessages = "\n LIMIT "  . (int)$this->componentParams->get('pm_num_loading_msgs', 100);
		
		try {
			$sql = 	"SELECT cchat.id, cchat.from," .
					"\n cchat.message, cchat.sent, cchat.read, cchat.type, cchat.status, u.id AS userid, " .
					"\n u.{$this->userFieldName} AS " . $this->dbInstance->quoteName('fromusername') .
					"\n FROM #__jchat AS cchat" .
					"\n INNER JOIN #__users AS u ON cchat.fromuser = u.id" .
					"\n WHERE ((cchat.touser = ". $this->dbInstance->quote($this->myUser->id) .
					"\n AND cchat.fromuser = " . $this->dbInstance->quote($fromLoggedID) . ")" .
					"\n OR (cchat.fromuser = ". $this->dbInstance->quote($this->myUser->id) .
					"\n AND cchat.touser = " . $this->dbInstance->quote($fromLoggedID) . "))" .
					"\n AND cchat.id NOT IN(SELECT " . $this->dbInstance->quoteName('messageid') . 
					"\n FROM " . $this->dbInstance->quoteName('#__jchat_messaging_deletedmessages') .
					"\n WHERE " . $this->dbInstance->quoteName('userid') . " = " . $this->dbInstance->quote($this->myUser->id) . ")" .
					$limitQueryMessages .
					"\n ORDER BY cchat.id DESC" .
					$limitMessages;
				
			$this->dbInstance->setQuery($sql);
			$rows = $this->dbInstance->loadAssocList();

			// Now update status of all messages received till the latest new message as read status
			$sql = "UPDATE" .
					"\n " . $this->dbInstance->quoteName('#__jchat') . " AS cchat" .
					"\n SET " . $this->dbInstance->quoteName('read') . " = 1 " .
					"\n WHERE " . $this->dbInstance->quoteName('touser') . " = " . (int)($this->myUser->id) .
					"\n AND " . $this->dbInstance->quoteName('fromuser') . " = " . (int)($fromLoggedID) .
					"\n AND " . $this->dbInstance->quoteName('read') . " = 0" .
					$limitQueryMessages .
					"\n ORDER BY cchat.id DESC" .
					$limitMessages;
				
			$this->dbInstance->setQuery($sql);
			$this->dbInstance->execute();
			
			return $rows;
		} catch ( JChatException $e ) {
			$this->setError ( $e );
			return false;
		} catch ( \Exception $e ) {
			$jchatException = new JChatException ( $e->getMessage (), 'error' );
			$this->setError ( $jchatException );
			return false;
		}
		
		return true;
	}
	
	/**
	 * Delete conversation messages based on messages ids but not deleting records
	 * It only flags the messages as deleted from the current user on demand to avoid redownload
	 * The physical deletion must be done in admin
	 *
	 * @param int $ids
	 * @access public
	 * @return bool
	 */
	public function deleteEntity($ids): bool {
		$chunksQuery = array();
		
		// Now update status of all messages received till latest new message as read status
		foreach ($ids as $messageId) {
			$chunksQuery[] = "(" .
					(int)($messageId) . ","  .
					(int)($this->myUser->id) . ")";
		}
		
		try {
			$sql = "INSERT IGNORE INTO #__jchat_messaging_deletedmessages (" .
					$this->dbInstance->quoteName('messageid') . ", " .
					$this->dbInstance->quoteName('userid') . 
					") VALUES " . implode(",\n", $chunksQuery);
			
			$this->dbInstance->setQuery($sql);
			if(!$this->dbInstance->execute()) {
				throw new JChatException(Text::_('COM_JCHAT_ERRORSYNC_INSERT'), 'notice');
			}
			
			$this->dbInstance->setQuery($sql);
			$this->dbInstance->execute();
				
		} catch ( JChatException $e ) {
			$this->setError ( $e );
			return false;
		} catch ( \Exception $e ) {
			$jchatException = new JChatException ( $e->getMessage (), 'error' );
			$this->setError ( $jchatException );
			return false;
		}
		
		return true;
	}
	
	/**
	 * Class constructor
	 * @access public
	 * @param Object& $wpdb
	 * @param Object& $userObject
	 * @return Object &
	 */
	public function __construct($config = array(), MVCFactoryInterface $factory = null) {
		// Parent model construct
		parent::__construct( $config, $factory );
		
		$this->getComponentParams();
		
		// Reference to the stream model
		$this->streamModel = $config['streamModel'];
		
		// Store the owned user object instance
		$this->myUser = $this->app->getIdentity();
		
		$filter = InputFilter::getInstance();
		$this->userFieldName = $filter->clean($this->componentParams->get('usefullname', 'username'), 'word');
	}
}