<?php

/**
 * Created by PhpStorm.
 * User: jderosa
 * Date: 21/08/19
 * Time: 11.30
 */

namespace FiloBlu\Rma\Helper;

use FiloBlu\ExtInventory\Model\InventoryFbOrderItemSourceRepository;
use Magento\Framework\DataObjectFactory;
use Magento\Framework\EntityManager\EventManager;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Module\Manager;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Store\Model\ScopeInterface;
use function count;
use Exception;
use FiloBlu\Rma\Api\Data\EasyReturnInterface;
use FiloBlu\Rma\Api\RmaExtensionAttributesRepositoryInterface;
use FiloBlu\Rma\Logger\Logger;
use FiloBlu\Rma\Model\Payment\ChangeSizePay;
use function in_array;
use Magento\Catalog\Model\Product\Attribute\Repository;
use Magento\Catalog\Model\Product\Type;
use Magento\Catalog\Model\ProductRepository;
use Magento\Checkout\Model\CartFactory;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Model\AbstractExtensibleModel;
use Magento\Framework\ObjectManagerInterface;
use Magento\InventorySales\Plugin\Sales\OrderManagement\AppendReservationsAfterOrderPlacementPlugin;
use Magento\Quote\Api\Data\AddressInterfaceFactory;
use Magento\Quote\Model\Quote\Address\Rate;
use Magento\Quote\Model\Quote\Address\RateRequestFactory;
use Magento\Quote\Model\Quote\ItemFactory;
use Magento\Quote\Model\Quote\TotalsCollector;
use Magento\Quote\Model\QuoteFactory;
use Magento\Quote\Model\QuoteManagement;
use Magento\Quote\Model\ResourceModel\Quote;
use Magento\Rma\Api\Data\ItemInterface;
use Magento\Rma\Api\Data\RmaInterface;
use Magento\Rma\Api\RmaRepositoryInterface;
use Magento\Rma\Model\ResourceModel\Item;
use Magento\Rma\Model\Rma\Source\Status;
use Magento\Sales\Api\Data\OrderAddressInterfaceFactory;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderItemInterface;
use Magento\Sales\Api\Data\OrderItemInterfaceFactory;
use Magento\Sales\Api\Data\OrderPaymentInterface;
use Magento\Sales\Api\OrderItemRepositoryInterface;
use Magento\Sales\Api\OrderManagementInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Address;
use Magento\Sales\Model\Order\AddressFactory;
use Magento\Sales\Model\OrderFactory;
use Magento\Sales\Model\Service\OrderService;
use Magento\Store\Api\WebsiteRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
use DataObject;
use RuntimeException;
use Throwable;

/**
 *
 */
class ChangeSizeOrder
{
    const GIFT_WRAP_MAPPING_PRODUCTS_CONFIG = 'esb/gwmapping/gwproducts';
    const ALLOWED_RETURN_GIFTWRAP_CONFIG = 'filoblu_disablerma_section/rma_group/allow_giftwrap_return';
    const CUSTOM_ATTRIBUTE_VALUE = 'filoblu_disablerma_section/rma_group/custom_attribute_value_label';
    /**
     * @var ProductRepository
     */
    protected $productRepository;
    /**
     * @var QuoteManagement
     */
    protected $quoteManagement;
    /**
     * @var CustomerRepositoryInterface
     */
    protected $customerRepository;
    /**
     * @var Rate
     */
    protected $shippingRate;
    /**
     * @var OrderItemRepositoryInterface
     */
    protected $orderItemRepositoryInterface;
    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;
    /**
     * @var OrderRepositoryInterface
     */
    protected $orderInterface;
    /**
     * @var QuoteFactory
     */
    protected $quoteFactory;
    /**
     * @var LoggerInterface
     */
    protected $logger;
    /**
     * @var RmaRepositoryInterface
     */
    protected $rmaRepository;
    /**
     * @var RmaExtensionAttributesRepositoryInterface
     */
    protected $rmaExtensionAttributesRepository;
    /**
     * @var RateRequestFactory
     */
    protected $rateRequest;
    /**
     * @var array
     */
    protected $orderData;
    /**
     * @var int
     */
    protected $shippingAmount = 0;
    /**
     * @var int
     */
    protected $shippingAmountExclTax = 0;
    /**
     * @var int
     */
    protected $shippingTax = 0;
    /**
     * @var bool
     */
    protected $hasOrderPickupInStore = false;
    /**
     * @var int
     */
    protected $pickupInStoreAmount = 0;
    /**
     * @var string
     */
    protected $parentOrderShippingMethod;
    /**
     * @var string
     */
    protected $parentOrderShippingDescription;
    /**
     * @var array
     */
    protected $additionalInfo;
    /**
     * @var array
     */
    protected $orderedProducts = [];
    /**
     * @var EasyReturnInterface
     */
    protected $easyReturn;
    /**
     * @var Configurable
     */
    protected $configurable;
    /**
     * @var CartFactory
     */
    protected $cartFactory;
    /**
     * @var Quote\ItemFactory
     */
    protected $quoteItemFactory;
    /**
     * @var OrderFactory
     */
    protected $orderFactory;
    /**
     * @var AddressFactory
     */
    protected $addressFactory;
    /**
     * @var \Magento\Sales\Model\ResourceModel\Order
     */
    protected $orderResourceModel;
    /**
     * @var OrderItemInterface
     */
    protected $orderItem;
    /**
     * @var OrderManagementInterface
     */
    protected $orderManagementInterface;
    /**
     * @var OrderItemInterfaceFactory
     */
    protected $orderItemFactory;
    /**
     * @var OrderService
     */
    protected $orderService;
    /**
     * @var TotalsCollector
     */
    protected $totalsCollector;
    /**
     * @var Repository
     */
    protected $attributeRepository;
    /**
     * @var AddressInterfaceFactory
     */
    protected $quoteAddressFactory;
    /**
     * @var EasyReturnHelper
     */
    protected $easyReturnHelper;
    /**
     * @var int
     */
    protected $rmaEntityId;
    /**
     * @var WebsiteRepositoryInterface
     */
    protected $websiteRepository;
    /**
     * @var EventManager
     */
    protected $eventManager;
    protected $orderManagementPlugin = null;
    /**
     * @var Manager
     */
    protected $management;
    /**
     * @var OrderAddressInterfaceFactory
     */
    private $addressInterfaceFactory;
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;
    /**
     * @var Item
     */
    private $itemResourceModel;
    /**
     * @var Logger
     */
    private $filobluLogger;
    /**
     * @var ChangeSizeAttributeHelper
     */
    private $changeSizeAttributeHelper;
    /**
     * @var string
     */
    private $rateRequestType;
    /**
     * @var ObjectManagerInterface
     */
    private $objectManager;
    /**
     * @var SerializerInterface
     */
    private $serializer;

