<?php

namespace FiloBlu\Rma\Model;

use FiloBlu\Rma\Api\Data\RmaAttributeMetadataInterfaceFactory;
use FiloBlu\Rma\Api\Data\RmaOptionInterfaceFactory;
use FiloBlu\Rma\Helper\EasyReturnHelper;
use FiloBlu\Rma\Helper\OfflinePaymentMethodsHelper;
use Magento\Framework\App\RequestInterface;
use Magento\Sales\Api\Data\OrderItemInterfaceFactory;
use function count;

use Exception;
use FiloBlu\ExtInventory\Helper\Data;
use FiloBlu\Rma\Api\ChangeSizeInterface;
use FiloBlu\Rma\Helper\ChangeSizeAttributeHelper;
use FiloBlu\Rma\Helper\RmaHelper;
use FiloBlu\Rma\Logger\Logger;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\ProductRepository;
use Magento\Catalog\Model\ResourceModel\Product;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\CatalogInventory\Api\StockStateInterface;
use Magento\ConfigurableProduct\Api\LinkManagementInterface;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;
use FiloBlu\Rma\Api\Data\RmaAttributeMetadataInterface;
use Magento\Customer\Model\Session;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Module\Manager;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface;
use Magento\InventorySalesApi\Api\StockResolverInterface;
use Magento\Rma\Api\RmaAttributesManagementInterface;
use Magento\Sales\Api\OrderItemRepositoryInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\ItemRepository;
use Magento\Store\Model\StoreManagerInterface;
use Throwable;
use Zend_Db_Statement_Exception;

/**
 *
 */
class ChangeSize implements ChangeSizeInterface
{
    /** @var string */
    const STOCK_MIN_QTY = 'filoblu_disablerma_section/rma_group/rma_minimum_qty';

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

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

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

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

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

    /**
     * @var JsonFactory
     */
    protected $jsonFactory;

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

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

    /**
     * @var Session
     */
    protected $customerSession;

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

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

    /**
     * @var Product
     */
    protected $resourceModel;

    /**
     * @var Configurable
     */
    protected $configurableResourceModel;

    /**
     * @var RmaHelper
     */
    protected $rmaHelper;
    /**
     * @var OrderItemInterfaceFactory
     */
    protected $orderItemFactory;
    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @var ObjectManager
     */
    private $objectManager;

    /**
     * @var Manager
     */
    private $manager;

    /**
     * @var RmaAttributesManagementInterface
     */
    private $rmaAttributesManagement;

    /**
     * @var OrderItemRepositoryInterface
     */
    private $orderItemRepository;

    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;
    /**
     * @var EasyReturnHelper
     */
    private $easyReturnHelper;
    /**
     * @var OfflinePaymentMethodsHelper
     */
    private $offlinePaymentMethodsHelper;
    /**
     * @var RmaAttributeMetadataInterface
     */
    private $rmaAttributeMetadata;
    /**
     * @var RmaAttributeMetadataInterfaceFactory
     */
    private $rmaAttributeMetadataFactory;
    /**
     * @var RmaOptionInterfaceFactory
     */
    private $rmaOptionInterfaceFactory;

