<?php

/**
 * Copyright © FiloBlu S.p.A. 2019. All rights reserved.
 */

namespace FiloBlu\Rma\Handlers;

use DateTime;
use FiloBlu\Rma\Helper\ChangeSizeAttributeHelper;
use FiloBlu\Rma\Helper\ChangeSizeOrder;
use FiloBlu\Rma\Helper\ReturnableProductHelper;
use FiloBlu\Rma\Helper\RmaHelper;
use FiloBlu\Rma\Model\ChangeSize;
use FiloBlu\Rma\Model\Payment\ChangeSizePay;
use Magento\Catalog\Model\ProductRepository;
use Magento\ConfigurableProduct\Api\LinkManagementInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Rma\Helper\Data;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderInterfaceFactory;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\ItemRepository;
use Magento\Setup\Exception;
use Psr\Log\LoggerInterface;
use Throwable;

use function count;
use function is_object;

/**
 *
 */
class CanCreateRmaHandler
{
    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;
    /**
     * @var OrderRepositoryInterface
     */
    protected $orderRepository;

    /**
     * @var LoggerInterface
     */
    protected $logger;

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

    /**
     * @var ChangeSize
     */
    protected $changeSize;

    /**
     * @var ItemRepository
     */
    protected $itemRepository;

    /**
     * @var ProductRepository
     */
    protected $productRepository;

    /**
     * @var LinkManagementInterface
     */
    protected $linkManagment;

    /**
     * @var ChangeSizePay
     */
    protected $changeSizePay;

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

    /**
     * @var Data
     */
    private $data;

    /**
     * @var OrderInterfaceFactory
     */
    private $orderInterfaceFactory;

    /**
     * @var ChangeSizeAttributeHelper
     */
    private $changeSizeAttributeHelper;

    /**
     * @var RmaHelper
     */
    private $rmaHelper;

    /**
     * Data constructor.
     * @param ScopeConfigInterface $scopeConfig
     * @param OrderRepositoryInterface $orderRepository
     * @param LoggerInterface $logger
     * @param RequestInterface $request
     * @param ItemRepository $itemRepository
     * @param ProductRepository $productRepository
     * @param LinkManagementInterface $linkManagement
     * @param ChangeSize $changeSize
     * @param ChangeSizePay $changeSizePay
     * @param ChangeSizeOrder $changeSizeOrder
     * @param Data $data
     * @param OrderInterfaceFactory $orderInterfaceFactory
     * @param ChangeSizeAttributeHelper $changeSizeAttributeHelper
     * @param RmaHelper $rmaHelper
     */
    public function __construct(
        ScopeConfigInterface $scopeConfig,
        OrderRepositoryInterface $orderRepository,
        LoggerInterface $logger,
        RequestInterface $request,
        ItemRepository $itemRepository,
        ProductRepository $productRepository,
        LinkManagementInterface $linkManagement,
        ChangeSize $changeSize,
        ChangeSizePay $changeSizePay,
        ChangeSizeOrder $changeSizeOrder,
        Data $data,
        OrderInterfaceFactory $orderInterfaceFactory,
        ChangeSizeAttributeHelper $changeSizeAttributeHelper,
        RmaHelper $rmaHelper
    ) {
        $this->scopeConfig = $scopeConfig;
        $this->orderRepository = $orderRepository;
        $this->logger = $logger;
        $this->request = $request;
        $this->itemRepository = $itemRepository;
        $this->productRepository = $productRepository;
        $this->linkManagment = $linkManagement;
        $this->changeSize = $changeSize;
        $this->changeSizePay = $changeSizePay;
        $this->changeSizeOrder = $changeSizeOrder;
        $this->data = $data;
        $this->orderInterfaceFactory = $orderInterfaceFactory;
        $this->changeSizeAttributeHelper = $changeSizeAttributeHelper;
        $this->rmaHelper = $rmaHelper;
    }

    /**
     * @param $order
     * @param false $forceCreate
     * @return bool
     */
    public function canCreateRma($order, $forceCreate = false)
    {
        if ($forceCreate) {
            return true;
        }

        if (!is_object($order)) {
            $order = $this->orderInterfaceFactory->create()->load($order);
        }

        if (!$this->checkAvailability($order)) {
            return false;
        }

        if (!$this->checkIfProductHasRmaEnabled($order)) {
            return false;
        }

        if (!$this->rmaHelper->orderContainsReturnableProducts($order)) {
            return false;
        }

        return $this->isValid($order);
    }

