<?php

namespace FiloBlu\Rma\Plugin\Model\ResourceModel;

use Exception;
use FiloBlu\Rma\Helper\RmaHelper;
use FiloBlu\Rma\Logger\Logger;
use Magento\Catalog\Model\ProductTypes\ConfigInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Rma\Api\RmaRepositoryInterface;
use Magento\Rma\Model\ResourceModel\ItemFactory;
use Magento\Rma\Model\Rma\Source\Status;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\ResourceModel\Order\Item\Collection;
use Magento\Sales\Model\ResourceModel\Order\Item\CollectionFactory;
use Throwable;
use Zend_Db_Expr;

/**
 *
 */
class Item
{
    /** @var string */
    const ITEM_COLLECTION_BY_QTY_INVOICED_XML_PATH = 'sales/rma_validity_settings/adminhtml_item_collection_logyc';
    /**
     * @var RmaHelper
     */
    protected $rmaHelper;

    /**
     * @var ItemFactory
     */
    private $itemFactory;

    /**
     * @var CollectionFactory
     */
    private $collectionFactory;

    /**
     * @var ConfigInterface
     */
    private $refundableList;

    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;

    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;

    /**
     * @var Logger
     */
    private $logger;

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

    /**
     * @var RequestInterface
     */
    private $request;

    /**
     * @var RmaRepositoryInterface
     */
    private $rmaRepository;


    /**
     * Item constructor.
     * @param ItemFactory $itemFactory
     * @param CollectionFactory $collectionFactory
     * @param ConfigInterface $refundableList
     * @param ScopeConfigInterface $scopeConfig
     * @param OrderRepositoryInterface $orderRepository
     * @param Logger $logger
     * @param ResourceConnection $resource
     * @param RmaHelper $rmaHelper
     * @param RequestInterface $request
     * @param RmaRepositoryInterface $rmaRepository
     */
    public function __construct(
        ItemFactory $itemFactory ,
        CollectionFactory $collectionFactory ,
        ConfigInterface $refundableList ,
        ScopeConfigInterface $scopeConfig ,
        OrderRepositoryInterface $orderRepository ,
        Logger $logger ,
        ResourceConnection $resource ,
        RmaHelper $rmaHelper,
        RequestInterface $request,
        RmaRepositoryInterface $rmaRepository
    )
    {
        $this->itemFactory = $itemFactory;
        $this->collectionFactory = $collectionFactory;
        $this->refundableList = $refundableList;
        $this->scopeConfig = $scopeConfig;
        $this->orderRepository = $orderRepository;
        $this->logger = $logger;
        $this->resource = $resource;
        $this->rmaHelper = $rmaHelper;
        $this->request = $request;
        $this->rmaRepository = $rmaRepository;
    }

