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

namespace Resursbank\MerchantApi\Hook;

use Carrier;
use JsonException;
use Order;
use OrderSlip;
use ReflectionException;
use Resursbank\Ecom\Exception\AttributeCombinationException;
use Resursbank\Ecom\Lib\Model\Payment\Order\ActionLog\OrderLine;
use Resursbank\Ecom\Lib\Order\OrderLineType;
use Resursbank\Ecom\Module\Payment\Repository;
use Resursbank\MerchantApi\Repository\ResursbankOrder;
use Resursbank\MerchantApi\Traits\AdminOrderHook;
use Resursbank\MerchantApi\Util\Log;
use Resursbank\MerchantApi\Service\Order as OrderService;
use Throwable;
use Validate;

/**
 * Hook handler for partial refund order slips.
 */
class ActionOrderSlipAdd
{
    use AdminOrderHook;

    /**
     * @param ResursbankOrder $orderRepository
     */
    public function __construct(
        private readonly ResursbankOrder $orderRepository
    ) {
    }

    /**
     * Perform partial refund.
     *
     * @param array $parameters
     * @return bool
     */
    public function exec(array $parameters): bool
    {
        try {
            $orderId = $parameters['order']->id;
            $order = $this->getOrder(orderId: $orderId);
            $mapiPayment = $this->getMapiPayment(
                orderId: $orderId,
                repository: $this->orderRepository
            );
        } catch (Throwable $error) {
            Log::error($error);
            return false;
        }

        if (!Validate::isLoadedObject(object: $order)) {
            return false;
        }

        try {
            $converted = OrderService::convertRefund(
                order: $order,
                productList: $parameters['productList']
            );

            // Load latest order slip (appears to always be index 0)
            $latestSlip = OrderSlip::getOrdersSlip(
                customer_id: $parameters['order']->id_customer,
                order_id: $orderId
            )[0];

            // Add shipping order line to converted order line collection.
            if ($latestSlip['shipping_cost']) {
                $convertedData = $converted->getData();
                $convertedData[] = $this->getShippingLine(
                    order: $order,
                    orderSlip: $latestSlip
                );
                $converted->setData(data: $convertedData);
            }

            // Perform refund
            Repository::refund(
                paymentId: $mapiPayment->id,
                orderLines: $converted
            );

            return true;
        } catch (Throwable $error) {
            Log::error($error);
        }

        return false;
    }

    /**
     * Generate the shipping order line for partial refund.
     *
     * @param Order $order
     * @param array $orderSlip
     * @return OrderLine
     * @throws JsonException
     * @throws ReflectionException
     * @throws AttributeCombinationException
     */
    private function getShippingLine(Order $order, array $orderSlip): OrderLine
    {
        $carrier = new Carrier(id: $order->id_carrier);
        $vatRate = round(num:
            ($orderSlip['total_shipping_tax_incl']
                / $orderSlip['total_shipping_tax_excl'] - 1)*100,
            precision: 2
        );
        $totalVatAmount = $orderSlip['total_shipping_tax_incl']
            - $orderSlip['total_shipping_tax_excl'];

        return new OrderLine(
            quantity: 1.0,
            quantityUnit: 'st',
            vatRate: $vatRate,
            totalAmountIncludingVat: $orderSlip['total_shipping_tax_incl'],
            description: (string) $carrier->name,
            reference: (string) $carrier->id,
            type: OrderLineType::SHIPPING,
            unitAmountIncludingVat: $orderSlip['total_shipping_tax_incl'],
            totalVatAmount: $totalVatAmount,
        );
    }
}
