<?php
/**
 * @package RSform!Pro
 * @copyright (C) www.rsjoomla.com
 * @license GPL, http://www.gnu.org/copyleft/gpl.html
 */

defined('_JEXEC') or die;

use Joomla\CMS\Language\Text;
use Joomla\CMS\Factory;

class RSFormZoomAPI
{
	private $accountId = null;
	private $clientId = null;
	private $clientSecret = null;
	private $questions_possible_answers = null;

	// the available email languages
	public $languages = array(
		'en-US',
		'de-DE',
		'es-ES',
		'fr-FR',
		'jp-JP',
		'pt-PT',
		'ru-RU',
		'zh-CN',
		'zh-TW',
		'ko-KO',
		'it-IT',
		'vi-VN'
	);

	public static function getInstance() {
		static $instance = null;

		if ($instance === null)
		{
			$instance = new RSFormZoomAPI();
		}

		return $instance;
	}

	public function loadEndpoint($type)
	{
		static $endpoints = array();

		if (!isset($endpoints[$type])) {
			switch($type) {
				case 'users':
					$endpoints[$type] = new Zoom\Endpoint\Oauth\Users($this->accountId, $this->clientId, $this->clientSecret);

					break;

				case 'meetings':
					$endpoints[$type] = new Zoom\Endpoint\Oauth\Meetings($this->accountId, $this->clientId, $this->clientSecret);

					break;
				case 'webinars':
					$endpoints[$type] = new Zoom\Endpoint\Oauth\Webinars($this->accountId, $this->clientId, $this->clientSecret);

					break;
			}
		}

		return $endpoints[$type];
	}

	/**
	 * RSFormZoomAPI constructor.
	 */
	public function __construct()
	{
		require_once __DIR__ . '/zoom/vendor/autoload.php';

		$this->getCredentials();

		$this->questions_possible_answers = array(
			'country' 					=> Text::_('PLG_SYSTEM_RSFPZOOM_QUESTIONS_FIELD_COUNTRY_VALUES'),
			'purchasing_time_frame' 	=> 'Within a month<br/>1-3 months<br/>4-6 months<br/>More than 6 months<br/>No timeframe',
			'role_in_purchase_process'  => 'Decision Maker<br/>Evaluator/Recommender<br/>Influencer<br/>Not involved',
			'no_of_employees'			=> '1-20<br/>21-50<br/>51-100<br/>101-500<br/>500-1,000<br/>1,001-5,000<br/>5,001-10,000<br/>More than 10,000'
		);
	}

	protected function getCredentials() {
		$this->accountId  = RSFormProHelper::getConfig('zoom.accountid');
		$this->clientId = RSFormProHelper::getConfig('zoom.clientid');
		$this->clientSecret = RSFormProHelper::getConfig('zoom.clientsecret');
	}

	public function getUserDetails($formId, $user_id, $specific_details = array(), $log = false) {

		try {
			if (empty($user_id)) {
				throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_MEETINGS_NOT_FOUND'));
			}

			$users = $this->loadEndpoint('users');
			$user = $users->retrieve($user_id);

			if (!isset($user['code']) || $user['code'] != 200) {
				$message = isset($user['message']) ? $user['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_USER_RETRIEVING');
				throw new Exception($message);
			}

			// return all the data if the specific one is not requested
			if (empty($specific_details))
			{
				return $user;
			}
			else
			{
				return array_intersect_key($user, $specific_details);
			}

		} catch (Exception $e) {
			if ($log) {
				$dataLog = new stdClass();
				$dataLog->form_id = $formId;
				$dataLog->msg = $e->getMessage();

				Factory::getApplication()->enqueueMessage(Text::sprintf('PLG_SYSTEM_RSFPZOOM_ERROR_GENERAL', $dataLog->msg), 'error');

				$this->storeErrorLog($dataLog);
			}

			return false;
		}

	}

	public function getUsersList($formId, $query = array(), $log = false)
	{
		try {
			$page_size = (int) RSFormProHelper::getConfig('zoom.pagesize');
			$users = $this->loadEndpoint('users');

			// set the page size
			if ($page_size > 0)
			{
				$query['page_size'] = $page_size;
			}

			$list = $users->list($query);

			if (!isset($list['code']) || $list['code'] != 200) {
				$message = isset($list['message']) ? $list['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_USERS_RETRIEVING');
				throw new Exception($message);
			}

			if (isset($list['total_records']) && $list['total_records']) {
				return $list;
			} else {
				// in some cases the code could be 200 but no records provided
				throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_USERS_NOT_FOUND'));
			}

		} catch (Exception $e) {
			if ($log) {
				$dataLog = new stdClass();
				$dataLog->form_id = $formId;
				$dataLog->msg = $e->getMessage();

				Factory::getApplication()->enqueueMessage(Text::sprintf('PLG_SYSTEM_RSFPZOOM_ERROR_GENERAL', $dataLog->msg), 'error');

				$this->storeErrorLog($dataLog);
			}

			return array();
		}
	}