    /**
     * @param \Magento\Rma\Model\ResourceModel\Item $subject
     * @param callable $proceed
     * @param $orderId
     * @return array
     */
    public function aroundGetReturnableItems(\Magento\Rma\Model\ResourceModel\Item $subject , callable $proceed , $orderId)
    {
        try {
            if (!$this->scopeConfig->getValue(self::ITEM_COLLECTION_BY_QTY_INVOICED_XML_PATH) ?? false) {
                return $proceed($orderId);
            }
            $order = $this->orderRepository->get($orderId);
            $itemToShip = false;
            $itemsOrdered = $order->getItems();
            foreach ($itemsOrdered as $item) {
                if ($item->getPrice() > 0) {
                    if ($item->getQtyInvoiced() - $item->getQtyShipped() > 0) {
                        $itemToShip = true;
                        break;
                    }
                }
            }
            if (!$itemToShip) {
                return $proceed($orderId);
            }
            return $this->getReturnableItems($orderId);
        } catch (Exception $exception) {
            $this->logger->critical($exception->getMessage() , ['exception' => $exception]);
            return $proceed($orderId);
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage() , ['exception' => $throwable]);
            return $proceed($orderId);
        }
    }

    /**
     * @param $orderId
     * @return array
     */
    public function getReturnableItems($orderId)
    {
        /** @var \Magento\Rma\Model\ResourceModel\Item $resourceItem */
        $resourceItem = $this->itemFactory->create();
        $connection = $resourceItem->getConnection();
        $salesAdapter = $this->resource->getConnection('sales');
        $shippedSelect = $salesAdapter->select()
            ->from(
                ['order_item' => $resourceItem->getTable('sales_order_item')] ,
                [
                    'order_item.item_id' ,
                    'order_item.qty_invoiced'
                ]
            )->where('order_item.order_id = ?' , $orderId);

        $orderItemsInvoiced = $salesAdapter->fetchPairs($shippedSelect);

        $requestedSelect = $connection->select()
            ->from(
                ['rma' => $resourceItem->getTable('magento_rma')] ,
                [
                    'rma_item.order_item_id' ,
                    new Zend_Db_Expr('SUM(qty_requested)')
                ]
            )
            ->joinInner(
                ['rma_item' => $resourceItem->getTable('magento_rma_item_entity')] ,
                'rma.entity_id = rma_item.rma_entity_id' ,
                []
            )->where(
                'rma_item.order_item_id IN (?)' ,
                array_keys($orderItemsInvoiced)
            )->where(
                sprintf(
                    '%s NOT IN (?)' ,
                    $connection->getIfNullSql('rma.status' , $connection->quote(Status::STATE_CLOSED))
                ) ,
                [Status::STATE_CLOSED]
            )->group('rma_item.order_item_id');
        $orderItemsRequested = $connection->fetchPairs($requestedSelect);
        $result = [];
        foreach ($orderItemsInvoiced as $itemId => $invoiced) {
            $requested = 0;
            if (isset($orderItemsRequested[$itemId])) {
                $requested = $orderItemsRequested[$itemId];
            }

            $result[$itemId] = 0;
            if ($invoiced > $requested) {
                $result[$itemId] = $invoiced - $requested;
            }
        }

        return $result;
    }


    /**
     * @param \Magento\Rma\Model\ResourceModel\Item $subject
     * @param callable $proceed
     * @param $orderId
     * @return Collection
     */
    public function aroundGetOrderItemsCollection(\Magento\Rma\Model\ResourceModel\Item $subject , callable $proceed , $orderId)
    {
        try {
            $this->rmaItemInconsistenciesCheck($subject, $orderId);
            if (!$this->scopeConfig->getValue(self::ITEM_COLLECTION_BY_QTY_INVOICED_XML_PATH) ?? false) {
                return $proceed($orderId);
            }
            $order = $this->orderRepository->get($orderId);
            $itemToShip = false;
            $itemsOrdered = $order->getItems();
            foreach ($itemsOrdered as $item) {
                if ($item->getPrice() > 0) {
                    if ($item->getQtyInvoiced() - $item->getQtyShipped() > 0) {
                        $itemToShip = true;
                        break;
                    }
                }
            }
            if (!$itemToShip) {
                return $proceed($orderId);
            }
            return $this->getOrderItemsCollection($orderId);
        } catch (Exception $exception) {
            $this->logger->critical($exception->getMessage() , ['exception' => $exception]);
            return $proceed($orderId);
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage() , ['exception' => $throwable]);
            return $proceed($orderId);
        }
    }


    public function getOrderItemsCollection($orderId)
    {
        /** @var \Magento\Rma\Model\ResourceModel\Item $resourceItem */
        $resourceItem = $this->itemFactory->create();
        $connection = $resourceItem->getConnection();
        $expression = new Zend_Db_Expr(
            '('
            . $connection->quoteIdentifier('qty_invoiced') . ' - ' . $connection->quoteIdentifier('qty_returned')
            . ')'
        );
        /** @var Collection $collection */
        $collection = $this->collectionFactory->create();
        return $collection->addExpressionFieldToSelect(
            'available_qty' ,
            $expression ,
            ['qty_invoiced' , 'qty_returned']
        )->addFieldToFilter(
            'order_id' ,
            $orderId
        )->addFieldToFilter(
            'product_type' ,
            ['in' => $this->refundableList->filter('refundable')]
        )->addFieldToFilter(
            $expression ,
            ['gt' => 0]
        )->addFieldToFilter('price' , ['gt' => [0]]);
    }

    /**
     * @param $subject
     * @param $result
     * @param $orderId
     * @return array|mixed
     */
    public function afterGetReturnableItems($subject , $result , $orderId)
    {
        if ($this->rmaHelper->isCustomizedProductRefundAllowed()) {
            return $result;
        }

        $order = $this->orderRepository->get($orderId);
        $itemsOrdered = $order->getItems();

        $itemsToRemove = [];
        $relation = [];

        foreach ($itemsOrdered as $item) {

            if ($item->getParentItemId()) {
                $relation[$item->getParentItemId()][$item->getItemId()] = $item->getItemId();
                continue;
            }

            $productOptions = $item->getProductOptions();

            if (!isset($productOptions['options'])) {
                continue;
            }

            $itemsToRemove[] = $item->getItemId();
        }

        if (empty($itemsToRemove)) {
            return $result;
        }

        $itemsReduced = array_reduce($itemsToRemove, function($carry, $item) use ($relation) {
            $carry[] = $item;
            if (isset($relation[$item])) {
                $carry +=  $relation[$item];
            }
            return $carry;
        }, []);

       return array_filter($result, function ($itemId) use ($itemsReduced) {
            return !(in_array($itemId, $itemsReduced));
        }, ARRAY_FILTER_USE_KEY);
    }

    protected function rmaItemInconsistenciesCheck($rmaItem, $orderId)
    {
        $rmaId = $this->request->getParam('rma_id');

        if (empty($rmaId)) {
            return false;
        }

        $rma = $this->rmaRepository->get($rmaId);

        $orderItemsToFix = [];

        foreach ($rma->getItems() as $rmaItem) {
            $orderItemsToFix[$rmaItem->getOrderItemId()] = [
                'qty_returned' => $rmaItem->getQtyReturned(),
                'qty_approved' => $rmaItem->getQtyApproved()
            ];
        }

        $order = $rma->getOrder();

        foreach ($order->getItems() as $orderItem) {
            $orderItemId = $orderItem->getItemId();
            if (array_key_exists($orderItemId, $orderItemsToFix)) {
                $orderItemToFix = $orderItemsToFix[$orderItemId];

                if (empty($orderItemToFix['qty_returned'])) {
                    if ($orderItem->getQtyReturned()) {
                        $orderItem->setQtyReturned(0);
                    }
                }

                if (empty($orderItemToFix['qty_approved'])) {
                    if ($orderItem->getQtyReturned()) {
                        $orderItem->setQtyReturned(0);
                    }
                }

                try {
                    $orderItem->save();
                } catch (Exception $exception) {
                    $this->logger->critical($exception->getMessage() , ['exception' => $exception]);
                }
            }
        }
    }
}
