<?php
/**
 * @package     CMGiftCard
 * @subpackage  plg_cmgiftcard_paybox
 * @copyright   Copyright (C) 2016-2024 CMExension
 * @license     GNU General Public License version 2 or later
 */

defined('_JEXEC') or die;

require_once JPATH_ROOT . '/administrator/components/com_cmgiftcard/helpers/plugin/cmgiftcard.php';

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

/**
 * Paybox payment method for CMGiftCard.
 *
 * @package     CMGiftCard
 * @subpackage  plg_cmgiftcard_paybox
 * @since       1.2.1
 */
class PlgCMGiftCardPaybox extends CMGiftCardPlugin
{
	/**
	 * Constructor
	 *
	 * @param   object  &$subject  The object to observe
	 * @param   array   $config    An optional associative array of configuration settings.
	 *                             Recognized key values include 'name', 'group', 'params', 'language'
	 *                             (this list is not meant to be comprehensive).
	 *
	 * @since   1.2.1
	 */
	public function __construct(&$subject, $config = array())
	{
		$this->gatewayId = 'paybox';
		$this->gatewayName = 'PLG_CMGIFTCARD_PAYBOX_NAME';

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

	/**
	 * Method to get payment form.
	 *
	 * @param   array  $data  Billing data.
	 *
	 * @return  array
	 *
	 * @since   1.2.1
	 */
	public function onCMGCGetPaymentForm($data)
	{
		$gatewayId = isset($data['billing']['payment_method']) ? $data['billing']['payment_method'] : '';

		if ($gatewayId != $this->gatewayId)
			return false;

		$gatewayName = $this->params->get('name', Text::_($this->gatewayName));
		$secretKey = $this->params->get('secret_key');
		$pbxSite = $this->params->get('site');
		$pbxRang = $this->params->get('rank');
		$pbxIdentifiant = $this->params->get('identifier');
		$pbxDevise = (int) $this->params->get('currency', '978');
		$pbxTotal = $data['total'] * 100;
		$pbxCmd = $data['billing']['order_number'];
		$pbxPorteur = $data['billing']['email'];
		$siteUrl = str_replace(Uri::root(true), '', substr(Uri::root(), 0, -1));
		$pbxEffectue = $siteUrl . CMGiftCardHelper::prepareRoute('index.php?option=com_cmgiftcard&view=checkout') . '?layout=success';
		$pbxAnnule = $siteUrl . CMGiftCardHelper::prepareRoute('index.php?option=com_cmgiftcard&view=checkout') . '?layout=cancel';
		$pbxRepondreA = Uri::root() . 'index.php?option=com_cmgiftcard&task=callback&gateway=' . $this->gatewayId;
		$pbxTime = date('c');
		$algorithm = 'SHA512';

		$firstName = $data['billing']['first_name'];
		$lastName = $data['billing']['last_name'];
		$addressLine1 = $data['billing']['address_1'] ?? '';
		$addressLine2 = $data['billing']['address_2'] ?? '';
		$city = $data['billing']['city'] ?? '';
		$countryCode = $data['billing']['postal_code'] ?? '';

		$pbxShoppingCartItems = '<items>';

		foreach ($data['items'] as $n => $item)
		{
			$pbxShoppingCartItems .= '<item>';
			$pbxShoppingCartItems .= '<label>' . $item['design_title'] . '</label>';
			$pbxShoppingCartItems .= '<price>' . $item['amount'] * 100 . '</price>';
			$pbxShoppingCartItems .= '<quantity>1</quantity>';
			$pbxShoppingCartItems .= '</item>';
		}

		$pbxShoppingCartItems .= '</items>';

		$pbxShoppingCart =  sprintf('<?xml version="1.0" encoding="utf-8"?><shoppingcart><total><totalQuantity>%d</totalQuantity></total>%s</shoppingcart>', count($data['items']), $pbxShoppingCartItems);
		$pbxBilling =  sprintf(
			'<?xml version="1.0" encoding="utf-8"?><Billing><Address><FirstName>%s</FirstName><LastName>%s</LastName><Address1>%s</Address1><Address2>%s</Address2><ZipCode>%s</ZipCode><City>%s</City><CountryCode>%s</CountryCode></Address></Billing>',
			$firstName,
			$lastName,
			$addressLine1,
			$addressLine2,
			$zipCode,
			$city,
			$countryCode
		);

		$formData = [
			'PBX_SITE'			=> $pbxSite,
			'PBX_RANG'			=> $pbxRang,
			'PBX_IDENTIFIANT'	=> $pbxIdentifiant,
			'PBX_TOTAL'			=> $pbxTotal,
			'PBX_DEVISE'		=> $pbxDevise,
			'PBX_CMD'			=> $pbxCmd,
			'PBX_PORTEUR'		=> $pbxPorteur,
			'PBX_RETOUR'		=> 'Amount:M;Reference:R;AuthNum:A;Error:E',
			'PBX_HASH'			=> $algorithm,
			'PBX_TIME'			=> $pbxTime,
			'PBX_REPONDRE_A'	=> urlencode($pbxRepondreA),
			'PBX_RUF1'			=> 'POST',
			'PBX_ANNULE'		=> urlencode($pbxAnnule),
			'PBX_EFFECTUE'		=> urlencode($pbxEffectue),
			'PBX_SHOPPINGCART'	=> $pbxShoppingCart,
			'PBX_BILLING'		=> $pbxBilling,
		];

		ksort($formData);

		$string = '';
		$count = 0;

		foreach ($formData as $key => $value)
		{
			$count++;
			$string .= $key . '=' . $value;

			if ($count < count($formData))
			{
				$string .= '&';
			}
		}

		$binKey = pack("H*", $secretKey);
		$formData['PBX_HMAC'] = strtoupper(hash_hmac($algorithm, $string, $binKey));

		$environment = $this->params->get('environment', 'live');
		$secondsToWait = (int) $this->params->get('seconds_to_wait', 5);

		$clickHereLink = '<a href="#" onClick="document.checkOutForm.submit(); return false;">' . Text::_('COM_CMGIFTCARD_CLICK_HERE') . '</a>';
		$checkoutMessage = Text::sprintf('COM_CMGIFTCARD_CHECKOUT_MESSAGE', $gatewayName, $secondsToWait, $clickHereLink);

		if ($environment == 'live')
		{
			$url = 'https://urlserveur.paybox.com/cgi/MYchoix_pagepaiement.cgi';
		}
		else
		{
			$url = 'https://preprod-tpeweb.paybox.com/cgi/MYchoix_pagepaiement.cgi';
		}

		foreach ($formData as $key => $value)
		{
			$formData[$key] = htmlspecialchars($value, ENT_QUOTES);
		}

		@ob_start();
		include dirname(__FILE__) . '/form.php';
		$html = @ob_get_clean();

		return $html;
	}

	/**
	 * Method to get validate Paybox callback.
	 *
	 * @return  boolean
	 *
	 * @since   1.2.1
	 */
	public function onCMGCValidateCallback()
	{
		$jinput = Factory::getApplication()->input;
		$gatewayId = $jinput->get('gateway', '');
		$data = $jinput->post->getArray();
		$db = Factory::getDbo();
		$query = $db->getQuery(true);

		$result = array(
			'validation'		=> false,
			'gateway_id'		=> '',
			'transaction_id'	=> '',
			'order_id'			=> ''
		);

		if ($gatewayId != $this->gatewayId || empty($data))
		{
			return $result;
		}

		$isValid = true;

		if (!isset($data['Error']))
		{
			$isValid = false;
			$reason = 'Missing Error parameter';
		}

		if ($isValid && $data['Error'] != '00000')
		{
			$isValid = false;
			$reason = 'Paybox reported transaction as invalid';
		}

		if (!$isValid)
		{
			$reason = 'Paybox reported transaction as invalid';
		}

		/**
		 * At https://www.paybox.com/espace-integrateur-documentation/la-solution-paybox-system/urls-dappels-et-adresses-ip/
		 * they say that 195.25.67.22 is a production server
		 * but it is the one sending callback in test mode so the validation is failed.
		 */
		/*
		if ($isValid)
		{
			$ipAddr = $_SERVER['REMOTE_ADDR'];
			$environment = $this->params->get('environment', 'live');

			if ($environment == 'production')
			{
				$payboxIpAddrs = [
					'194.2.122.158',
					'194.2.122.190',
					'195.25.7.166',
					'195.25.67.22'
				];
			}
			else
			{
				$payboxIpAddrs = [
					'195.101.99.76'
				];
			}

			if (!in_array($ipAddr, $payboxIpAddrs))
			{
				$isValid = false;
				$reason = "$ipAddr was not from Paybox's servers";
			}
		}
		*/

		if ($isValid)
		{
			$orderId = isset($data['Reference']) ? (int) $data['Reference'] : 0;

			$query->clear()
				->select('*')
				->from($db->qn('#__cmgiftcard_orders'))
				->where($db->qn('order_number') . ' = ' . $db->q($orderId));
			$db->setQuery($query);
			$order = $db->loadObject();

			if (!isset($order->id))
			{
				$isValid = false;
				$reason = "Order number $orderId didn't exist";
			}

			if ($isValid && isset($order->order_status) && $order->order_status == 'paid')
			{
				$isValid = false;
				$reason = "Order number $orderId was already paid.";
			}

			if ($isValid && isset($order->order_status) && $order->order_status == 'refunded')
			{
				$isValid = false;
				$reason = "Order number $orderId was already refunded.";
			}
		}

		if ($isValid)
		{
			$query->clear()
				->select('*')
				->from($db->qn('#__cmgiftcard_cards'))
				->where($db->qn('order_id') . ' = ' . $db->q($order->id));
			$cards = $db->setQuery($query)->loadObjectList();

			if (count($cards))
			{
				$amount = 0;

				foreach ($cards as $card)
				{
					$amount += $card->amount;
				}

				$requestAmount = floatval($data['Amount'] / 100);
				$isValid = ($amount - $requestAmount) < 0.01;

				if (!$isValid)
				{
					$reason = "Invalid paid amount, expected $amount, got $requestAmount";
				}
			}
			else
			{
				$isValid = false;
				$reason = "No gift cards found";
			}
		}

		$result['valid'] = $isValid;

		if ($isValid)
		{
			$result['gateway_id'] = $this->gatewayId;
			$result['transaction_id'] = isset($data['AuthNum']) ? $data['AuthNum'] : '';
			$result['order_id'] = $order->id;
		}
		else
		{
			$data['failure_reason'] = $reason;
		}

		$this->logNotification($data, $isValid);

		return $result;
	}
}