	public function getUserMeetings($userId = null, $type = 'scheduled', $query = array())
	{
		if (empty($userId)) {
			return false;
		}

		if (!empty($type)) {
			$query['type'] = $type;
		}

		if (!isset($query['page_size'])) {
			$query['page_size'] = 50;
		}

		$nice_meetings = array();

		$meetings = $this->loadEndpoint('meetings');
		$list = $meetings->list($userId, $query);

		if (!isset($list['code']) || $list['code'] != 200) {
			$message = isset($list['message']) ? $list['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_MEETINGS_RETRIEVING');
			throw new Exception($message);
		}

		if (isset($list['total_records']) && $list['total_records']) {
			foreach ($list['meetings'] as $meeting) {
				// on 32 bits systems the value is returned as float,and array must have as index integers or strings
				$meeting['id'] = (string) $meeting['id'];

				$nice_meetings[$meeting['id']] = $meeting['topic'];
			}
		} else {
			// in some cases the code could be 200 but no records provided
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_MEETINGS_NOT_FOUND'));
		}

		return $nice_meetings;
	}

	public function getUserWebinars($userId = null, $query = array())
	{
		if (empty($userId)) {
			return false;
		}

		if (!isset($query['page_size'])) {
			$query['page_size'] = 50;
		}

		$nice_webinars = array();

		$webinars = $this->loadEndpoint('webinars');
		$list = $webinars->list($userId, $query);

		if (!isset($list['code']) || $list['code'] != 200) {
			$message = isset($list['message']) ? $list['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_WEBINARS_RETRIEVING');
			throw new Exception($message);
		}

		if (isset($list['total_records']) && $list['total_records']) {
			foreach ($list['webinars'] as $webinar) {
				// on 32 bits systems the value is returned as float,and array must have as index integers or strings
				$webinar['id'] = (string) $webinar['id'];

				$nice_webinars[$webinar['id']] = $webinar['topic'];
			}
		} else {
			// in some cases the code could be 200 but no records provided
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_WEBINARS_NOT_FOUND'));
		}

		return $nice_webinars;
	}

	public function getMeetingDetails($meetingId = null)
	{
		if (empty($meetingId)) {
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_MEETING_ID_NOT_FOUND'));
		}


		$meetings = $this->loadEndpoint('meetings');
		$details = $meetings->meeting($meetingId);


		if (!isset($details['code']) || $details['code'] != 200) {
			$message = isset($details['message']) ? $details['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_MEETING_DETAILS_RETRIEVING');
			throw new Exception($message);
		}

		$parsed_details = array();

		if (!empty($details['join_url'])) {
			$parsed_details['join_url'] = $details['join_url'];
		}

		if (!empty($details['registration_url'])) {
			$parsed_details['registration_url'] = $details['registration_url'];
		}

		if (!empty($details['password'])) {
			$parsed_details['password'] = $details['password'];
		}

		if (!empty($details['id'])) {
			$parsed_details['meeting_id'] = $details['id'];
		}

		return $parsed_details;
	}

	public function getWebinarDetails($meetingId = null)
	{
		if (empty($meetingId)) {
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_WEBINAR_ID_NOT_FOUND'));
		}


		$webinars = $this->loadEndpoint('webinars');
		$details = $webinars->webinar($meetingId);


		if (!isset($details['code']) || $details['code'] != 200) {
			$message = isset($details['message']) ? $details['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_WEBINAR_DETAILS_RETRIEVING');
			throw new Exception($message);
		}

		$parsed_details = array();

		if (!empty($details['join_url'])) {
			$parsed_details['join_url'] = $details['join_url'];
		}

		if (!empty($details['registration_url'])) {
			$parsed_details['registration_url'] = $details['registration_url'];
		}

		if (!empty($details['password'])) {
			$parsed_details['password'] = $details['password'];
		}

		if (!empty($details['id'])) {
			$parsed_details['meeting_id'] = $details['id'];
		}

		return $parsed_details;
	}