    /**
     * ChangeSizeOrder constructor.
     * @param ScopeConfigInterface $scopeConfig
     * @param ProductRepository $productRepository
     * @param QuoteManagement $quoteManagement
     * @param CustomerRepositoryInterface $customerRepository
     * @param Rate $shippingRate
     * @param OrderItemRepositoryInterface $orderItemRepositoryInterface
     * @param OrderRepositoryInterface $orderInterface
     * @param QuoteFactory $quoteFactory
     * @param LoggerInterface $logger
     * @param RmaRepositoryInterface $rmaRepository
     * @param RmaExtensionAttributesRepositoryInterface $rmaExtensionAttributesRepository
     * @param OrderAddressInterfaceFactory $addressInterfaceFactory
     * @param StoreManagerInterface $storeManager
     * @param Item $itemResourceModel
     * @param Logger $filobluLogger
     * @param ChangeSizeAttributeHelper $changeSizeAttributeHelper
     * @param EasyReturnRateFactory $easyReturnRateFactory
     * @param RateRequestFactory $rateRequest
     * @param EasyReturnInterface $easyReturn
     * @param Configurable $configurable
     * @param CartFactory $cartFactory
     * @param ItemFactory $quoteItemFactory
     * @param OrderFactory $orderFactory
     * @param AddressFactory $addressFactory
     * @param \Magento\Sales\Model\ResourceModel\Order $orderResourceModel
     * @param OrderItemInterface $orderItem
     * @param OrderManagementInterface $orderManagementInterface
     * @param OrderItemInterfaceFactory $orderItemFactory
     * @param OrderService $orderService
     * @param TotalsCollector $totalsCollector
     * @param Repository $attributeRepository
     * @param AddressInterfaceFactory $quoteAddressFactory
     * @param EasyReturnHelper $easyReturnHelper
     * @param WebsiteRepositoryInterface $websiteRepository
     * @param ObjectManagerInterface $objectManager
     * @param EventManager $eventManager
     * @param Manager $management
     * @param SerializerInterface $serializer
     * @param null $orderManagementPlugin
     */
    public function __construct(
        ScopeConfigInterface                      $scopeConfig,
        ProductRepository                         $productRepository,
        QuoteManagement                           $quoteManagement,
        CustomerRepositoryInterface               $customerRepository,
        Rate                                      $shippingRate,
        OrderItemRepositoryInterface              $orderItemRepositoryInterface,
        OrderRepositoryInterface                  $orderInterface,
        QuoteFactory                              $quoteFactory,
        LoggerInterface                           $logger,
        RmaRepositoryInterface                    $rmaRepository,
        RmaExtensionAttributesRepositoryInterface $rmaExtensionAttributesRepository,
        OrderAddressInterfaceFactory              $addressInterfaceFactory,
        StoreManagerInterface                     $storeManager,
        Item                                      $itemResourceModel,
        Logger                                    $filobluLogger,
        ChangeSizeAttributeHelper                 $changeSizeAttributeHelper,
        RateRequestFactory                        $rateRequest,
        Configurable                              $configurable,
        CartFactory                               $cartFactory,
        ItemFactory                               $quoteItemFactory,
        OrderFactory                              $orderFactory,
        AddressFactory                            $addressFactory,
        \Magento\Sales\Model\ResourceModel\Order  $orderResourceModel,
        OrderItemInterface                        $orderItem,
        OrderManagementInterface                  $orderManagementInterface,
        OrderItemInterfaceFactory                 $orderItemFactory,
        OrderService                              $orderService,
        TotalsCollector                           $totalsCollector,
        Repository                                $attributeRepository,
        AddressInterfaceFactory                   $quoteAddressFactory,
        EasyReturnHelper                          $easyReturnHelper,
        WebsiteRepositoryInterface                $websiteRepository,
        ObjectManagerInterface                    $objectManager,
        EventManager                              $eventManager,
        Manager                                   $management,
        SerializerInterface                       $serializer,
                                                  $orderManagementPlugin = null
    )
    {
        $this->productRepository = $productRepository;
        $this->quoteManagement = $quoteManagement;
        $this->customerRepository = $customerRepository;
        $this->shippingRate = $shippingRate;
        $this->orderItemRepositoryInterface = $orderItemRepositoryInterface;
        $this->orderInterface = $orderInterface;
        $this->scopeConfig = $scopeConfig;
        $this->quoteFactory = $quoteFactory;
        $this->logger = $logger;
        $this->rmaRepository = $rmaRepository;
        $this->rmaExtensionAttributesRepository = $rmaExtensionAttributesRepository;
        $this->addressInterfaceFactory = $addressInterfaceFactory;
        $this->storeManager = $storeManager;
        $this->itemResourceModel = $itemResourceModel;
        $this->filobluLogger = $filobluLogger;
        $this->changeSizeAttributeHelper = $changeSizeAttributeHelper;
        $this->rateRequest = $rateRequest;
        $this->configurable = $configurable;
        $this->cartFactory = $cartFactory;
        $this->quoteItemFactory = $quoteItemFactory;
        $this->orderFactory = $orderFactory;
        $this->addressFactory = $addressFactory;
        $this->orderResourceModel = $orderResourceModel;
        $this->orderItem = $orderItem;
        $this->orderManagementInterface = $orderManagementInterface;
        $this->orderItemFactory = $orderItemFactory;
        $this->orderService = $orderService;
        $this->totalsCollector = $totalsCollector;
        $this->attributeRepository = $attributeRepository;
        $this->quoteAddressFactory = $quoteAddressFactory;
        $this->easyReturnHelper = $easyReturnHelper;
        $this->websiteRepository = $websiteRepository;
        $this->objectManager = $objectManager;
        $this->eventManager = $eventManager;
        $this->management = $management;

        $this->serializer = $serializer;
        if (class_exists(\FiloBlu\ExtInventory\Plugin\Magento\Sales\Api\OrderManagementInterface::class)) {
            $this->orderManagementPlugin = ObjectManager::getInstance()->get(\FiloBlu\ExtInventory\Plugin\Magento\Sales\Api\OrderManagementInterface::class);
        }
    }

