<?php
/**
 * Mageplaza
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Mageplaza.com license that is
 * available through the world-wide-web at this URL:
 * https://www.mageplaza.com/LICENSE.txt
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade this extension to newer
 * version in the future.
 *
 * @category    Mageplaza
 * @package     Mageplaza_EditOrder
 * @copyright   Copyright (c) Mageplaza (https://www.mageplaza.com/)
 * @license     https://www.mageplaza.com/LICENSE.txt
 */

namespace Mageplaza\EditOrder\Model\Order;

use Exception;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Sales\Api\OrderManagementInterface as OrderManagement;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Item;
use Magento\Sales\Model\Order\ItemFactory;
use Mageplaza\EditOrder\Helper\Data as HelperData;
use Mageplaza\EditOrder\Model\ItemComparison;
use Mageplaza\EditOrder\Model\OrderManager;

/**
 * Class Total
 * @package Mageplaza\EditOrder\Model\Order
 */
class Total
{
    const TYPE_COLLECT_ITEMS    = 'items';
    const TYPE_COLLECT_SHIPPING = 'shipping';

    /**
     * @var OrderManagement
     */
    protected $orderManagement;

    /**
     * @var StockItemInterface
     */
    protected $stockItem;

    /**
     * @var HelperData
     */
    protected $_helperData;

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * @var PriceCurrencyInterface
     */
    protected $priceCurrency;

    /**
     * @var ItemFactory
     */
    protected $orderItemFactory;

    /**
     * @var StockRegistryInterface
     */
    protected $stockRegistry;

    /**
     * @var SaveOther
     */
    protected $saveOther;

    /**
     * @var ItemComparison
     */
    protected $itemComparison;

    /**
     * @var OrderManager
     */
    protected $orderManager;

    /**
     * @param HelperData $helperData
     * @param ResourceConnection $resourceConnection
     * @param ItemFactory $orderItemFactory
     * @param SaveOther $saveOther
     * @param ItemComparison $itemComparison
     * @param OrderManager $orderManager
     */
    public function __construct(
        HelperData $helperData,
        ResourceConnection $resourceConnection,
        ItemFactory $orderItemFactory,
        SaveOther $saveOther,
        ItemComparison $itemComparison,
        OrderManager $orderManager
    ) {
        $this->_helperData        = $helperData;
        $this->resourceConnection = $resourceConnection;
        $this->orderItemFactory   = $orderItemFactory;
        $this->saveOther          = $saveOther;
        $this->itemComparison     = $itemComparison;
        $this->orderManager       = $orderManager;
        $this->priceCurrency       = $orderManager;
    }

    /**
     * @param Order $order
     * @param array $data
     * @param array $qty
     *
     * @return array
     * @throws Exception
     */
    public function saveOrder($order, $data)
    {
        $origItems = $oldShippedQty = [];
        foreach ($order->getItems() as $item) {
            if ($item->getParentItemId()) {
                continue;
            }
            $origItem = $this->orderItemFactory->create();
            $origItem->setData($item->getData());
            $origItems[]                    = $origItem;
            $oldShippedQty[$item->getSku()] = $item->getQtyShipped();
        }

        $total = $this->collectTotals($order, $data);

        $newOrderIds  = [];
        if (array_key_exists('orderData', $data)) {
            $orderData = $data['orderData'];
            foreach ($order->getAllItems() as $orderItem) {
                $newOrderIds[] = $orderItem->getProductId();
                if ((isset($orderData['invoiceData'][$orderItem->getProductId()])) &&
                    (float) $orderData['invoiceData'][$orderItem->getProductId()] > (float) $orderItem->getQtyOrdered()) {
                    $orderData['invoiceData'][$orderItem->getProductId()] = $orderItem->getQtyOrdered();
                }
            }
        }

        $this->itemComparison->changeSubTotalOrder($data, $order, $total);

        $order->setMpIsEditOrder('1');
        $order->save();

        return [
            'success' => true
        ];
    }

    /**
     * @param Order $order
     * @param array $data
     *
     * @return array
     * @throws Exception
     */
    public function collectTotals($order, $data)
    {
        $totals = [];

        if ($data['type'] === self::TYPE_COLLECT_SHIPPING) {
            $totals = $this->collectTotalsByShipping($order, $data);
        }

        if ($data['type'] === self::TYPE_COLLECT_ITEMS) {
            $orderDataUpdate = $this->itemComparison->reSetUpdateOrder($order);
            $totals          = $this->collectTotalsByItems($order, $data, $orderDataUpdate);
            $this->orderManager->updateItemManager($order);
        }

        return $totals;
    }

