<?php
/**
 * 2007-2021 PrestaShop
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to http://www.prestashop.com for more information.
 *
 * @author    PrestaShop SA <contact@prestashop.com>
 * @copyright 2007-2021 PrestaShop SA
 * @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License
 *            (AFL 3.0) International Registered Trademark & Property of
 *            PrestaShop SA
 */

namespace Prestashop\Module\AddonPayments\Operations;

use ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs;
use ComerciaGlobalPayments\AddonPayments\SDK\Request\Voided;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponseInterface;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\OperationInterface;
use Exception;
use Order;
use Prestashop\Module\AddonPayments\Exception\GatewayException;
use Prestashop\Module\AddonPayments\OperationContext;
use Prestashop\Module\AddonPayments\Services\ApiFactory;
use PrestaShopLogger;

/**
 * Handles full order refund when the transaction haven't been captured.
 *
 * This operation checks the transactions' registry for pending transactions that should be voided.
 */
final class OrderVoid extends ApiBaseOperation
{
    use RefundPaymentsTrait;

    /**
     * @var \Context
     */
    private $context;

    /**
     * @var \addonpayments
     */
    private $module;

    /**
     * ProcessTransaction constructor.
     *
     * @param $context
     * @param $module
     */
    public function __construct($context, $module)
    {
        $this->context = $context;
        $this->module = $module;
    }

    public function __invoke(Order $order)
    {
        if ((bool)$order->hasBeenPaid()) {
            return;
        }

        /** @var \ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs $apClient */
        $apClient = ApiFactory::createInstance(EPGJs::NAME);
        $transactions = \Transactions::getVoidableByOrder($order->id);
        foreach ($transactions as $transaction) {
            try {
                $request = (new Voided())
                    ->setMerchantTransactionId($transaction->merchantTransactionId)
                    ->setPaymentSolution($transaction->paymentSolution)
                    ->setTransactionId($transaction->payFrexTransactionId)
                    ->setDescription(sprintf($this->module->l('Void order %s', 'ordervoid'), $order->reference))
                    ->setMerchantParams(
                        [
                            'reference' => $order->reference,
                            'employee'  => $this->context->employee->id,
                            'customer'  => $this->context->customer->id,
                            'void'      => true,
                        ]
                    );
                /** @var \ComerciaGlobalPayments\AddonPayments\SDK\Response\OperationInterface|\ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponseInterface $response */
                $response = $apClient->voided($request);

                if ($response instanceof ErrorResponseInterface) {
                    $this->handleErrorResponse($response);
                }

                if (0 === $response->count()) {
                    throw new GatewayException(
                        'The response does not contain any operations.',
                        GatewayException::NO_TRANSACTIONS
                    );
                }

                if (OperationInterface::STATUS_SUCCESS
                    !== $response->getTransaction()->getStatus()) {
                    throw new GatewayException(
                        "Void request failed for transaction {$transaction->payFrexTransactionId}",
                        GatewayException::OPERATION_FAILED
                    );
                }

                $operationContext = new OperationContext(
                    $order->id,
                    $transaction->amount,
                    $order->id_currency,
                    $order->secure_key
                );
                $this->processTransactions($response, $operationContext);
            } catch (Exception $e) {
                $message
                    =
                    "Could not perform void operation for transaction {$transaction->payFrexTransactionId}: {$e->getMessage()}";
                PrestaShopLogger::addLog($message, 3, $e->getCode(),
                    'Transactions', $transaction->id_transaction);
            }
        }
    }
}