    /**
     * @param array $orderData
     * @return bool|AbstractExtensibleModel|OrderInterface|object|null
     * @throws Exception
     */
    public function createMageOrder(array $orderData)
    {
        $this->orderData = $orderData;

        /**
         * @var Order $order
         */
        $order = $orderData['order'];
        $request = $orderData['request'];

        if ((!isset($request['filoblu_easy_return']) || $request['filoblu_easy_return'] != 'on') && (!isset($request['easy_return']) || $request['easy_return'] == false)) {
            if ($order->getShippingAddress()) {
                $request = $order->getShippingAddress()->toArray();
            } elseif ($order->getAddresses()) {
                $addresses = $order->getAddresses();
                if (count($addresses) === 1) {
                    $request = $addresses;
                } else {
                    $request = $addresses[0];
                    foreach ($addresses as $address) {
                        if ($address['address_type'] === 'shipping') {
                            $request = $address;
                            break;
                        }
                    }
                }
            }
        }

        try {
            // fix for api v1/returns POST that doesn't set Store
            if (!$this->storeManager->getStore()->getId() != $order->getStore()->getId()) {
                $this->storeManager->setCurrentStore($order->getStore());
            }

            // get original order store
            $orderStore = $order->getStore()->getWebsiteId();


            return $this->cloneOrder($order, $orderStore, $request, $orderData['calculate_shipping_cost'] ?? false);
        } catch (Exception $exception) {
            $this->filobluLogger->debug($exception->getMessage(), ['exception' => $exception]);
            throw new Exception($exception->getMessage());
            //return false;
        } catch (Throwable $throwable) {
            $this->filobluLogger->debug($throwable->getMessage(), ['exception' => $throwable]);
            throw new Exception($throwable->getMessage());
            //return false;
        }
    }

