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

defined('_JEXEC') or die('Restricted access');

define('RSFORM_FIELD_PAYMENT_2CHECKOUT', 513);

class plgSystemRsfp2checkout extends JPlugin
{
	protected $componentId 	    = RSFORM_FIELD_PAYMENT_2CHECKOUT;
	protected $componentValue   = '2checkout';
	protected $log = array();

	protected $autoloadLanguage = true;

	public function onRsformBackendAfterCreateFieldGroups(&$fieldGroups, $self)
	{
		$formId = JFactory::getApplication()->input->getInt('formId');
		$exists = RSFormProHelper::componentExists($formId, RSFORM_FIELD_PAYMENT_2CHECKOUT);

		$fieldGroups['payment']->fields[] = (object) array(
			'id' 	=> RSFORM_FIELD_PAYMENT_2CHECKOUT,
			'name' 	=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_COMPONENT'),
			'icon'  => 'rsficon rsficon-coin-dollar',
			'exists' => $exists ? $exists[0] : false
		);
	}
	
	protected function getTable()
	{
		return JTable::getInstance('RSForm_2Checkout', 'Table');
	}

	public function onRsformFormSave($form)
	{
		try
		{
			$post = array(
				'vars' 		=> JFactory::getApplication()->input->get('2checkout_vars', array(), 'array'),
				'form_id' 	=> JFactory::getApplication()->input->getInt('formId', 0)
			);
			$row = $this->getTable();

			if (!$row)
			{
				throw new Exception('Could not get an instance of the 2Checkout Table');
			}

			if (!$row->save($post))
			{
				throw new Exception('Could not save information to database');
			}
		}
		catch (Exception $e)
		{
			JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error');
		}

		return true;
	}

	public function onRsformBackendAfterShowFormEditTabsTab()
	{
		?>
		<li><a href="javascript: void(0);" id="rsfp2checkout"><span class="rsficon rsficon-coin-dollar"></span><span class="inner-text"><?php echo JText::_('PLG_SYSTEM_RSFP2CHECKOUT_TAB'); ?></span></a></li>
		<?php
	}

	public function onRsformBackendAfterShowFormEditTabs()
	{
		$formId = JFactory::getApplication()->input->getInt('formId', 0);
		$row    = $this->getTable();

		if (!$row)
		{
			return;
		}

		$row->load($formId);

		$fields_array = $this->_getFields($formId);
		$fields       = array(
			JHtml::_('select.option', '', JText::_('PLG_SYSTEM_RSFP2CHECKOUT_IGNORE')),
		);

		foreach ($fields_array as $field)
		{
			$fields[] = JHtml::_('select.option', $field, $field);
		}

		// Merge Vars
		$merge_vars = array(
			'card_holder_name'		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_CARD_HOLDER_NAME'),
			'street_address'		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_STREET_ADDRESS'),
			'street_address2'		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_STREET_ADDRESS2'),
			'city'      			=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_CITY'),
			'state'      			=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_STATE'),
			'zip'      				=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_ZIP'),
			'country'      			=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_COUNTRY'),
			'email'      			=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_EMAIL'),
			'phone'      			=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_PHONE'),
			'phone_extension'   	=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_PHONE_EXTENSION'),
			'ship_name'      		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_NAME'),
			'ship_street_address'   => JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_STREET_ADDRESS'),
			'ship_street_address2'  => JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_STREET_ADDRESS2'),
			'ship_city'      		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_CITY'),
			'ship_state'      		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_STATE'),
			'ship_zip'      		=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_ZIP'),
			'ship_country'      	=> JText::_('PLG_SYSTEM_RSFP2CHECKOUT_SHIP_COUNTRY')
		);
		?>
		<div id="rsfp2checkoutdiv">
			<table class="admintable">
				<tr>
					<td valign="top" align="left" width="30%">
						<table class="table table-bordered">
							<?php
							foreach ($merge_vars as $merge_var => $title)
							{
								?>
								<tr class="2checkout_fields">
									<td width="80" class="key" nowrap="nowrap" align="right">
										<label for="<?php echo '2checkout_vars' . $merge_var ?>"><?php echo $title; ?></label></td>
									<td><?php echo JHtml::_('select.genericlist', $fields, '2checkout_vars[' . $merge_var . ']', null, 'value', 'text', isset($row->vars[$merge_var]) ? $row->vars[$merge_var] : null); ?></td>
								</tr>
								<?php
							}
							?>
						</table>
					</td>
				</tr>
			</table>
		</div>
		<?php
	}

