<?php

/**
 * Copyright © Resurs Bank AB. All rights reserved.
 * See LICENSE for license details.
 */

declare(strict_types=1);

namespace Resursbank\MerchantApi\Controller\Admin\Order;

use Db;
use Exception;
use JsonException;
use OrderSlip;
use PrestaShopDatabaseException;
use PrestaShopException;
use ReflectionException;
use Resursbank\Ecom\Exception\ApiException;
use Resursbank\Ecom\Exception\AttributeCombinationException;
use Resursbank\Ecom\Exception\AuthException;
use Resursbank\Ecom\Exception\ConfigException;
use Resursbank\Ecom\Exception\CurlException;
use Resursbank\Ecom\Exception\Validation\EmptyValueException;
use Resursbank\Ecom\Exception\Validation\IllegalTypeException;
use Resursbank\Ecom\Exception\Validation\IllegalValueException;
use Resursbank\Ecom\Exception\Validation\NotJsonEncodedException;
use Resursbank\Ecom\Exception\ValidationException;
use Resursbank\Ecom\Lib\Utilities\Price;
use Resursbank\Ecom\Module\Payment\Repository;
use Resursbank\MerchantApi\Exception\InvalidDataException;
use Resursbank\MerchantApi\Exception\PaymentException;
use Resursbank\MerchantApi\Service\Order;
use Resursbank\MerchantApi\Util\Log;
use Resursbank\MerchantApi\Util\Translator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;

/**
 * Refund entire, remaining, payment at Resurs Bank.
 */
class Refund extends Aftershop
{
    /**
     * Refund payment.
     */
    public function execute(Request $request): RedirectResponse
    {
        try {
            $payment = $this->getMapiPayment(request: $request);
            $order = $this->getOrder(request: $request);

            if (!$payment->canRefund() ||
                !Order::isResursbank(orderReference: $order->reference)
            ) {
                throw new PaymentException(
                    message: Translator::translate('payment-cannot-be-refunded')
                );
            }

            $payment2 = Repository::refund(paymentId: $payment->id);

            $this->session->getFlashBag()->add(
                type: 'success',
                message: sprintf(
                    Translator::translate('refund-success'),
                    Price::format($payment2->order->refundedAmount - $payment->order->refundedAmount)
                )
            );

            $this->setOrderStatus(request: $request, status: Order::ORDER_STATE_REFUNDED);

            // Create credit slip.
            $order = $this->getOrder(request: $request);
            OrderSlip::create($order, $order->getProductsDetail(), null, ((float) $order->total_paid_tax_incl + $order->total_shipping_tax_incl));

            // Mark all products as refunded.
            $this->updateProductQuantities(request: $request);
        } catch (Exception $e) {
            Log::error(message: $e);
            $this->session->getFlashBag()->add(
                type: 'error',
                message: Translator::translate('failed-to-refund-payment') . ' (' . $e->getMessage() . ')'
            );
        }

        return $this->redirectBack();
    }

    /**
     * Update the quantityRefunded for each product in the order.
     *
     * @param Request $request
     * @throws AttributeCombinationException
     * @throws JsonException
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     * @throws ReflectionException
     * @throws ApiException
     * @throws AuthException
     * @throws ConfigException
     * @throws CurlException
     * @throws ValidationException
     * @throws EmptyValueException
     * @throws IllegalTypeException
     * @throws IllegalValueException
     * @throws NotJsonEncodedException
     */
    private function updateProductQuantities(Request $request)
    {
        $order = $this->getOrder(request: $request);
        $orderDetails = $order->getOrderDetailList();

        foreach ($orderDetails as $orderDetail) {
            $detailId = (int) $orderDetail['id_order_detail'];

            if ($detailId === 0) {
                throw new InvalidDataException('Invalid order detail data.');
            }

            $qty = (int) $orderDetail['product_quantity'];

            if ($qty === 0) {
                continue;
            }

            Db::getInstance()->update(
                'order_detail',
                [
                    'product_quantity_refunded' => $qty,
                ],
                'id_order_detail = ' . $detailId
            );
        }
    }
}