    /**
     * @param Order $originalOrder
     * @param $websiteId
     * @param $request
     * @param bool $calculateShippingCosts
     * @return false|Order
     * @throws LocalizedException
     * @throws NoSuchEntityException
     */
    public function cloneOrder(Order $originalOrder, $websiteId, $request, $calculateShippingCosts)
    {
        $isCustomer = true;

        try {
            $this->customerRepository->get($originalOrder->getCustomerEmail(), $websiteId);
        } catch (Throwable $t) {
            $isCustomer = false;
        }

        /** @var Order $newOrder */
        $newOrder = $this->orderFactory->create();
        $newOrder->setData($originalOrder->getData());
        $newOrder->unsetData('entity_id');
        $newOrder->unsetData('increment_id');
        $newOrder->unsetData('quote_id');
        $newOrder->unsetData('created_at');
        $newOrder->unsetData('updated_at');
        $newOrder->setState(Order::STATE_NEW);
        $newOrder->setStatus(ChangeSizeAttributeHelper::ORDER_STATUS_CHANGESIZE_CODE);

        //Remove Gift Wrap
        if (!$this->isAllowedReturnGiftWrap() && $newOrder->getData('gw_id')) {
            $newOrder->unsetData('gw_id');
            $newOrder->unsetData('gw_base_price');
            $newOrder->unsetData('gw_price');
            $newOrder->unsetData('gw_base_tax_amount');
            $newOrder->unsetData('gw_tax_amount');
            $newOrder->unsetData('gw_items_base_price');
            $newOrder->unsetData('gw_items_price');
            $newOrder->unsetData('gw_base_price_incl_tax');
            $newOrder->unsetData('gw_price_incl_tax');
            $newOrder->unsetData('gw_base_price_invoiced');
            $newOrder->unsetData('gw_price_invoiced');
            $newOrder->unsetData('gw_items_base_price_invoiced');
            $newOrder->unsetData('gw_items_price_invoiced');
            $newOrder->unsetData('gw_base_tax_amount_invoiced');
            $newOrder->unsetData('gw_tax_amount_invoiced');
            $newOrder->unsetData('gw_items_base_tax_invoiced');
            $newOrder->unsetData('gw_items_tax_invoiced');
            $newOrder->unsetData('gift_message_id');
        }

        // Reset the addresses
        $newOrder->unsetData('addresses');
        $newOrder->unsetData('email_sent');
        $newOrder->unsetData('send_email');

        //Reset Gift Cards
        $newOrder->unsetData('gift_cards');
        $newOrder->unsetData('base_gift_cards_amount');
        $newOrder->unsetData('gift_cards_amount');
        $newOrder->unsetData('base_gift_cards_invoiced');
        $newOrder->unsetData('gift_cards_invoiced');
        $newOrder->unsetData('base_gift_cards_refunded');
        $newOrder->unsetData('gift_cards_refunded');

        // Reset the totals
        $newOrder->setGrandTotal(0);
        $newOrder->setBaseGrandTotal(0);
        $newOrder->setSubtotal(0);
        $newOrder->setBaseSubtotal(0);
        $newOrder->setTaxAmount(0);
        $newOrder->setBaseTaxAmount(0);
        $newOrder->setDiscountAmount(0);
        $newOrder->setBaseDiscountAmount(0);
        $newOrder->setDiscountAmount(0);
        $newOrder->setBaseDiscountInvoiced(0);
        $newOrder->setDiscountInvoiced(0);
        $newOrder->setDiscountTaxCompensationAmount(0);
        $newOrder->setBaseDiscountTaxCompensationAmount(0);

        $newOrder->setData('customer_name', $originalOrder->getCustomerName());
        $newOrder->setData('is_not_virtual', true);

        // Clone customer data
        if ($isCustomer) {
            $newOrder->setCustomerFirstname($originalOrder->getCustomerFirstname());
            $newOrder->setCustomerMiddlename($originalOrder->getCustomerMiddlename());
            $newOrder->setCustomerLastname($originalOrder->getCustomerLastname());
            $newOrder->setCustomerEmail($originalOrder->getCustomerEmail());
            $newOrder->setCustomerId($originalOrder->getCustomerId());
        } else {
            $newOrder->setCustomerIsGuest(true);
            $newOrder->setCustomerFirstname($request['firstname']);
            $newOrder->setCustomerLastname($request['lastname']);
            $newOrder->setCustomerEmail($originalOrder->getCustomerEmail());
        }

        // Clone shipping and payment methods
        $newOrder->setShippingMethod($originalOrder->getShippingMethod());

        $newOrder->unsetData('pickup_in_store_hash');
        $newOrder->unsetData('pickup_in_store_email_send_date');
        $newOrder->unsetData('pickup_in_store_confirm_received_date');
        $newOrder->unsetData('pickup_in_store_confirm_date');

        // Set currency code
        $newOrder->setOrderCurrencyCode($originalOrder->getOrderCurrencyCode());
        $newOrder->setBaseCurrencyCode($originalOrder->getBaseCurrencyCode());

        // Remove existing items from the order
        $newOrder->setItems([]);


        $requestAddress = $originalOrder->getShippingAddress()->toArray();

        if ($this->isPickupInStoreOrder($newOrder)) {
            $requestAddress = $originalOrder->getShippingAddress()->toArray();
        }

        if (!$this->isPickupInStoreOrder($newOrder) && ($requestAddress['street'] != $request['street'][0])) {
            $requestAddress = $request['pickup_address'] ?? $request;
        }

        unset($request['pickup_address']);

        //Generate new adresses
        /** @var Address $shippingAddress */
        $newShippingAddress = $this->addressFactory->create();
        $this->cloneAddress($this->buildAddressData($requestAddress, $newOrder), $newShippingAddress);

        /** @var Address $billingAddress */
        $newBillingAddress = $this->addressFactory->create();
        $this->cloneAddress($this->buildAddressData($originalOrder->getBillingAddress()->toArray(), $newOrder), $newBillingAddress, 'billing');

        //Set order new addresses
        $addresses = [$newShippingAddress->getEntityId() => $newShippingAddress, $newBillingAddress->getEntityId() => $newBillingAddress];

        $newOrder->setAddresses($addresses);

        $newShippingAddress->unsAddressId();
        $newOrder->setShippingAddress($newShippingAddress);

        $newBillingAddress->unsAddressId();
        $newOrder->setBillingAddress($newBillingAddress);

        $this->setOrderPayment($newOrder, $originalOrder);

        $this->calculateTotals($newOrder, $calculateShippingCosts, $originalOrder);

        $newOrder->setSendEmail(false);

        try {
            $this->setProducts($newOrder, $originalOrder);
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
            $newOrder->delete();
            return false;
        }

        $this->additionalInfo[RefundParentOrderHelper::CHANGE_SIZE_PAY_VERSION_INDEX] = RefundParentOrderHelper::CHANGE_SIZE_PAY_LATEST_VERSION;
        $newOrder->getPayment()->setAdditionalInformation($this->additionalInfo);

        $newOrder->setSendEmail(true);
        $newOrder->setCanSendNewEmailFlag(true);

        $newOrder->save();

        $this->eventManager->dispatch('filoblu_rma_after_change_size_create_order', ['new_order' => $newOrder, 'parent_order' => $originalOrder]);

        $newOrder->save();

        $this->reserveProducts($newOrder);

        $this->setOrderItemInventorySource($newOrder);

        return $newOrder;
    }

    /**
     * @return mixed
     */
    public function isAllowedReturnGiftWrap()
    {
        return $this->scopeConfig->getValue(self::ALLOWED_RETURN_GIFTWRAP_CONFIG);
    }

    public function isPickupInStoreOrder(OrderInterface $order)
    {
        return $order->getShippingMethod() == 'pickupinstore_pickupinstore';
    }

    /**
     * @param Address $oldAddress
     * @param Address $newAddress
     * @param string $type
     * @return void
     * @throws Exception
     */
    public function cloneAddress(Address $oldAddress, Address $newAddress, $type = 'shipping')
    {
        $toSkip = ['entity_id', 'id', 'parent_id', 'quote_address_id'];

        foreach ($oldAddress->getData() as $key => $value) {
            if (in_array($key, $toSkip)) {
                continue;
            }

            $newAddress->setData($key, $value);
        }

        $newAddress->setAddressType($type);

        $newAddress->save();

    }

    public function buildAddressData(array $address, OrderInterface $order)
    {
        $newAddress = $this->addressInterfaceFactory->create();
        foreach ($address as $k => $val) {
            $newAddress->setData($k, $val);
        }
        $newAddress->setEmail($order->getCustomerEmail());

        $newAddress->unsetData('entity_id');
        $newAddress->unsetData('id');
        $newAddress->unsetData('parent_id');
        $newAddress->unsetData('quote_address_id');

        return $newAddress;
    }