    /**
     * TODO: reduce complexity and check code coherence
     * @param OrderInterface $order
     * @return bool
     */
    public function checkAvailability(OrderInterface $order)
    {
        try {
            $items = $this->request->getParam('items') ?? [];

            if (count($items) === 0) {
                return true;
            }

            foreach ($items as $item) {
                if (!isset($item['change_size'])) {
                    return true;
                }

                $itemId = $item['change_size'];
                $simpleProduct = $this->productRepository->getById($itemId);

                if (!$this->changeSize->isSaleable($simpleProduct, $order->getStoreId(), $item['qty_requested'])) {
                    $this->logger->critical(
                        "ChangeSize Rma : product with sku {$simpleProduct->getSku()} is not salable!"
                    );
                    return false;
                }

                if (!isset($item['qty_requested']) || $item['qty_requested'] <= 0) {
                    return false;
                }

                if (
                    $this->changeSize->getProductQtyById(
                        $simpleProduct->getId(),
                        $order->getStoreId()
                    ) < $item['qty_requested']
                ) {
                    return false;
                }

                if ($this->allowChangeSizeOrderRma()) {
                    $params = $this->request->getParams();
                    if (isset($params['order_id'])) {
                        $orderId = $params['order_id'];
                        $this->logger->info(
                            "ChangeSize Rma : changeSize allowed on ChangeSizeOrder with id $orderId by BE configuration."
                        );
                    }

                    return true;
                }

                if (!$this->isChangeSizeOrder($order)) {
                    return true;
                }

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

                if ($this->isChangeSizeOrder($order) && $item['resolution'] != $changeSizeOptionId) {
                    return true;
                }

                $this->logger->info(
                    "ChangeSize Rma: can't create changeSize rma because of this is a Change Size Order"
                );
                return false;
            }
            return true;
        } catch (\Exception $exception) {
            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
            return false;
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage(), ['exception' => $throwable]);
            return false;
        }
    }

    /**
     * @return bool
     */
    public function allowChangeSizeOrderRma()
    {
        return $this->changeSizeAttributeHelper->canChangesizeOrderUseChangesize();
    }

    /**
     * @return bool
     */
    public function isChangeSizeOrder($order)
    {
        try {
            if ($order->getEntityId()) {
                return $this->changeSizeOrder->isChangeSizeOrder($order);
            }

            throw new Exception('Error order_id is undefined');
        } catch (\Exception $exception) {
            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
            return false;
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage(), ['exception' => $throwable]);
            return false;
        }
    }

    /**
     * @param  $order
     * @return bool
     */
    public function isValid($order)
    {
        try {
            if (!is_object($order)) {
                $order = $this->orderRepository->get($order);
            }

            if (!$this->rmaHelper->isValidPickupInStoreOrderStatus($order)) {
                return false;
            }

            $validityDays = $this->rmaHelper->getValidityDays($order->getStoreId());

            $allShipments = $order->getShipmentsCollection();

            if (count($allShipments) === 0) {
                return false;
            }

            $orderDate = null;
            foreach ($allShipments as $shipment) {
                $orderDate = $shipment->getCreatedAt();
            }

            $dateTimeCreated = new DateTime($orderDate);
            $dateTimeCreated->setTime(0, 0, 0);
            $dateTimeValidityEnds = $dateTimeCreated;
            $dateTimeValidityEnds->modify("+{$validityDays}day");
            $today = new DateTime();
            $today->setTime(0, 0, 0);
            if ($today <= $dateTimeValidityEnds) {
                return true;
            }
        } catch (\Exception $exception) {
            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
        } catch (Throwable $throwable) {
            $this->logger->critical($throwable->getMessage(), ['exception' => $throwable]);
        }

        return false;
    }

    /**
     * @param OrderInterface $order
     * @return bool
     * @throws LocalizedException
     */
    public function getCanOrderCreateRma(OrderInterface $order)
    {
        return $this->checkAvailability($order) && $this->isValid($order) && $this->checkIfIsReturnable($order);
    }

    /**
     * @param \Magento\Sales\Api\Data\OrderInterface $order
     * @return bool
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function checkIfIsReturnable(OrderInterface $order)
    {
        $items = $this->data->getOrderItems($order);

        return $items->count() && $this->data->isEnabled();
    }

    public function checkIfProductHasRmaEnabled($order)
    {
        $items = $this->data->getOrderItems($order);

        $returnableItems = 0;

        foreach ($items as $item) {
            $product = $item->getProduct();

            if (!$this->rmaHelper->isProductReturnable($product, $item->getSku())) {
                continue;
            }

            $returnableItems++;
        }

        return $returnableItems;

    }
}