    /**
     * ChangeSize constructor.
     * @param ChangeSizeAttributeHelper $changeSizeAttributeHelper
     * @param ItemRepository $itemRepository
     * @param ProductRepository $productRepository
     * @param LinkManagementInterface $linkManagement
     * @param JsonFactory $jsonFactory
     * @param CollectionFactory $collectionFactory
     * @param StockStateInterface $stockItem
     * @param Session $customerSession
     * @param Product $resourceModel
     * @param Configurable $configurableResourceModel
     * @param RmaHelper $rmaHelper
     * @param ScopeConfigInterface $scopeConfig
     * @param StoreManagerInterface $storeManager
     * @param Logger $logger
     * @param Manager $manager
     * @param RmaAttributesManagementInterface $rmaAttributesManagement
     * @param OrderItemRepositoryInterface $orderItemRepository
     * @param OrderRepositoryInterface $orderRepository
     * @param OrderItemInterfaceFactory $orderItemFactory
     * @param RequestInterface $request
     * @param EasyReturnHelper $easyReturnHelper
     * @param OfflinePaymentMethodsHelper $offlinePaymentMethodsHelper
     * @param RmaAttributeMetadataInterfaceFactory $rmaAttributeMetadataFactory
     * @param RmaOptionInterfaceFactory $rmaOptionInterfaceFactory
     */
    public function __construct(
        ChangeSizeAttributeHelper $changeSizeAttributeHelper,
        ItemRepository $itemRepository,
        ProductRepository $productRepository,
        LinkManagementInterface $linkManagement,
        JsonFactory $jsonFactory,
        CollectionFactory $collectionFactory,
        StockStateInterface $stockItem,
        Session $customerSession,
        Product $resourceModel,
        Configurable $configurableResourceModel,
        RmaHelper $rmaHelper,
        ScopeConfigInterface $scopeConfig,
        StoreManagerInterface $storeManager,
        Logger $logger,
        Manager $manager,
        RmaAttributesManagementInterface $rmaAttributesManagement,
        OrderItemRepositoryInterface $orderItemRepository,
        OrderRepositoryInterface $orderRepository,
        OrderItemInterfaceFactory $orderItemFactory,
        RequestInterface $request,
        EasyReturnHelper $easyReturnHelper,
        OfflinePaymentMethodsHelper $offlinePaymentMethodsHelper,
        RmaAttributeMetadataInterfaceFactory $rmaAttributeMetadataFactory,
        RmaOptionInterfaceFactory $rmaOptionInterfaceFactory
    ) {
        $this->changeSizeAttributeHelper = $changeSizeAttributeHelper;
        $this->itemRepository = $itemRepository;
        $this->productRepository = $productRepository;
        $this->linkManagement = $linkManagement;
        $this->jsonFactory = $jsonFactory;
        $this->collectionFactory = $collectionFactory;
        $this->stockItem = $stockItem;
        $this->customerSession = $customerSession;
        $this->scopeConfig = $scopeConfig;
        $this->resourceModel = $resourceModel;
        $this->configurableResourceModel = $configurableResourceModel;
        $this->rmaHelper = $rmaHelper;
        $this->storeManager = $storeManager;
        $this->objectManager = ObjectManager::getInstance();
        $this->logger = $logger;
        $this->manager = $manager;
        $this->rmaAttributesManagement = $rmaAttributesManagement;
        $this->orderItemRepository = $orderItemRepository;
        $this->orderRepository = $orderRepository;
        $this->orderItemFactory = $orderItemFactory;
        $this->request = $request;
        $this->easyReturnHelper = $easyReturnHelper;
        $this->offlinePaymentMethodsHelper = $offlinePaymentMethodsHelper;
        $this->rmaAttributeMetadataFactory = $rmaAttributeMetadataFactory;
        $this->rmaOptionInterfaceFactory = $rmaOptionInterfaceFactory;
    }