    /**
     * @param Order $newOrder
     * @param Order $oldOrder
     * @return void
     * @throws Exception
     */
    protected function setOrderPayment(Order $newOrder, Order $oldOrder)
    {
        $orderData = $this->getOrderDataByKey();
        $items = $this->getOrderDataByKey('items');
        $parentSkuReplaced = $this->getOrderDataByKey('parent_product_sku');

        $simpleItemIds = [];

        if ($orderData) {
            foreach ($oldOrder->getItems() as $orderItem) {
                if (!$orderItem->getParentItemId()) {
                    continue;
                }

                $key = $this->searchOrderItemKeyByValue($orderItem->getParentItemId());

                if ($key !== false) {
                    $simpleItemIds[] = $orderItem->getItemId();

                    if (!$this->rmaEntityId) {
                        $this->rmaEntityId = $items[$key]['rma_entity_id'];
                    }
                }
            }
        }

        $qtyOrdered = 0;
        $final_price = 0;

        foreach ($items as $item) {
            $qtyOrdered += $item['qty'];
            /** @var OrderItemInterface $order_item */
            $originalOrderItem = clone $this->orderItemRepositoryInterface->get($item['original_order_item_id']);
            $final_price += $originalOrderItem->getPriceInclTax();
        }

        $newOrder->setTotalQtyOrdered($qtyOrdered);
        $newOrder->setBaseTotalQtyOrdered($qtyOrdered);

        /** @var OrderPaymentInterface $payment */
        $payment = ObjectManager::getInstance()->create(OrderPaymentInterface::class);
        $payment->setMethod(ChangeSizePay::PAYMENT_CODE);

        $this->additionalInfo = ['parent_order_id' => $oldOrder->getId(), 'parent_order_increment_id' => $oldOrder->getIncrementId(), 'rma_id' => $orderData['rma_id'], 'rma_entity_id' => $this->rmaEntityId, 'parent_order_item_id' => implode(',', $simpleItemIds), 'parent_product_sku' => $parentSkuReplaced, 'grand_total' => $final_price, 'base_grand_total' => $final_price,];

        $payment->setAdditionalInformation($this->additionalInfo);

        $newOrder->setPayment($payment);
    }

    /**
     * @throws Exception
     */
    public function getOrderDataByKey($key = '')
    {
        if (!isset($this->orderData)) {
            throw new Exception('Order Data is empty!');
        }

        if (!$key) {
            return $this->orderData;
        }

        if (!isset($this->orderData[$key])) {
            throw new Exception(sprintf('Order Data key "%s" des not exists', $key));
        }

        return $this->orderData[$key];
    }

    /**
     * @param $value
     * @param $key
     * @return false|int|string
     * @throws Exception
     */
    public function searchOrderItemKeyByValue($value, $key = 'original_order_item_id')
    {
        $items = $this->getOrderDataByKey('items');

        return array_search($value, array_column($items, $key));
    }

    public function calculateTotals(Order $order , $calculateShippingCosts , Order $originalOrder)
    {
        $items = $this->getOrderDataByKey('items');

        $discountAmount = 0;
        $baseDiscountAmount = 0;
        $discountTaxCompensationAmount = 0;
        $baseDiscountTaxCompensationAmount = 0;
        $taxAmount = 0;
        $baseTaxAmount = 0;
        $discountPercent = 0;
        $baseSubTotal = 0;
        $subtotal = 0;
        $originalPrice = 0;
        $baseOriginalPrice = 0;
        $priceInclTax = 0;
        $basePriceInclTax = 0;

        foreach ($items as $item) {
            $originalOrderItem = clone $this->orderItemRepositoryInterface->get($item['original_order_item_id']);

            if ($originalOrderItem->getParentItemId()) {
                $originalOrderItem = $originalOrderItem->getParentItem();
            }

            $itemQty = (float)$item['qty'];
            if ((float)$originalOrderItem->getDiscountAmount())
            {
                if ($originalOrderItem->getQtyOrdered() > 1 && $originalOrderItem->getQtyOrdered() != $itemQty)
                {
                    $discountAmount += $itemQty * ($originalOrderItem->getDiscountAmount() / $originalOrderItem->getQtyOrdered());
                    $baseDiscountAmount += $itemQty * ($originalOrderItem->getBaseDiscountAmount() / $originalOrderItem->getQtyOrdered());
                    $discountTaxCompensationAmount += $itemQty * ($originalOrderItem->getDiscountTaxCompensationAmount() / $originalOrderItem->getQtyOrdered());
                    $baseDiscountTaxCompensationAmount += $itemQty * ($originalOrderItem->getBaseDiscountTaxCompensationAmount() / $originalOrderItem->getQtyOrdered());
                } else {
                    $discountAmount += $originalOrderItem->getDiscountAmount();
                    $baseDiscountAmount += $originalOrderItem->getBaseDiscountAmount();
                    $discountTaxCompensationAmount += $originalOrderItem->getDiscountTaxCompensationAmount();
                    $baseDiscountTaxCompensationAmount += $originalOrderItem->getBaseDiscountTaxCompensationAmount();
                }
            }

            $taxAmount += $itemQty * ($originalOrderItem->getTaxAmount() / $originalOrderItem->getQtyOrdered());
            $baseTaxAmount += $itemQty * ($originalOrderItem->getBaseTaxAmount() / $originalOrderItem->getQtyOrdered());

            $subtotal += $originalOrderItem->getPrice() * $itemQty;
            $baseSubTotal += $originalOrderItem->getBasePrice() * $itemQty;

            $priceInclTax += $originalOrderItem->getPriceInclTax() * $itemQty;
            $basePriceInclTax += $originalOrderItem->getBasePriceInclTax() * $itemQty;

            if (!$discountPercent && (int)$originalOrderItem->getDiscountPercent()) {
                $discountPercent = $originalOrderItem->getDiscountPercent();
            }
        }

        $grandTotal = $priceInclTax;
        $baseGrandTotal = $basePriceInclTax;

        if ($discountAmount) {
            $grandTotal = $priceInclTax - $discountAmount;
            $baseGrandTotal = $basePriceInclTax - $baseDiscountAmount;
            $order->setDiscountTaxCompensationAmount($discountTaxCompensationAmount);
            $order->setBaseDiscountTaxCompensationAmount($baseDiscountTaxCompensationAmount);
            $order->setDiscountAmount($discountAmount * -1);
            $order->setBaseDiscountAmount($baseDiscountAmount * -1);
        }

        $shippingAmount = 0;
        $baseShippingAmount = 0;
        $shippingTaxAmount = 0;
        $baseShippingTaxAmount = 0;
        $shippingInclTax = 0;
        $baseShippingInclTax = 0;

        $this->parentOrderShippingDescription = 'Metodo di spedizione - Standard';
        $this->parentOrderShippingMethod = 'freeshipping_freeshipping';

        if ((int)$originalOrder->getShippingAmount() && $calculateShippingCosts) {
            $shippingAmount = $originalOrder->getShippingAmount();
            $shippingInclTax = $originalOrder->getShippingInclTax();
            $baseShippingAmount = $originalOrder->getBaseShippingAmount();
            $shippingTaxAmount = $originalOrder->getShippingTaxAmount();
            $baseShippingTaxAmount = $originalOrder->getBaseShippingTaxAmount();
            $baseShippingInclTax = $originalOrder->getBaseShippingInclTax();

            //shippingAmount and baseShippingAmount must be always without tax
            if ($shippingAmount == $baseShippingInclTax) {
                $shippingAmount -= $shippingTaxAmount;
            }

            if ($baseShippingAmount == $baseShippingInclTax) {
                $baseShippingAmount -= $baseShippingTaxAmount;
            }

            $this->parentOrderShippingMethod = $originalOrder->getShippingMethod();

            $grandTotal += $shippingInclTax;
            $baseGrandTotal += $baseShippingInclTax;
            $taxAmount += $shippingTaxAmount;
            $baseTaxAmount += $baseShippingTaxAmount;
        }

        $order->setShippingAmount($shippingAmount);
        $order->setBaseShippingAmount($baseShippingAmount);
        $order->setShippingTaxAmount($shippingTaxAmount);
        $order->setBaseShippingTaxAmount($baseShippingTaxAmount);
        $order->setShippingInclTax($shippingInclTax);
        $order->setBaseShippingInclTax($baseShippingInclTax);

        $order->setShippingDescription($this->parentOrderShippingDescription);

        $order->setGrandTotal($grandTotal);
        $order->setBaseGrandTotal($baseGrandTotal);

        $order->setSubtotal($subtotal);
        $order->setBaseSubtotal($baseSubTotal);

        $order->setTaxAmount($taxAmount);
        $order->setBaseTaxAmount($baseTaxAmount);

        $order->setSubtotalInclTax($priceInclTax);
        $order->setBaseSubtotalInclTax($basePriceInclTax);

        $order->setBaseTotalPaid(0);
        $order->setTotalPaid(0);

        foreach ($order->getData() as $key => $value) {
            if (strpos($key , 'invoiced') !== false) {
                $order->setData($key , 0);
            }

            if (strpos($key , 'refunded') !== false) {
                $order->setData($key , 0);
            }
        }
    }