	public function getQuestions($meetingId = null, $formId = null, $zoom_type = 'meetings') {
		$questions = array();

		try {
			if (empty($meetingId)) {
				if ($zoom_type == 'meetings') {
					$error_msg = Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_NO_MEETING_ID');
				} else {
					$error_msg = Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_NO_WEBINAR_ID');
				}
				throw new Exception($error_msg);
			}

			$endpoint = $zoom_type == 'meetings' ? $this->loadEndpoint('meetings') : $this->loadEndpoint('webinars');
			$list = $endpoint->listRegistrationQuestions($meetingId);

			if (!isset($list['code']) || $list['code'] != 200) {
				$message = isset($list['message']) ? $list['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_QUESTIONS_RETRIEVING');
				throw new Exception($message);
			}

			// at this point every thing looks ok so we must add the mandatory questions
			$questions[] = array('type' => 'mandatory', 'question' => 'email', 'required' => true);
			$questions[] = array('type' => 'mandatory', 'question' => 'first_name', 'required' => true);

			// we must set the last_name here because not always the questions are retrieved (if no other question is selected the last name even if is predefined selected it will not be provided)
			$questions[] = array('type' => 'mandatory', 'question' => 'last_name', 'required' => true);

			if (!empty($list['questions'])) {
				foreach ($list['questions'] as $question) {
					// skip the last_name
					if ($question['field_name'] == 'last_name') {
						continue;
					}
					$field_data = array('type' => 'question', 'question' => $question['field_name'], 'required' => $question['required']);

					// some questions (field_names) have specific values that can be added
					if (isset($this->questions_possible_answers[$question['field_name']])) {
						$field_data['answers'] = trim($this->questions_possible_answers[$question['field_name']]);
					}

					$questions[] = $field_data;
				}
			}

			if (!empty($list['custom_questions'])) {
				foreach ($list['custom_questions'] as $c_question) {
					$questions[] = array('type' => 'custom_question', 'question' => $c_question['title'], 'required' => $c_question['required'], 'answers' => $c_question['answers']);
				}
			}

		} catch (Exception $e) {
			if (!empty($formId)) {
				$dataLog = new stdClass();
				$dataLog->form_id = $formId;
				$dataLog->msg = $e->getMessage();

				$this->storeErrorLog($dataLog);
			}

			return $questions;
		}

		return $questions;
	}

	public function addRegistrant($meetingId = null, $data = array(), $occurence_ids = array() ) {
		if (empty($data)) {
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_REGISTRANT_NO_DATA'));
		}

		// Determine the registrant type
		$zoom_type = 'meetings';
		if (isset($data['zoom_type'])) {
			$zoom_type = $data['zoom_type'];
			// remove it from the data array
			unset($data['zoom_type']);
		}

		if (empty($meetingId)) {
			if ($zoom_type == 'meetings') {
				$error_msg = Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_NO_MEETING_ID');
			} else {
				$error_msg = Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_NO_WEBINAR_ID');
			}
			throw new Exception($error_msg);
		}

		if (!isset($data['email']) || !strlen($data['email'])) {
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_REGISTRANT_NO_EMAIL'));
		}

		if (!isset($data['first_name']) || !strlen($data['first_name'])) {
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_REGISTRANT_NO_FNAME'));
		}

		if (!isset($data['last_name']) || !strlen($data['last_name'])) {
			throw new Exception(Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_REGISTRANT_NO_LNAME'));
		}

		$query = array();

		if (!empty($occurence_ids)) {
			$query['occurrence_ids'] = is_array($occurence_ids) ? implode(',', $occurence_ids) : $occurence_ids;
		}

		$endpoint = $zoom_type == 'meetings' ? $this->loadEndpoint('meetings') : $this->loadEndpoint('webinars');
		$add = $endpoint->addRegistrant($meetingId, $data, $query);


		if (!isset($add['code']) || !in_array($add['code'], array(200, 201))) {
			$message = isset($add['message']) ? $add['message'] : Text::_('PLG_SYSTEM_RSFPZOOM_API_ERROR_REGISTRANT_DEFAULT');
			throw new Exception($message);
		}


		if (!empty($add['join_url'])) {
			return $add['join_url'];
		}

		throw new Exception($add['message']);
	}


	public function storeErrorLog($dataLog) {
		$db 	= Factory::getDbo();
		$query 	= $db->getQuery(true);

		$now = Factory::getDate('now');
		$columns = array('form_id', 'message', 'date');
		$values = array($db->q($dataLog->form_id), $db->q($dataLog->msg), $db->q($now->toSql()));

		$query->insert($db->qn('#__rsform_zoom_meetings_error_log'))
			->columns($db->qn($columns))
			->values(implode(',', $values));

		$db->setQuery($query);
		if ($db->execute()) {
			// delete the old log for this form (prevent overloading the database)
			$this->deleteOldErrorLog($dataLog->form_id);
		}
	}

	protected function deleteOldErrorLog($form_id) {
		$db 	= Factory::getDbo();
		$query 	= $db->getQuery(true);

		// get the l5'th one
		$query->select('*')
			->from($db->qn('#__rsform_zoom_meetings_error_log'))
			->where($db->qn('form_id').' = '.$db->q($form_id))
			->order($db->qn('date').' DESC');
		$db->setQuery($query, 15, 1);

		if ($resource = $db->loadAssoc()) {
			$query = $db->getQuery(true);
			$query->delete($db->qn('#__rsform_zoom_meetings_error_log'))
				->where($db->qn('date').' < '.$db->q($resource['date']))
				->where($db->qn('form_id').' = '.$db->q($form_id));
			$db->setQuery($query);
			$db->execute();
		}
	}

	public function deleteAllErrorLog($form_id) {
		$db 	= Factory::getDbo();
		$query 	= $db->getQuery(true);

		$query->delete($db->qn('#__rsform_zoom_meetings_error_log'))
			->where($db->qn('form_id').' = '.$db->q($form_id));
		$db->setQuery($query);
		$db->execute();
	}
}