<?php

/**
 * Created by PhpStorm.
 * User: jderosa
 * Date: 21/04/20
 * Time: 12.02
 */

namespace FiloBlu\Rma\Handlers;

use Exception;
use FiloBlu\Rma\Api\Data\ChangeSizeItemsInterface;
use FiloBlu\Rma\Api\RmaExtensionAttributesRepositoryInterface;
use FiloBlu\Rma\Helper\ChangeSizeAttributeHelper;
use FiloBlu\Rma\Helper\ChangeSizeOrder;
use FiloBlu\Rma\Helper\RmaHelper;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Rma\Api\Data\ItemExtensionFactory;
use Magento\Rma\Api\Data\ItemInterface;
use Magento\Rma\Api\Data\RmaInterface;
use Magento\Rma\Model\Item as ItemModel;
use Magento\Rma\Model\ResourceModel\Item\CollectionFactory as RmaCollectionFactory;
use Magento\Rma\Model\Rma\Source\Status;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
use Throwable;

use function array_key_exists;
use function array_slice;
use function key;

/**
 *
 */
class CreateChangeSizeOrderHandler
{
    /**
     * @var array
     */
    protected $changSizeItems = [];
    /**
     * @var Request
     */
    protected $webApiRequest;
    /**
     * @var OrderSender
     */
    protected $orderSender;
    /**
     * @var RmaCollectionFactory
     */
    protected $rmaItemCollectionFactory;
    /**
     * @var ResourceConnection
     */
    protected $resourceConnection;
    /**
     * @var OrderRepositoryInterface
     */
    protected $orderRepository;
    /**
     * @var ChangeSizeAttributeHelper
     */
    private $changeSizeAttributeHelper;
    /**
     * @var RmaExtensionAttributesRepositoryInterface
     */
    private $rmaExtensionAttributesRepository;
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var ChangeSizeOrder
     */
    private $changeSizeOrder;

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

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var ItemModel
     */
    private $itemModel;
    /**
     * @var RmaHelper
     */
    private $rmaHelper;
    /**
     * @var ItemExtensionFactory
     */
    private $itemExtensionFactory;

    /**
     * CreateChangeSizeOrderHandler constructor.
     * @param ChangeSizeAttributeHelper $changeSizeAttributeHelper
     * @param RmaExtensionAttributesRepositoryInterface $rmaExtensionAttributesRepository
     * @param LoggerInterface $logger
     * @param ChangeSizeOrder $changeSizeOrder
     * @param RequestInterface $request
     * @param StoreManagerInterface $storeManager
     * @param ItemModel $itemModel
     * @param RmaHelper $rmaHelper
     * @param Request $webApiRequest
     * @param OrderSender $orderSender
     * @param RmaCollectionFactory $rmaItemCollectionFactory
     * @param ResourceConnection $resourceConnection
     * @param OrderRepositoryInterface $orderRepository
     * @param ItemExtensionFactory $itemExtensionFactory
     */
    public function __construct(
        ChangeSizeAttributeHelper $changeSizeAttributeHelper,
        RmaExtensionAttributesRepositoryInterface $rmaExtensionAttributesRepository,
        LoggerInterface $logger,
        ChangeSizeOrder $changeSizeOrder,
        RequestInterface $request,
        StoreManagerInterface $storeManager,
        ItemModel $itemModel,
        RmaHelper $rmaHelper,
        Request $webApiRequest,
        OrderSender $orderSender,
        RmaCollectionFactory $rmaItemCollectionFactory,
        ResourceConnection $resourceConnection,
        OrderRepositoryInterface $orderRepository,
        ItemExtensionFactory $itemExtensionFactory
    ) {
        $this->changeSizeAttributeHelper = $changeSizeAttributeHelper;
        $this->rmaExtensionAttributesRepository = $rmaExtensionAttributesRepository;
        $this->logger = $logger;
        $this->changeSizeOrder = $changeSizeOrder;
        $this->request = $request;
        $this->storeManager = $storeManager;
        $this->itemModel = $itemModel;
        $this->rmaHelper = $rmaHelper;
        $this->webApiRequest = $webApiRequest;
        $this->orderSender = $orderSender;
        $this->rmaItemCollectionFactory = $rmaItemCollectionFactory;
        $this->resourceConnection = $resourceConnection;
        $this->orderRepository = $orderRepository;
        $this->itemExtensionFactory = $itemExtensionFactory;
    }

    /**
     * @param \Magento\Rma\Api\Data\RmaInterface $rma
     * @return \Magento\Rma\Api\Data\ItemInterface|null
     */
    public function getLastChangeSizeRmaItem(RmaInterface $rma)
    {
        $changeSizeResolution = $this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();

        $lastItem = null;
        foreach ($rma->getItems() as $rmaItem) {
            if ((int)$rmaItem->getResolution() !== $changeSizeResolution) {
                continue;
            }

            $lastItem = $rmaItem;
        }

        return $lastItem;
    }