    /**
     * @param OrderInterface $order
     * @param OrderItemInterface[] $originalOrderItems
     * @return mixed|null
     */
    public function getRate(OrderInterface $order , array $originalOrderItems)
    {
        if (!$order->getShippingAddress()) {
            return null;
        }

        $taxPercent = 0.0;

        foreach ($originalOrderItems as $item) {
            if ((float)$item->getTaxPercent() > $taxPercent) {
                $taxPercent = (float)$item->getTaxPercent();
            }
        }

        $rateRequest = $this->rateRequest->create()->addData($order->getShippingAddress()->toArray());
        $rateRequest->setWebsiteId($order->getStore()->getWebsiteId());
        $rateRequest->setDestCountryId($order->getShippingAddress()->getCountryId());
        $rateRequest->setData('order_total' , $order->getGrandTotal());
        $rateRequest->setData('tax_percentage' , $taxPercent);

        $originalShippingRate = $this->matrixrate->create()->collectRates($rateRequest);

        $this->rateRequestType = $rateRequest->getConditionMRName();

        $rates = $originalShippingRate->getAllRates();

        if (empty($rates)) {
            throw new RuntimeException('Could not find a rate for order');
        }

        return array_shift($rates);
    }

    /**
     * @param Order $newOrder
     * @param Order $oldOrder
     * @return void
     * @throws NoSuchEntityException
     * @throws Exception
     */
    protected function setProducts(Order $newOrder, Order $oldOrder)
    {
        $items = $this->getOrderDataByKey('items');

        foreach ($items as $item) {
            $originalOrderItem = clone $this->orderItemRepositoryInterface->get($item['original_order_item_id']);
            $newProduct = $this->productRepository->getById($item['product_id']);

            $parentProduct = null;
            $attributes = [];

            $parentItem = null;

            if ($newProduct->getTypeId() === Type::TYPE_SIMPLE) {
                $parentProductIds = $this->configurable->getParentIdsByChild($newProduct->getId());
                $parentProduct = $this->productRepository->getById(end($parentProductIds));

                foreach ($parentProduct->getTypeInstance()->getConfigurableOptions($parentProduct) as $option) {
                    foreach ($option as $optionItem) {
                        if ($optionItem['sku'] === $newProduct->getSku()) {
                            $attribute = $this->attributeRepository->get($optionItem['attribute_code']);
                            $attribute->setStoreId($oldOrder->getStoreId());
                            $attributeLabel = $this->getCustomAttributeCode($optionItem['attribute_code']);
                            $optionValue = $this->getOptionValue($newProduct, $optionItem['attribute_code'], $attributeLabel);
                            $attributes = ['info_buyRequest' => ['super_attribute' => [$attribute->getAttributeId() => $optionItem['value_index']], 'qty' => $item['qty']], 'attributes_info' => [['label' => $attribute->getStoreLabel(),
                                'value' => $optionValue,
                                'option_id' => $attribute->getAttributeId(),
                                'option_value' => $optionItem['value_index']]]];

                            break;
                        }
                    }
                }

                $attributes = array_merge($attributes, ['simple_name' => $newProduct->getName(), 'simple_sku' => $newProduct->getSku(), 'product_calculations' => 1, 'giftcard_lifetime' => null, 'giftcard_is_redeemable' => 0, 'giftcard_email_template' => null, 'giftcard_type' => null]);

                if (!isset($this->additionalInfo['new_product_sku'])) {
                    $this->additionalInfo['new_product_sku'] = $newProduct->getSku();
                }

                $parentItem = $this->generateOrderItem($parentProduct, $item['qty'], $originalOrderItem, $attributes);

                $parentItem->setData('price', $originalOrderItem->getData('price'));
                $parentItem->setData('base_price', $originalOrderItem->getData('base_price'));
                $parentItem->setData('discount_percent', $originalOrderItem->getData('discount_percent'));
                $parentItem->setData('original_price', $originalOrderItem->getData('original_price'));
                $parentItem->setData('base_original_price', $originalOrderItem->getData('base_original_price'));
                // TODO: check if use $originalOrderItem->getData('qty_invoiced'); or $originalOrderItem->getData('qty_ordered');
                $originalOrderItemQty = (float)$originalOrderItem->getData('qty_ordered');
                $itemQty = (float)$item['qty'];
                if ($originalOrderItemQty > 1 && $originalOrderItemQty != $itemQty) {
                    $parentItem->setData('tax_amount', $itemQty * ($originalOrderItem->getData('tax_amount') / $originalOrderItemQty));
                    $parentItem->setData('base_tax_amount', $itemQty * ($originalOrderItem->getData('base_tax_amount') / $originalOrderItemQty));
                    $parentItem->setData('discount_tax_compensation_amount', $itemQty * ($originalOrderItem->getData('discount_tax_compensation_amount') / $originalOrderItemQty));
                    $parentItem->setData('base_discount_tax_compensation_amount', $itemQty * ($originalOrderItem->getData('base_discount_tax_compensation_amount') / $originalOrderItemQty));
                    $parentItem->setData('discount_amount', $itemQty * ($originalOrderItem->getData('discount_amount') / $originalOrderItemQty));
                    $parentItem->setData('base_discount_amount', $itemQty * ($originalOrderItem->getData('base_discount_amount') / $originalOrderItemQty));
                    $parentItem->setData('row_total', $itemQty * ($originalOrderItem->getData('row_total') / $originalOrderItemQty));
                    $parentItem->setData('base_row_total', $itemQty * ($originalOrderItem->getData('base_row_total') / $originalOrderItemQty));
                } else {
                    $parentItem->setData('tax_amount', $originalOrderItem->getData('tax_amount'));
                    $parentItem->setData('base_tax_amount', $originalOrderItem->getData('base_tax_amount'));
                    $parentItem->setData('discount_tax_compensation_amount', $originalOrderItem->getData('discount_tax_compensation_amount'));
                    $parentItem->setData('base_discount_tax_compensation_amount', $originalOrderItem->getData('base_discount_tax_compensation_amount'));
                    $parentItem->setData('discount_amount', $originalOrderItem->getData('discount_amount'));
                    $parentItem->setData('base_discount_amount', $originalOrderItem->getData('base_discount_amount'));
                    $parentItem->setData('row_total', $originalOrderItem->getData('row_total'));
                    $parentItem->setData('base_row_total', $originalOrderItem->getData('base_row_total'));
                }

                $parentItem->setProductType('configurable');
                $parentItem->setOrderId($newOrder->getEntityId());//->save();
                $parentItem->setIsVirtual(false);
                $parentItem->setSku($newProduct->getSku());
                $newOrder->addItem($parentItem);
            }

            unset($attributes['attributes_info'], $attributes['simple_name'], $attributes['simple_sku'], $attributes['product_calculations']);

            $childItem = $this->generateOrderItem($newProduct, $item['qty'], $originalOrderItem, $attributes, $parentItem);
            $childItem->setOrderId($newOrder->getEntityId());
            $childItem->setIsVirtual(false);
            $newOrder->addItem($childItem);
        }
    }