	protected function _getFields($formId)
	{
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select($db->qn('p.PropertyValue'))
			->from($db->qn('#__rsform_components', 'c'))
			->join('LEFT', $db->qn('#__rsform_properties', 'p') . ' ON (' . $db->qn('c.ComponentId') . '=' . $db->qn('p.ComponentId') . ')')
			->where($db->qn('c.FormId') . '=' . $db->q($formId))
			->where($db->qn('p.PropertyName') . '=' . $db->q('NAME'))
			->order($db->qn('c.Order') . ' ' . $db->escape('ASC'));

		return $db->setQuery($query)->loadColumn();
	}

	/**
	 * Add a grand total and tax placeholder
	 * @param $args
	 */
	public function onRsformAfterCreatePlaceholders($args)
	{
		$choosePayment = RSFormProHelper::componentExists($args['form']->FormId, RSFORM_FIELD_PAYMENT_CHOOSE);
		if ($choosePayment)
		{
			$properties = RSFormProHelper::getComponentProperties($choosePayment[0]);
			$fieldName  = $properties['NAME'];

			if (!isset($args['submission']->values[$fieldName]))
			{
				return;
			}

			if ($args['submission']->values[$fieldName] != $this->componentValue)
			{
				return;
			}

			$grandTotal = $this->calcTax($args['submission']->values['rsfp_Total'], RSFormProHelper::getConfig('2checkout.tax.value'), RSFormProHelper::getConfig('2checkout.tax.type'));

			$placeholders = &$args['placeholders'];
			$values = &$args['values'];

			$placeholders[] = '{grandtotal}';
			$values[] = $this->number_format($grandTotal);

			$placeholders[] = '{tax}';
			$values[] = $this->number_format($grandTotal - $args['submission']->values['rsfp_Total']);
		}
	}

	/**
	 * @param $placeholders
	 * @param $componentId
	 *
	 * @return mixed
	 * @since 2.0.0
	 */
	public function onRsformAfterCreateQuickAddPlaceholders(& $placeholders, $componentId)
	{
		if ($componentId == $this->componentId)
		{
			$placeholders['display'][] = '{grandtotal}';
			$placeholders['display'][] = '{tax}';
		}

		return $placeholders;
	}

	public function onRsformGetPayment(&$items, $formId)
	{
		if ($components = RSFormProHelper::componentExists($formId, $this->componentId))
		{
			$data = RSFormProHelper::getComponentProperties($components[0]);

			$item 			= new stdClass();
			$item->value 	= $this->componentValue;
			$item->text 	= $data['LABEL'];
			
			if ($tax = RSFormProHelper::getConfig('2checkout.tax.value'))
			{
				$item->tax = $tax;
				$item->tax_type = RSFormProHelper::getConfig('2checkout.tax.type') == '0' ? 'percent' : 'fixed';

			}

			// add to array
			$items[] = $item;
		}
	}