    /**
     * @param string $productIds Order Item Ids
     * @param int $websiteId
     * @return array
     */
    public function getChangeSizeEnabledProducts(string $productIds, int $websiteId)
    {
        try {
            if (!$this->changeSizeAttributeHelper->isChangeSizeEnabled($websiteId)) {
                return [];
            }
            $storeId = null;
            $enabledProducts = [];
            $orderItemIds = explode(',', $productIds);

            foreach ($orderItemIds as $orderItemId) {
                if ($storeId === null) {
                    $storeId = $this->rmaHelper->getStoreByOrderItemId($orderItemId);
                }

                $item = $this->itemRepository->get($orderItemId);

                $sku = $item->getSku();

                if ($this->easyReturnHelper->isABundleSku($orderItemId)) {
                    continue;
                }

                $product = $this->productRepository->get($sku, false, $storeId);
                $parentProducts = $this->configurableResourceModel->getParentIdsByChild($product->getId());

                if (!$product->getIsReturnable()) {
                    continue;
                }

                if ($parentProducts) {
                    $enabledProducts[] = $orderItemId;
                }
            }

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

    /**
     * @param int $itemId
     * @param int $qty
     * @return RmaAttributeMetadataInterface[]
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getAllAttributesWithAlternativesMetadata(int $itemId, int $qty)
    {
        $attributesMetadata = [];
        $attributes = $this->rmaAttributesManagement->getAllAttributesMetadata();
        $reasonId = (int)$this->request->getParam('reason_id') ?? null;

        foreach ($attributes as $attribute) {
            $attributeCode = $this->rmaAttributeMetadataFactory->create(['data' => $attribute->__toArray()]);
            switch ($attributeCode->getAttributeCode()) {
                case 'reason':
                    $attributeCode->setOptions($this->getAllowedReasons($itemId, $attribute, $qty, $reasonId));
                    break;

                case 'resolution':
                    $attributeCode->setOptions($this->getAllowedResolutions($itemId, $attribute, $qty, $reasonId));
                    break;
            }

            $attributesMetadata[] = $attributeCode;
        }

        return $attributesMetadata;
    }

    /**
     * @return array
     */
    public function getAllowedReasons($itemId, $attributeCode, $qty, $selectedReasonId)
    {
        $reasons = $this->rmaHelper->getAllMappedReason();

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

        $item = $this->itemRepository->get($itemId);

        $disallowedRmaStatus = $this->rmaHelper->getDisallowedRmaStatusEmailSending();
        $disallowedRmaStatusProductAttributeBind = $this->rmaHelper->getDisallowedRmaStatusProductAttributeBind();

        $allowed = [];

        $attributeValueId = null;

        foreach ($reasons as $reason) {

            $showAttribute = false;

            if ($disallowedRmaStatusProductAttributeBind && in_array($reason['brand_reason'], $disallowedRmaStatus)) {

                $key = array_search($reason['brand_reason'], $disallowedRmaStatus);
                $attributeValueId = $disallowedRmaStatus[$key];

                $product = $item->getProduct();

                $attribute = $disallowedRmaStatusProductAttributeBind[$key];

                if ($attribute) {
                    $showAttribute = (bool)$product->getData($attribute);

                    if (!$showAttribute) {
                        if ($item->getProductType() == 'configurable') {
                            $childrenItems = $item->getChildrenItems();
                            $item = reset($childrenItems);
                        }

                        $product = $item->getProduct();
                        $showAttribute = (bool)$product->getData($attribute);
                    }
                }

            }

            if ($reason['is_visible'] == 1) {
                $magentoReasons = $this->rmaAttributesManagement
                    ->getAttributeMetadata('reason')
                    ->getOptions();
                foreach ($magentoReasons as $magentoReason) {

                    if ($magentoReason->getValue() == $attributeValueId) {
                        if (!$showAttribute) {
                            continue;
                        }
                    }

                    if ($reason['brand_reason'] == $magentoReason->getValue()) {
                        $allowed[] = ['label' => $magentoReason->getLabel(), 'value' => $magentoReason->getValue()];
                    }
                }
            }
        }

        return $allowed;
    }

    /**
     * @return string
     */
    public function getLabel()
    {
        try {
            return (string)$this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
            return ChangeSizeAttributeHelper::REASON_CHANGE_SIZE_LABEL;
        }
    }

    /**
     * @param $itemId
     * @param $attributeCode
     * @param $qty
     * @param $selectedReasonId
     * @return array
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getAllowedResolutions($itemId, $attributeCode, $qty, $selectedReasonId)
    {
        $allowed = [];
        $websiteId = $this->storeManager->getWebsite()->getId();
        $enabled = $this->changeSizeAttributeHelper->isChangeSizeEnabled($websiteId);
        $changeSizeId = $this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();
        $defaultReasonId = $this->rmaHelper->getDefaultChangeSizeMappedReason();
        $ibanRequired = $this->getOrderIbanRequired($itemId);

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

        if (
            !$enabled || ($this->isChangeSizeOrder(
                $itemId
            ) && !$this->changeSizeAttributeHelper->canChangesizeOrderUseChangesize($websiteId))
        ) {
            foreach ($attributeCode->getOptions() as $option) {

                if ($option->getValue() == '') {
                    continue;
                }

                if ($option->getValue() == $changeSizeOptionId) {
                    $ibanRequired = false;
                }

                if ($option->getValue() != $changeSizeId) {
                    $allowed[] = $this->rmaOptionInterfaceFactory->create(['data' => $option->__toArray()])->setIbanRequiredForRma($ibanRequired);
                }
            }

            return $allowed;
        }

        foreach ($attributeCode->getOptions() as $option) {

            if ($option->getValue() == '') {
                continue;
            }

            if ($option->getValue() == $changeSizeOptionId) {
                $ibanRequired = false;
            }

            if ($selectedReasonId) {
                $isSelectedReasonWrongSize = $this->isSelectedReasonWrongSize($selectedReasonId);

                if ($option->getValue() == $changeSizeId && !$isSelectedReasonWrongSize) {
                    continue;
                }
            }

            if ($option->getValue() == $changeSizeId) {
                if ($defaultReasonId > 0) {
                    $sizes = $this->getSizes($itemId, $qty, $option->getValue(), $defaultReasonId, $websiteId);
                    if (count($sizes) > 0) {
                        $option->setOptions($sizes);
                        $allowed[] = $this->rmaOptionInterfaceFactory->create(['data' => $option->__toArray()])->setIbanRequiredForRma($ibanRequired);
                    }
                }

                continue;
            }
            $allowed[] = $this->rmaOptionInterfaceFactory->create(['data' => $option->__toArray()])->setIbanRequiredForRma($ibanRequired);
        }

        return $allowed;
    }

    /**
     * @param $itemId
     * @return bool
     */
    private function isChangeSizeOrder($itemId)
    {
        try {
            $orderId = $this->orderItemRepository->get($itemId)->getOrderId();
            $order = $this->orderRepository->get($orderId);
            return $this->changeSizeAttributeHelper->isChangeSizeOrder($order);
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
        } catch (Throwable $t) {
            $this->logger->critical($t->getMessage(), ['exception' => $t]);
        }
        return false;
    }

    /**
     * @param int $itemId
     * @param int $qty
     * @param int $resolution
     * @param int $reason
     * @param int $websiteId
     * @return array
     */
    public function getSizes(int $itemId, int $qty, int $resolution, int $reason, int $websiteId)
    {
        $size = [];

        //check if reason is allowed for ChangeSize Order
        if (!$this->rmaHelper->checkIfAllowedMagentoChangeSizeReason($reason)) {
            return $size;
        }


        try {
            // lo store id dovrebbe essere preso dal reso $item->getStoreId();
            $storeId = $this->rmaHelper->getStoreByOrderItemId($itemId);
            $showOosSizesInSelect = $this->rmaHelper->getShowOosSizesInSelect($storeId);

            if ($qty === 0) {
                $qty = 1;
            }

            /** @var \Magento\Sales\Model\Order\Item $item */
            $item = $this->itemRepository->get($itemId);

            $product = $item->getProduct();
            $originalSku = $item->getSku();
            if (!$product || $product->getTypeId() !== 'configurable') {
                return $size;
            }

            $configurable = $product->getTypeInstance();

            $usedAttributes = $configurable->getUsedProductAttributes($product);
            $attributeMap = [];

            foreach ($item->getProductOptionByCode('attributes_info') as $attribute) {
                $attributeMap[$attribute['option_id']] = $attribute['option_value'];
            }

            $selectedSize = null;
            $attributesToCheck = [];
            $selectedAttributes = [];

            foreach ($usedAttributes as $attribute) {

                if ($attribute->getAttributeCode() === 'size') {
                    if (isset($attributeMap[$attribute->getId()])) {
                        $selectedSize = $attributeMap[$attribute->getId()];
                    }

                    continue;
                }

                if (isset($attributeMap[$attribute->getId()])) {
                    $selectedAttributes[$attribute->getAttributeCode()] = $attributeMap[$attribute->getId()];
                }

                $attributesToCheck[] = $attribute->getAttributeCode();
            }

            foreach ($configurable->getUsedProducts($item->getProduct()) as $child) {

                $child = $child->load($child->getEntityId());

                if ($child->getStatus() == Status::STATUS_DISABLED) {
                    continue;
                }

                if ($child->getSize() == $selectedSize) {
                    continue;
                }

                $skip = false;
                foreach ($attributesToCheck as $attributeCode) {
                    if ($child->getData($attributeCode) != $selectedAttributes[$attributeCode]) {
                        $skip = true;
                        break;
                    }
                }

                if ($skip) {
                    continue;
                }

                if (!$this->isSaleable($child, $storeId, $qty) && !$showOosSizesInSelect) {
                    continue;
                }

                if (!is_null($child->getIsReturnable()) && !$child->getIsReturnable()) {
                    continue;
                }

                if ($child->getId() === null) {
                    $child->setId($child->getIdBySku($child->getSku()));
                }

                $productAvailableQty = $this->getProductQtyById($child->getId(), $storeId) - $this->getMinQty();

                if (!$showOosSizesInSelect && ($productAvailableQty < $qty)) {
                    continue;
                }

                if ($child->getSku() == $originalSku) {
                    continue;
                }

                if ($productAvailableQty <= 0) {
                    $productAvailableQty = 1;
                }

                $size[] = [
                    'value'       => $child->getId(),
                    'text'        => $child->getResource()->getAttribute('size')->getFrontend()->getValue(
                        $child
                    ),
                    'qty'         => $productAvailableQty,
                    'is_saleable' => $this->isSaleable($child, $storeId, $qty),
                    'selected'    => false
                ];
            }
            return $size;
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
            return $size;
        }
    }

    /**
     * @param $product
     * @param int $storeId
     * @param int $qty
     * @return bool
     * @throws LocalizedException
     */
    public function isSaleable($product, int $storeId, int $qty)
    {
        if ($this->manager->isEnabled('FiloBlu_ExtInventory')) {
            $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId();
            $websiteCode = $this->storeManager->getWebsite($websiteId)->getCode();
            return $this->isSaleableMultistock($product, $websiteCode, $qty);
        }
        try {
            $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId();
            $products = $this->collectionFactory->create()
                ->setStoreId($storeId)
                ->addPriceData($this->customerSession->getCustomerGroupId(), $websiteId)
                ->addAttributeToFilter('sku', ['eq' => $product->getSku()])
                ->getItems();
            return (bool)$products;
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
            return false;
        } catch (Throwable $t) {
            $this->logger->critical($t->getMessage(), ['exception' => $t]);
            return false;
        }
    }

    /**
     * @param $product
     * @param $websiteCode
     * @param $qty
     * @return bool
     * @throws Zend_Db_Statement_Exception
     */
    public function isSaleableMultistock($product, $websiteCode, $qty)
    {
        /** @var Data $helper */
        $helper = $this->objectManager->get(Data::class);
        $salableQty = $helper->getSalableQty($product, $websiteCode);
        return $salableQty >= $qty;
    }

    /**
     * @param $productId
     * @param $storeId
     * @return float|int
     */
    public function getProductQtyById($productId, $storeId)
    {
        try {
            if (
                !interface_exists(GetProductSalableQtyInterface::class) || !$this->manager->isEnabled(
                    'Magento_InventorySalesApi'
                )
            ) {
                return $this->stockItem->getStockQty($productId);
            }
            $websiteCode = $this->storeManager->getWebsite(
                $this->storeManager->getStore($storeId)->getWebsiteId()
            )->getCode();
            $getProductSalableQty = $this->objectManager->create(GetProductSalableQtyInterface::class);
            $stockResolver = $this->objectManager->create(StockResolverInterface::class);
            $stockId = $stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId();
            $product = $this->productRepository->getById($productId);
            return (int)$getProductSalableQty->execute($product->getSku(), $stockId);
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
            return 0;
        } catch (Throwable $t) {
            $this->logger->critical($t->getMessage(), ['exception' => $t]);
            return 0;
        }
    }

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

    public function isSelectedReasonWrongSize($selectedReason) {
        $reasons = $this->rmaHelper->getAllMappedReason();


        foreach ($reasons as $reason) {
            if ($reason['standard_reason'] != 'wrong_size') {
                continue;
            }

            if ($reason['brand_reason'] == $selectedReason) {
                return true;
            }
        }

        return false;
    }

    private function getOrderIbanRequired($itemId)
    {
        $orderId = $this->orderItemRepository->get($itemId)->getOrderId();
        $order = $this->orderRepository->get($orderId);

        return $this->offlinePaymentMethodsHelper->isIbanRequired($order);
    }
}