    /**
     * @return mixed
     */
    public function getCustomAttributeCode($attributeCode)
    {

        if (!$this->scopeConfig->getValue(self::CUSTOM_ATTRIBUTE_VALUE)) {
            return $attributeCode;
        }

        $attributes = $this->serializer->unserialize($this->scopeConfig->getValue(self::CUSTOM_ATTRIBUTE_VALUE));

        if (empty($attributes)) {
            return $attributeCode;
        }

        foreach ($attributes as $attribute) {
            if ($attribute['attribute_to_override'] == $attributeCode) {
                return $attribute['attribute_to_show'];
            }
        }
    }

    public function getOptionValue($newProduct, $attributeCode, $attributeLabel)
    {
        $optionValue = $newProduct->getAttributeText($attributeCode);
        if ($attributeLabel != $attributeCode) {
            $attributeType = $this->attributeRepository->get($attributeLabel);
            if (empty($newProduct->getData($attributeLabel))) {
                return $optionValue;
            }
            $optionValue = $newProduct->getData($attributeLabel);
            if ($attributeType->getBackendType() == 'int') {
                $optionValue = $newProduct->getAttributeText($attributeLabel);
            }
        }
        return $optionValue;
    }

    /**
     * @param $product
     * @param $qty
     * @param OrderItemInterface $originalOrderItem
     * @param $parentItem
     * @param $options
     * @return OrderItemInterface
     */
    protected function generateOrderItem($product, $qty, OrderItemInterface $originalOrderItem, $options, $parentItem = null)
    {
        $newOrderItem = $this->orderItemFactory->create();
        $newOrderItem->setProductId($product->getId());
        $newOrderItem->setProductType($product->getTypeId());
        $newOrderItem->setSku($product->getSku());
        $newOrderItem->setName($product->getName());
        $newOrderItem->setStoreId($originalOrderItem->getStoreId());
        $newOrderItem->setWeight($originalOrderItem->getWeight());
        $newOrderItem->setQtyOrdered($qty);
        $newOrderItem->setTaxPercent($originalOrderItem->getTaxPercent());
        $newOrderItem->setTaxAmount($originalOrderItem->getTaxAmount());
        $newOrderItem->setBaseTaxAmount($originalOrderItem->getBaseTaxAmount());
        $newOrderItem->setPriceInclTax($originalOrderItem->getPriceInclTax());
        $newOrderItem->setBasePriceInclTax($originalOrderItem->getBasePriceInclTax());
        $newOrderItem->setRowTotalInclTax($originalOrderItem->getPriceInclTax() * $qty);
        $newOrderItem->setBaseRowTotalInclTax($originalOrderItem->getPriceInclTax() * $qty);
        $newOrderItem->setRowTotal($originalOrderItem->getPrice() * $qty);
        $newOrderItem->setBaseRowTotal($originalOrderItem->getPrice() * $qty);
        $newOrderItem->setPrice($originalOrderItem->getPrice());
        $newOrderItem->setBasePrice($originalOrderItem->getBasePrice());
        $newOrderItem->setOriginalPrice($originalOrderItem->getPriceInclTax());
        $newOrderItem->setProductOptions($options);
        $newOrderItem->setKpiFullPrice($originalOrderItem->getPriceInclTax());

        if ($parentItem) {
            $newOrderItem->setParentItem($parentItem);
            $newOrderItem->setParentItemId($parentItem->getId());
        }

        return $newOrderItem;
    }