	public function onRsformDoPayment($payValue, $formId, $SubmissionId, $price, $products, $code)
	{
		// execute only for our plugin
		if ($payValue != $this->componentValue)
		{
			return;
		}

		if ($price > 0)
		{
			list($replace, $with) = RSFormProHelper::getReplacements($SubmissionId);

			$args = array(
				'mode'				 => '2CO',
				'currency_code' 	 => RSFormProHelper::getConfig('payment.currency'),
				'code'				 => $code,
				'form_id'			 => $formId,
				'task'				 => 'plugin',
				'plugin_task'		 => '2checkout.notify',
				'x_receipt_link_url' => JUri::root() . 'index.php?option=com_rsform&task=plugin&plugin_task=2checkout.notify'
			);

			$args['sid'] = RSFormProHelper::getConfig('2checkout.merchant_code');

			if (RSFormProHelper::getConfig('2checkout.sandbox'))
			{
				$args['demo'] = 'Y';
			}

			// Add our products
			$args['li_0_type'] = 'product';
			$args['li_0_name'] = implode(', ', $products);
			$args['li_0_quantity'] = 1;
			$args['li_0_price'] = number_format($price, 2, '.', '');

			// Add tax
			if ($tax = RSFormProHelper::getConfig('2checkout.tax.value'))
			{
				$grandTotal = $this->calcTax($price, RSFormProHelper::getConfig('2checkout.tax.value'), RSFormProHelper::getConfig('2checkout.tax.type'));
				$tax = number_format($grandTotal - $price, 2, '.', '');

				$args['li_1_type'] = 'tax';
				$args['li_1_price'] = $tax;
			}

			// Add mapped fields
			$row = $this->getTable();

			if ($row->load($formId))
			{
				foreach ($row->vars as $field => $rsformField)
				{
					if ($rsformField)
					{
						$args[$field] = str_replace($replace, $with, '{' . $rsformField . ':value}');
					}
				}
			}

			// Get a new instance of the 2Checkout object. This is used so that we can change values sent to 2Checkout through the "Scripts" areas.
			$instance = RSFormPro2Checkout::getInstance();

			// If any options have already been set, use this to override the ones used here
			$instance->args = array_merge($args, $instance->args);

			JFactory::getApplication()->redirect($instance->url.'?'.http_build_query($instance->args, '', '&'));
		}
	}

	protected function doReturn($formId){
		// Get session object
		$session = JFactory::getSession();
		$app = JFactory::getApplication();
		// Get data from session
		$formparams = $session->get('com_rsform.formparams.formId'.$formId);
		if ($formparams && $formparams->redirectUrl)
		{
			// Mark form as processed
			$formparams->formProcessed = true;

			// Store new session data
			$session->set('com_rsform.formparams.formId'.$formId, $formparams);

			// Redirect
			$app->redirect($formparams->redirectUrl);
		}
	}

	public function onRsformBackendAfterCreateComponentPreview($args = array())
	{
		if ($args['data']['componentTypeId'] == $this->componentId)
		{
			$args['out'] = '<span style="font-size:24px;margin-right:5px" class="rsficon rsficon-coin-dollar"></span> '.$args['data']['LABEL'];
		}
	}

	public function onRsformBackendAfterShowConfigurationTabs($tabs)
	{
		$tabs->addTitle(JText::_('PLG_SYSTEM_RSFP2CHECKOUT_LABEL'), 'form-2checkout');
		$tabs->addContent($this->configurationScreen());
	}

	public function onRsformDefineHiddenComponents(&$hiddenComponents)
	{
		$hiddenComponents[] = $this->componentId;
	}

	/**
	 * Helper function to write log entries
	 */
	protected function writeLog()
	{
		// Need to separate IPN entries
		$this->addLogEntry("----------------------------- \n");

		$config   = JFactory::getConfig();
		$log_path = $config->get('log_path') . '/rsformpro_2checkout_log.php';
		$log      = implode("\n", $this->log);

		/**
		 * If it's the first time we write in it, we need to add die() at the beginning of the file
		 */
		if (is_writable($config->get('log_path')))
		{
			if (!file_exists($log_path))
			{
				file_put_contents($log_path, "<?php die(); ?>\n");
			}

			/**
			 * we start appending log entries
			 */
			file_put_contents($log_path, $log, FILE_APPEND);
		}
	}

	/**
	 * Helper function to add messages to the log
	 *
	 * @param $message
	 */
	protected function addLogEntry($message)
	{
		$this->log[] = JFactory::getDate()->toSql() . ' : ' . $message;
	}