    /**
     * TODO: Refactor
     * @param RmaInterface $subject
     * @param \Magento\Rma\Api\Data\RmaInterface $result
     * @return array
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     * @throws \Exception
     */
    public function checkIfCreateChangeSizeOrder(RmaInterface $subject, RmaInterface $result, $changeSizeItems)
    {
        $websiteId = $this->storeManager->getStore($subject->getStoreId())->getWebsiteId();

        if (!$this->changeSizeAttributeHelper->isChangeSizeEnabled($websiteId)) {
            return $result;
        }

        try {
            /** @var \Magento\Rma\Api\Data\ItemInterface[] $rmaItems */
            $rmaItems = $result->getItems();
            $parentProduct = null;
            $orderCreationStrategy = $this->rmaHelper->getChangeSizeOrderCreationStrategy();
            $changeSizeResolution = $this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();

            foreach ($rmaItems as $item) {

                if ($item->getStatus() !== Status::STATE_PENDING) {
                    continue;
                }

                if ((int)$item->getResolution() !== $changeSizeResolution) {
                    continue;
                }

                if ($this->rmaExtensionAttributesRepository->getChangeSizeOrderId($item->getId())) {
                    continue;
                }

                $itemExtensionAttributes = $item->getExtensionAttributes();

                if (empty($itemExtensionAttributes)) {
                    $itemExtensionAttributes = $this->itemExtensionFactory->create();
                }

                $changeSizeItem = [];
                $itemId = $item->getEntityId();

                if ($itemExtensionAttributes->getChangeSize(
                    ) != null) {
                    $item->setChangeSize($itemExtensionAttributes->getChangeSize());
                }

                if ($item->getChangeSize() != '' || !empty(
                    $item->getData(
                        ChangeSizeAttributeHelper::CHANGE_SIZE_RMA_ATTRIBUTE_CODE
                    )
                    )) {
                    $changeProductId = $item->getChangeSize();

                    if ($itemExtensionAttributes->getChangeSize(
                        ) != null) {
                        continue;
                    }

                    /** @var ItemModel $item */
                    $parentProduct = $item->getProductSku() ?? $this->itemModel->setEntityId($itemId)->getProductSku();
                    $changeSizeItem = [
                        'product_id'             => $changeProductId,
                        'qty'                    => $item->getQtyRequested(),
                        'parent_product_sku'     => $parentProduct,
                        'rma_entity_id'          => $itemId,
                        'original_order_item_id' => $item->getOrderItemId()
                    ];
                }

                $customEmail = $subject->getCustomerCustomEmail();
                if (isset($customEmail) && $customEmail !== '') {
                    $subject->getOrder()->setCustomerEmail($customEmail);
                }

                if ($orderCreationStrategy === 'each') {
                    // Create an order for each item requested
                    $order = $this->createChangeSizeOrder(
                        $subject->getOrder(),
                        [$changeSizeItem],
                        $subject->getEntityId(),
                        $this->canAddShippingCosts($result, $item),
                        $parentProduct
                    );

                    $this->rmaExtensionAttributesRepository->setChangeSizeOrderId($itemId, $order->getId());
                    $this->orderSender->send($order);

                    try {

                        if ($changeSizeItems) {
                            $changeSizeItems->setChangeSizeOrderId($order->getId());
                            $changeSizeItems->setChangeSizeOrderIncrementId($order->getIncrementId());
                            $changeSizeItems->save();
                        }

                    } catch (\Throwable $t) {
                        $this->logger->critical($t->getMessage(), ['exception' => $t]);
                    } catch (\Exception $e) {
                        $this->logger->critical($e->getMessage(), ['exception' => $e]);
                    }

                    continue;
                }

                $this->changSizeItems[] = $changeSizeItem;
            }

            if ($orderCreationStrategy === 'total' && $this->changSizeItems) {
                //create on order with all items requested
                $this->createChangeSizeOrder(
                    $subject->getOrder(),
                    $this->changSizeItems,
                    $subject->getEntityId(),
                    true,
                    $parentProduct
                );
            }
        } catch (Exception $exception) {
            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
            throw new Exception($exception->getMessage());
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage(), ['exception' => $throwable]);
            throw new Exception($throwable->getMessage());
        }
        return $result;
    }

    /**
     * @param \Magento\Sales\Model\Order| \Magento\Sales\Api\Data\OrderInterface $order
     * @param array $items
     * @param $rmaId
     * @param null $parentProduct
     * @param bool $calculateShipping
     * @return bool|\Magento\Sales\Api\Data\OrderInterface
     * @throws \Exception
     */
    protected function createChangeSizeOrder(
        $order,
        array $items,
        $rmaId,
        bool $calculateShipping,
        $parentProduct = null
    ) {
        $orderData = [
            'order'                   => $order,
            'rma_id'                  => $rmaId,
            'parent_product_sku'      => $parentProduct,
            'items'                   => $items,
            'request'                 => $this->request->getParams(),
            'calculate_shipping_cost' => $calculateShipping
        ];

        $request = $this->request->getParams();

        if (!$request) {
            $request = $this->webApiRequest->getParams();
        }

        if (!$request) {
            $request = $this->webApiRequest->getBodyParams();
        }

        if (isset($request['rmaDataObject']['extension_attributes'])) {
            $requestData = $request['rmaDataObject']['extension_attributes'];
            $orderData['request'] = array_merge($orderData['request'], $requestData);
            $orderData['request'] = array_merge($orderData['request'], $requestData['pickup_address']);
            unset($orderData['pickup_address']);
        }

        return $this->changeSizeOrder->createMageOrder($orderData);
    }

    /**
     * @param \Magento\Rma\Api\Data\RmaInterface $rma
     * @param \Magento\Rma\Api\Data\ItemInterface $rmaItem
     * @return bool
     */
    public function canAddShippingCosts(RmaInterface $rma, ItemInterface $currentRmaItem)
    {
        $orderItemsToRma = [];
        $order = $rma->getOrder();

        if (!$this->changeSizeAttributeHelper->mustAddShippingCosts($order->getStoreId())) {
            return false;
        }

        $changeSizeResolution = $this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();

        $changeSizeRmaItems = [];

        foreach ($order->getAllVisibleItems() as $orderItem) {
            /**
             * TODO: discriminare prodotti tipo giftcard, prodotti con custom options e giftwrap
             */

            $orderItemId = $orderItem->getItemId();

            $rmaItem = $this->getRmaByOrderItemId($orderItemId, $rma);


            if ($rmaItem) {
                $orderItemsToRma[$orderItem->getId()] = (int)$rmaItem->getRmaEntityId();


                if ($rmaItem->getResolution() != $changeSizeResolution) {

                    continue;
                }

                $changeSizeRmaItems[$orderItem->getId()][(int)$rmaItem->getRmaEntityId()] = $rmaItem->getRmaEntityId();

                continue;
            }

            $orderItemsToRma[$orderItem->getId()] = null;
        }

        $currentRmaItems = array_filter($rma->getItems(), function ($item) use ($changeSizeResolution) {
            return $item->getResolution() == $changeSizeResolution;
        });

        $lastItem = null;

        if (!empty($currentRmaItems)) {
            $lastItem = end($currentRmaItems);
        }

        if (!$lastItem) {
            return false;
        }

        $itemsCount = count($currentRmaItems) + count($changeSizeRmaItems);

        //if ($itemsCount == count($order->getAllVisibleItems() &&
        if ($itemsCount == (int)$order->getTotalQtyOrdered() &&
            (int)$lastItem->getOrderItemId() == (int)$currentRmaItem->getOrderItemId()
        ) {
            return true;
        }

        return false;
    }

    /**
     * @param $orderItemId
     * @param $rma
     * @return ItemInterface|null
     */
    public function getRmaByOrderItemId($orderItemId, $rma) {
        $connection = $this->resourceConnection->getConnection();
        $rmaItemTable = $connection->getTableName('magento_rma_item_entity');
        $rmaTable = $connection->getTableName('magento_rma');

        $select = $connection->select()->from([
            'mrie' => $rmaItemTable
        ],
            ['mrie.entity_id']
        )
            ->joinLeft(
                ['mr' => $rmaTable],
                'mrie.rma_entity_id = mr.entity_id',
                []
            )
            ->where('mrie.order_item_id = ?', $orderItemId)
            ->where('mr.status NOT IN (?)', ['closed'])
            ->where('mr.entity_id NOT IN (?)', [$rma->getId()]);

        $result = $connection->fetchOne($select);

        if ($result == null) {
            return null;
        }

        $rmaItemCollection = $this->rmaItemCollectionFactory->create();
        $rmaItem = $rmaItemCollection
            ->addFieldToSelect('*')
            ->addFieldToFilter('entity_id', $result)->load()->getFirstItem();
        /** @var ItemInterface $rmaItem */

        if ($rmaItem && $rmaItem->getEntityId() > 0 && $rmaItem->getEntityId() != '') {
            return $rmaItem;
        }

        return null;
    }

    public function generateMissingOrder($data)
    {
        try {
            $rmaId = $data->getRmaEntityId();
            $rmaItem = $data->getRmaItem();
            $order = $data->getOrder();

            $changeSizeItem = [
                'product_id'             => $data->getProductId(),
                'qty'                    => $rmaItem->getQtyRequested(),
                'parent_product_sku'     => $data->getParentSku(),
                'rma_entity_id'          => $rmaId,
                'original_order_item_id' => $data->getOriginalOrderItemId()
            ];

            $changeSizeOrder = $this->createChangeSizeOrder(
                $order,
                [$changeSizeItem],
                $rmaId,
                $this->canAddShippingCosts($data->getRma(), $rmaItem),
                $data->getParentSku()
            );

            $this->rmaExtensionAttributesRepository->setChangeSizeOrderId($rmaItem->getId(), $order->getId());
            $this->orderSender->send($changeSizeOrder);

        } catch (\Exception $exception) {
            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
            return false;
        }

        return $changeSizeOrder;
    }
}