    /**
     * @param Order $order
     * @return void
     */
    public function reserveProducts(Order $order)
    {
        if (!class_exists(AppendReservationsAfterOrderPlacementPlugin::class) || !interface_exists(OrderManagementInterface::class)) {
            return;
        }

        try {
            $this->objectManager->get(AppendReservationsAfterOrderPlacementPlugin::class)->aroundPlace($this->objectManager->get(OrderManagementInterface::class), function (OrderInterface $order) {
                return $order;
            }, $order);
        } catch (Exception $exception) {
            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage(), ['throwable' => $throwable]);
        }
    }

    /**
     * @param OrderInterface $order
     * @return false|void
     */
    public function setOrderItemInventorySource(OrderInterface $order)
    {
        if (!$this->canUseMultistockFeature()) {
            return false;
        }

        $this->orderManagementPlugin->executeAfterPlace($order);
    }

    /**
     * @return bool
     */
    protected function canUseMultistockFeature()
    {
        if (!$this->management->isEnabled('FiloBlu_ExtInventory')) {
            return false;
        }

        if (!class_exists(InventoryFbOrderItemSourceRepository::class)) {
            return false;
        }

        return true;
    }

    /**
     * @param int $orderId
     * @return bool
     */
    public function isOrderTotallyReturned(int $orderId)
    {
        $itemResourceModel = $this->itemResourceModel;
        $returnableItems = $itemResourceModel->getReturnableItems($orderId);
        $fullRma = true;
        foreach ($returnableItems as $item) {
            if ($item > 0) {
                $fullRma = false;
                break;
            }
        }

        return $fullRma;
    }

    /**
     * @param Order|OrderInterface $order
     * @return bool
     */
    public function isValidForUpdateStatus($order)
    {
        if (!$order) {
            return false;
        }
        try {
            try {
                $paymentInfo = $order->getExtensionAttributes()->getPaymentAdditionalInfo() ?? [];
                $rmaId = 0;
                foreach ($paymentInfo as $info) {
                    if ($info->getKey() && $info->getKey() === 'rma_id' && $info->getValue()) {
                        $rmaId = $info->getValue();
                        break;
                    }
                }
            } catch (Throwable $t) {
                $paymentInfo = $order->getPayment()->getAdditionalInformation() ?? [];
                $rmaId = 0;
                foreach ($paymentInfo as $k => $info) {
                    if ($k === 'rma_id' && $info) {
                        $rmaId = $info;
                        break;
                    }
                }
            }

            $rma = $this->rmaRepository->get($rmaId);
            if (!$rma) {
                return false;
            }
            $rmaStatus = $rma->getStatus();
            $allowedStatus = [Status::STATE_PROCESSED_CLOSED, Status::STATE_DENIED, Status::STATE_CLOSED, Status::STATE_APPROVED_ON_ITEM, Status::STATE_APPROVED];

            if (in_array($rmaStatus, $allowedStatus, true)) {
                return true;
            }

            return false;
        } catch (Exception $e) {
            $this->filobluLogger->debug($e->getMessage());
            return false;
        } catch (Throwable $t) {
            $this->filobluLogger->debug($t->getMessage());
            return false;
        }
    }

    /**
     * @param $order
     * @return bool
     */
    public function isChangeSizeOrder($order)
    {
        return $this->changeSizeAttributeHelper->isChangeSizeOrder($order);
    }

    /**
     * @param RmaInterface $rma
     * @return array
     */
    public function getOrderItemsByRma(RmaInterface $rma): array
    {
        $rmaItems = $this->getChangeSizeItems($rma);

        if (empty($rmaItems)) {
            return [];
        }

        $orderItems = [];

        /** @var ItemInterface $rmaItem */
        foreach ($rmaItems as $rmaItem) {
            $orderItems[] = $this->orderItemRepositoryInterface->get($rmaItem->getOrderItemId());
        }

        return $orderItems;
    }

    /**
     * @param RmaInterface $rma
     * @return array
     */
    public function getChangeSizeItems(RmaInterface $rma)
    {
        $changeSizeItems = [];

        foreach ($rma->getItems() as $item) {
            if (!$item->getExtensionAttributes()) {
                continue;
            }

            if (!$item->getExtensionAttributes()->getChangeSize()) {
                continue;
            }

            $changeSizeItems[] = $item;
        }

        return $changeSizeItems;
    }

    public function getGiftWrapProductsSkus()
    {
        $giftWrapMapping = $this->giftWrapMapping();
        if ($giftWrapMapping) {
            $productSkus = [];
            foreach (json_decode($giftWrapMapping, true) ?? [] as $index => $map) {
                $mapSku = trim($map['product_sku'] ?? '');
                if (!in_array($mapSku, $productSkus)) {
                    $productSkus[] = $mapSku;
                }
            }
            return $productSkus;
        }
        return [];
    }

    /**
     * @return mixed
     */
    public function giftWrapMapping()
    {
        return $this->scopeConfig->getValue(self::GIFT_WRAP_MAPPING_PRODUCTS_CONFIG);
    }
}