	public function onRsformFrontendSwitchTasks()
	{
		$input = JFactory::getApplication()->input;

		// Notification receipt
		if ($input->getString('plugin_task', '') == '2checkout.notify')
		{
			$code 	= $input->getCmd('code');
			$formId = $input->getInt('form_id');
			$this->addLogEntry('IPN received from 2Checkout');

			try
			{
				$this->validateIpn();
				$SubmissionId = $this->_getSubmissionId($formId, $code);
				if ($SubmissionId)
				{
					$this->addLogEntry('Update database');

					$db = JFactory::getDbo();
					$query = $db->getQuery(true);
					$query->update($db->qn('#__rsform_submission_values'))
						->set($db->qn('FieldValue') . ' = ' . $db->q(1))
						->where($db->qn('FieldName') . ' = ' . $db->q('_STATUS'))
						->where($db->qn('FormId') . ' = ' . $db->q($formId))
						->where($db->qn('SubmissionId') . ' = ' . $db->q($SubmissionId));
					$db->setQuery($query);
					$db->execute();

					$this->addLogEntry('Successfully updated database');

					// Add order_number to _TRANSACTION_ID field
					if ($order_number = $input->getString('order_number'))
					{
						$query = $db->getQuery(true);
						$query->update($db->qn('#__rsform_submission_values'))
							->set($db->qn('FieldValue') . ' = ' . $db->q($order_number))
							->where($db->qn('FieldName') . ' = ' . $db->q('_TRANSACTION_ID'))
							->where($db->qn('FormId') . ' = ' . $db->q($formId))
							->where($db->qn('SubmissionId') . ' = ' . $db->q($SubmissionId));
						$db->setQuery($query);
						$db->execute();
					}

					JFactory::getApplication()->triggerEvent('onRsformAfterConfirmPayment', array($SubmissionId));
					$this->addLogEntry('Payment confirmed');
					$this->writeLog();
				}
			}
			catch (Exception $e)
			{
				$message = 'Validation failed -> ' . $e->getMessage();
				$this->addLogEntry($message);
				$this->addLogEntry('POST data is:');
				$this->addLogEntry(print_r($_REQUEST, true));

				$this->writeLog();

				JFactory::getApplication()->enqueueMessage(JText::_('PLG_SYSTEM_RSFP2CHECKOUT_THERE_WAS_AN_ERROR'), 'error');
			}
			$this->doReturn($formId);
		}
	}

	private function validateIpn()
	{
		$input 		= JFactory::getApplication()->input;

		$ordernumber 		= RSFormProHelper::getConfig('2checkout.sandbox') ? 1 : $input->get('order_number', '', 'string');
		$sid 				= RSFormProHelper::getConfig('2checkout.merchant_code');
		$secret_word		= RSFormProHelper::getConfig('2checkout.secret_word');
		$total 		 		= $input->get('total', '', 'string');
		$processed 	 		= $input->get('credit_card_processed', '', 'cmd');
		$key 		 		= $input->get('key', '', 'cmd');

		if ($processed !== 'Y')
		{
			throw new Exception('Credit card has not been processed.');
		}

		$hash = strtoupper(md5($secret_word.$sid.$ordernumber.$total));
		if ($hash !== $key)
		{
			throw new Exception("Computed hash ({$hash}) does not match received hash ({$key}).");
		}
	}

	private function loadFormData()
	{
		$data 	= array();
		$db 	= JFactory::getDbo();

		$query = $db->getQuery(true)
			->select('*')
			->from($db->qn('#__rsform_config'))
			->where($db->qn('SettingName') . ' LIKE ' . $db->q('2checkout.%', false));
		if ($results = $db->setQuery($query)->loadObjectList())
		{
			foreach ($results as $result)
			{
				$data[$result->SettingName] = $result->SettingValue;
			}
		}

		return $data;
	}

