<?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\Refund;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponseInterface;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\OperationInterface;
use Exception;
use function in_array;
use Order;
use OrderPayment;
use Prestashop\Module\AddonPayments\Exception\GatewayException;
use Prestashop\Module\AddonPayments\Exception\NotReallyAnException;
use Prestashop\Module\AddonPayments\OperationContext;
use Prestashop\Module\AddonPayments\Services\ApiFactory;
use PrestaShopLogger;

/**
 * Handles full order refund.
 *
 * This operation loads an order payments and checks them against the transactions registry,
 * if matches the payment is refunded.
 */
final class OrderRefund 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, $amount = null)
    {
        if (!$order->hasBeenPaid()) {
            // This is a 'cancel product' so no need for action.
            return;
        }

        /** @var \OrderPayment[] $payments */
        $payments = OrderPayment::getByOrderReference($order->reference);

        if (empty($payments)) {
            throw new NotReallyAnException("The order {$order->id} does not have any refundable transactions");
        }

        $processed = [];
        /** @var \ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs $apClient */
        $apClient = ApiFactory::createInstance(EPGJs::NAME);
        foreach ($payments as $payment) {
            if ($payment->payment_method !== $this->module->name) {
                continue;
            }

            if (in_array((string) $payment->transaction_id, $processed, true)) {
                continue;
            }

            $transaction = $this->findTransaction($order, $payment->transaction_id);

            if (null === $transaction) {
                continue;
            }

            if('quix' !== $transaction->paymentSolution && 'nemuru' !== $transaction->paymentSolution) {
                $amount = !empty($amount) ? $amount : $transaction->amount;
            }

            try {
                $request = (new Refund())
                    ->setMerchantTransactionId($transaction->merchantTransactionId)
                    ->setAmount($amount)
                    ->setPaymentSolution($transaction->paymentSolution)
                    ->setTransactionId($transaction->payFrexTransactionId)
                    ->setDescription(sprintf($this->module->l('Complete refund for order %s', 'orderrefund'), $order->reference))
                    ->setMerchantParams(
                        [
                            'reference'   => $order->reference,
                            'employee'    => $this->context->employee->id,
                            'customer'    => $this->context->customer->id,
                            'full_refund' => true,
                        ]
                    );
                $response = $apClient->refund($request);

                if( $response instanceof OperationInterface && $response->hasFailed()) {
                    $transactions = $response->getOperations();
                    $operations = count($transactions);

                    $operationContext = new OperationContext(
                        $order->id,
                        $this->context->cart->getOrderTotal(),
                        $this->context->cart->id_currency,
                        $this->context->cart->secure_key
                    );

                    foreach ($transactions as $transaction) {
                        (new ProcessTransaction($operationContext, $this->module))(
                            $transaction,
                            $transaction->getSortedOrder() === $operations
                        );
                    }

                    $transaction = $response->getTransaction();

                    return $transaction->getRespCode();
                }

                if ('quix' === $transaction->paymentSolution || 'nemuru' === $transaction->paymentSolution) {
                    return $response;
                }

                return false;

            } catch (Exception $e) {
                $message = "Could not perform refund operation for transaction {$payment->transaction_id}: {$e->getMessage()}";
                PrestaShopLogger::addLog($message, 3, $e->getCode(), 'Transactions', $payment->transaction_id);
            }
        }
    }
}