    /**
     * @param Order $order
     * @param array $shipData
     *
     * @return array
     */
    public function collectTotalsByShipping($order, $shipData)
    {
        $oldShipAmount     = $order->getBaseShippingAmount();
        $oldGrandTotal     = $order->getBaseGrandTotal();
        $oldTaxAmount      = $order->getBaseTaxAmount();
        $oldShipTax        = $order->getBaseShippingTaxAmount();
        $oldDiscountAmount = $order->getBaseDiscountAmount();
        $oldShipDiscount   = $order->getBaseShippingDiscountAmount();

        $newShipTaxAmount  = $shipData['ship_tax_percent'] * $shipData['ship_amount'] / 100;
        $newTaxAmount      = $oldTaxAmount - $oldShipTax + $newShipTaxAmount;
        $newDiscountAmount = -$oldDiscountAmount - $oldShipDiscount + $shipData['ship_discount_amount'];
        $newGrandTotal     = $oldGrandTotal - $oldDiscountAmount -
            $oldTaxAmount + $newTaxAmount - $newDiscountAmount - $oldShipAmount + $shipData['ship_amount'];

        $shipData['ship_amount']          = $this->_helperData->convertCurrency($shipData['ship_amount'], $order);
        $newShipTaxAmount                 = $this->_helperData->convertCurrency($newShipTaxAmount, $order);
        $shipData['ship_discount_amount'] = $this->_helperData->convertCurrency($shipData['ship_discount_amount'], $order);
        $newTaxAmount                     = $this->_helperData->convertCurrency($newTaxAmount, $order);
        $newDiscountAmount                = $this->_helperData->convertCurrency($newDiscountAmount, $order);
        $newGrandTotal                    = $this->_helperData->convertCurrency($newGrandTotal, $order);

        return [
            'ship_amount'          => $shipData['ship_amount'],
            'ship_tax_amount'      => $newShipTaxAmount,
            'ship_discount_amount' => $shipData['ship_discount_amount'],
            'ship_description'     => $shipData['ship_description'],
            'tax_amount'           => $newTaxAmount,
            'discount_amount'      => $newDiscountAmount,
            'grand_total'          => $newGrandTotal
        ];
    }

    /**
     * @param Order $order
     * @param array $itemsData
     *
     * @return array
     * @throws Exception
     */
    public function collectTotalsByItems($order, $itemsData, $orderDataUpdate)
    {
        $weight            = 0;
        $shipTax           = $order->getBaseShippingTaxAmount();
        $newTaxAmount      = $itemsData['tax_amount'] + $shipTax;
        $shipDiscount      = $order->getBaseShippingDiscountAmount();
        $newDiscountAmount = $itemsData['discount_amount'] + $shipDiscount;
        $shipAmount        = $order->getBaseShippingAmount();
        $removeItemQuote   = $orderDataUpdate[ItemComparison::REMOVE_ITEMS];

        //calculate weight
        foreach ($order->getItems() as $item) {
            $weight += $this->itemComparison->weightCalculate($item->getProductId(), $item->getQtyOrdered());
        }
        foreach ($removeItemQuote as $quote) {
            $weight -= $this->itemComparison->weightCalculate($quote['product_id'], $quote['qty']);
        }
        $order->setWeight($weight);
        $newSubtotal        = $this->itemComparison->getSubtotalByItems($order);
        $fixedProductTax    = $this->getTotalFPT($order); // not in Tax Amount of Item
        $newSubtotalInclTax = $itemsData['tax_amount'] + $newSubtotal + $fixedProductTax;
        $newGrandTotal      = $newSubtotal + $shipAmount + $newTaxAmount + $fixedProductTax - $newDiscountAmount;

        if ($this->_helperData->isIncludeFPTInSubtotal()) {
            $newSubtotal += $fixedProductTax;
        }
        return [
            'tax_amount'        => $newTaxAmount,
            'subtotal'          => $newSubtotal,
            'discount_amount'   => $newDiscountAmount,
            'grand_total'       => $newGrandTotal,
            'subtotal_incl_tax' => $newSubtotalInclTax
        ];
    }

    /**
     * All Fixed Product Tax
     *
     * @param Order $order
     *
     * @return float
     */
    public function getTotalFPT($order)
    {
        $totalFPT = 0;
        foreach ($order->getAllItems() as $item) {
            $totalFPT += (float) $item->getData('weee_tax_applied_row_amount');
        }

        return $totalFPT;
    }
}