	private function configurationScreen()
	{
		ob_start();

		JForm::addFormPath(__DIR__ . '/forms');
		JForm::addFieldPath(__DIR__ . '/fields');

		$form = JForm::getInstance( 'plg_system_rsfp2checkout.configuration', 'configuration', array('control' => 'rsformConfig'), false, false );
		$form->bind($this->loadFormData());

		?>
		<div id="page-2checkout" class="form-horizontal">
			<?php
			foreach ($form->getFieldsets() as $fieldset)
			{
				if ($fields = $form->getFieldset($fieldset->name))
				{
					foreach ($fields as $field)
					{
						// This is a workaround because our fields are named "2checkout." and Joomla! uses the dot as a separator and transforms the JSON into [2checkout][apikey] instead of [2checkout.apikey].
						echo str_replace('"rsformConfig[2checkout][', '"rsformConfig[2checkout.', $form->renderField($field->fieldname));
					}
				}
			}
			?>
		</div>
		<?php

		$contents = ob_get_contents();
		ob_end_clean();
		return $contents;
	}

	/**
	 * @param $price
	 * @param $amount
	 * @param $type
	 *
	 * @return mixed
	 */
	public function calcTax($price, $amount, $type)
	{
		$price = (float) $price;
		$amount = (float) $amount;
		switch ($type)
		{
			case false:
				$price = $price + (($price * $amount) / 100);
				break;

			case true:
				$price = $price + $amount;
				break;
		}

		return $price;
	}

	protected function _getSubmissionId($formId, $code){
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true);
		$query->select($db->qn('SubmissionId'))
			->from($db->qn('#__rsform_submissions', 's'))
			->where($db->qn('s.FormId') . ' = ' . $db->q($formId))
			->where('MD5(CONCAT(' . $db->qn('s.SubmissionId') . ',' . $db->qn('s.DateSubmitted') . ')) = ' . $db->q($code));
		$db->setQuery($query);

		if ($SubmissionId = $db->loadResult())
		{
			return $SubmissionId;
		}

		return false;
	}

	public function onRsformFormDelete($formId)
	{
		if ($row = $this->getTable())
		{
			$row->delete($formId);
		}
	}

	public function onRsformBackendFormCopy($args)
	{
		$formId = $args['formId'];
		$newFormId = $args['newFormId'];

		if ($row = $this->getTable())
		{
			if ($row->load($formId))
			{
				$row->form_id = $newFormId;
				if ($row->check())
				{
					$row->store();
				}
			}
		}
	}

	/**
	 * @param $form
	 * @param $xml
	 * @param $fields
	 */
	public function onRsformFormBackup($form, $xml, $fields)
	{
		if ($row = $this->getTable())
		{
			if ($row->load($form->FormId))
			{
				$row->check();

				$data = $row->getProperties();
				unset($data['form_id']);

				$xml->add('twocheckout');
				foreach ($data as $property => $value)
				{
					$xml->add($property, $value);
				}
				$xml->add('/twocheckout');
			}
		}
	}

	/**
	 * @param $form
	 * @param $xml
	 * @param $fields
	 */
	public function onRsformFormRestore($form, $xml, $fields)
	{
		if (isset($xml->twocheckout))
		{
			$data = array(
				'form_id' => $form->FormId
			);
			foreach ($xml->twocheckout->children() as $property => $value)
			{
				$data[$property] = (string) $value;
			}
			$row = $this->getTable();
			$row->save($data);
		}
	}

	/**
	 *
	 */
	public function onRsformBackendFormRestoreTruncate()
	{
		JFactory::getDbo()->truncateTable('#__rsform_2checkout');
	}

	private function number_format($val)
	{
		return number_format((float) $val, RSFormProHelper::getConfig('payment.nodecimals'), RSFormProHelper::getConfig('payment.decimal'), RSFormProHelper::getConfig('payment.thousands'));
	}
}

class RSFormPro2Checkout
{
	public $args = array();

	public $url = 'https://www.2checkout.com/checkout/purchase';

	public function __construct(){}

	public static function getInstance()
	{
		static $inst;
		if (!$inst)
		{
			$inst = new RSFormPro2Checkout;
		}

		return $inst;
	}
}