<?php
/**
 * @category    Leadlion
 * @package     Leadlion_AutoRelated
 * @author      Leadlion Team <info@Leadlion.com>
 * @copyright   Copyright (c) 2019 Leadlion (https://www.Leadlion.com)
 * @license     https://opensource.org/licenses/gpl-3.0.html  GNU General Public License (GPL 3.0)
 */

namespace Leadlion\AutoRelated\Model;

use Leadlion\AutoRelated\Helper\Data;
use Magento\Catalog\Model\Product\Visibility;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollection;
use Magento\CatalogInventory\Helper\Stock;
use Magento\Framework\App\CacheInterface;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Registry;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Catalog\Api\CategoryRepositoryInterface;

class Collection extends AbstractModel
{
    const DEFAULT_LIMIT = '50';
    /**
     * @var \Magento\Core\Model\Factory\Helper
     */
    protected $_helper;

    /**
     * @var \Magento\Framework\App\ObjectManager
     */
    protected $_objectManager;

    /**
     * Catalog config
     *
     * @var \Magento\Catalog\Model\Config
     */
    protected $_catalogConfig;

    /**
     * @var \Magento\Framework\Registry
     */
    protected $_registry;

    /**
     * @var Stock
     */
    protected $_stockHelper;

    /**
     * @var \Magento\Catalog\Model\Product\Visibility
     */
    protected $_productVisibility;

    /**
     * @var Magento\Framework\Stdlib\DateTime\TimezoneInterface
     */
    protected $_localeDate;

    /**
     * @var ProductCollection
     */
    protected $_productCollection;

    /**
     * @var int
     */
    protected $_limit;
    private $cache;
    private $serializer;
    private $objectManager;
    /**
     * @var Magento\Catalog\Helper\ProductInterface
     */
    private $_productHelper;
    /**
     * @var CategoryRepositoryInterface
     */
    private $_categoryRepository;

    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $_storeManager;

    protected $eavConfig;

    /**
     * Collection constructor.
     * @param Registry $registry
     * @param Data $helper
     * @param Stock $stockHelper
     * @param Visibility $productVisibility
     * @param Category $categoryModel
     * @param TimezoneInterface $localeDate
     * @param CollectionFactory $productCollection
     */
    public function __construct(
        Registry $registry,
        Data $helper,
        Stock $stockHelper,
        Visibility $productVisibility,
        TimezoneInterface $localeDate,
        CategoryRepositoryInterface $categoryRepository,
        ProductCollection $productCollection,
        \Magento\Catalog\Block\Product\Context $context,
        SerializerInterface $serializer,
        \Magento\Catalog\Helper\Data $productHelper,
        CacheInterface $cache,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Eav\Model\Config $eavConfig
    ) {
        $this->_registry = $registry;
        $this->_helper = $helper;
        $this->_productHelper = $productHelper;
        $this->_stockHelper = $stockHelper;
        $this->_productVisibility = $productVisibility;
        $this->_categoryRepository = $categoryRepository;
        $this->_localeDate = $localeDate;
        $this->_productCollection = $productCollection;
        $this->_catalogConfig = $context->getCatalogConfig();
        $this->_limit =  $this->_helper->get_limit();
        $this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $this->cache = $cache;
        $this->serializer = $serializer;
        $this->_storeManager = $storeManager;
        $this->eavConfig = $eavConfig;
    }

    public function getAdvancedCollection($product, $collectionType, $advancedInfo)
    {
        $products = null;
        if (empty($advancedInfo)) {
            return $this->_getAutoFilersCollection($product);
        }

        foreach ($advancedInfo as $key => $info) {
            foreach ($info as $key => $infoRel) {
                $relatedRules[$infoRel['category']] = $infoRel;
            }
        }
        $category = false;
        $catIds = $product->getCategoryIds();
        if (!empty($catIds)) {
            $lastCategoryId = end($catIds);
            if ($lastCategoryId == $this->getRootCategoryId()) {
                $lastCategoryId = prev($catIds);
            }
            if (!in_array($lastCategoryId, array_keys($relatedRules))) {
                return $this->_getAutoFilersCollection($product);
            }
            $category =  $this->_categoryRepository->get($lastCategoryId);
            if ($category) {
                $advancedCategoryRules = $relatedRules[$lastCategoryId];
                $products = $this->_productCollection->create();
                $priceRule = $advancedCategoryRules['price_filter'];
                if ($priceRule != '0') {
                    $this->addPriceRule(
                        $priceRule,
                        $advancedCategoryRules['comparison_value'],
                        $product->getFinalPrice(),
                        $products
                    );
                }
                $products = $this->_addProductAttributesAndPrices($products)
                ->addAttributeToSelect('auto_body')
                ->addAttributeToSelect('auto_from_to')
                ->addAttributeToSelect('auto_from_to_covorase')
                    ->addAttributeToFilter('status', 1)
                    ->addAttributeToFilter('is_saleable', 1)
                    ->addAttributeToFilter('visibility', 4)
                    ->addAttributeToFilter('entity_id', ['neq' => $product->getId()]);
                if ($advancedCategoryRules['products_cat'] == 1) {
                    $products->addCategoryFilter($category);
                } else {
                    $products->getSelect()->joinLeft(
                        ['cp' => $products->getResource()->getTable('catalog_category_product')],
                        'cp.product_id = e.entity_id',
                        ['category_id']
                    );
                    $products->getSelect()->where('cp.category_id not in (?)', [$lastCategoryId, 2]);
                }
                if ($product->getData('model_bare_transversale') &&
                    $product->getAttributeText('model_bare_transversale') &&
                    $collectionType != 'related'
                ) {
                    $model = explode(' ', trim($product->getAttributeText('model_bare_transversale')));
                    $products->addAttributeToFilter('name',
                        ['like' => '%'.$product->getAttributeText('auto_brand').' '.$model[0].'%']
                    );
                } else {
                    $autoFiltersConfig = $advancedCategoryRules['filters'];
                    if ($autoFiltersConfig) {
                        $autoFromToFilters = [];
                        $autoFromToCurrentValue = null;
                        $autoFilters = array_map('trim', explode(',', $autoFiltersConfig));
                        foreach ($autoFilters as $filterCode) {
                            if (strpos($filterCode, 'auto_from_to') !== false) {
                                $autoFromToProduct = $product->getAttributeText($filterCode);
                                if ($autoFromToProduct) {
                                    $autoFromToCurrentValue = $autoFromToProduct;
                                }
                            } else {
                                $products->addAttributeToFilter($filterCode, ['eq' => $product->getData($filterCode)]);
                            }
                        }
                        if ($autoFromToCurrentValue) {
                            foreach ($autoFilters as $filterCode) {
                                if (strpos($filterCode, 'auto_from_to') !== false) {
                                    $optionId = $this->eavConfig->getAttribute('catalog_product', $filterCode)
                                        ->getSource()->getOptionId($autoFromToCurrentValue);
                                    $autoFromToFilters[] = [
                                        'attribute' => $filterCode,
                                        'eq' => $optionId
                                    ];
                                }
                            }
                            $products->addAttributeToFilter($autoFromToFilters, null, 'left');
                        }
                    }
                }
                $products->getSelect()->joinLeft(
                    ['_inv' => $products->getResource()->getTable('cataloginventory_stock_status')],
                    "_inv.product_id = e.entity_id and _inv.website_id = 0",
                    ['qty','stock_status']
                );
                $products->addExpressionAttributeToSelect('in_stock', 'IF(_inv.qty<= 0, 0, 1)', []);
                
                $products->getSelect()->group('e.entity_id');

                $products->getSelect()->reset('order');
                $products->getSelect()->order('in_stock DESC');
                $products->getSelect()->order('stock_status DESC');

                $products->setPageSize(self::DEFAULT_LIMIT)->load();

                $hasAutoFromToFilter = !empty($autoFilters) && count(array_filter($autoFilters, function($f) {
                    return strpos($f, 'auto_from_to') !== false;
                })) > 0;

                if ($hasAutoFromToFilter) {
                    foreach ($products as $upsellProd) {
                        $toRemove = true;
                        foreach ($autoFilters as $filterCode) {
                            if (strpos($filterCode, 'auto_from_to') !== false) {
                                $autoFromToForUpsellProduct = $upsellProd->getAttributeText($filterCode);
                                $autoFromToForCurrentProduct = $product->getAttributeText($filterCode);
                                if ($autoFromToForUpsellProduct == $autoFromToForCurrentProduct) {
                                    $toRemove = false;
                                }
                            }
                        }
                        if ($toRemove) {
                            $products->removeItemByKey($upsellProd->getId());
                        }
                    }
                }
            } else {
                return false;
            }
        }
        return $products;
    }

    protected function getRootCategoryId()
    {
        return $this->_storeManager->getStore()->getRootCategoryId();
    }

    protected function _getAutoFilersCollection($product)
    {
        $autoFilters= explode(',', $this->_helper->getConfigData('leadlion/layer_navigation/auto_filters'));

        $category = false;
        $catIds = $product->getCategoryIds();
        $products = $this->_productCollection->create();
        if (!empty($catIds)) {
            $category =  $this->_categoryRepository->get($catIds[0]);
        }
        if ($category) {
            $products=$this->_addProductAttributesAndPrices($products)
                ->addAttributeToFilter('status', 1)
                ->addAttributeToFilter('is_saleable', 1)
                ->addAttributeToFilter('visibility', 4)
                ->addAttributeToFilter('entity_id', ['neq' => $product->getId()])
                ->addCategoryFilter($category);
            foreach ($autoFilters as $filterCode) {
                $products->addAttributeToFilter($filterCode, ['eq' => $product->getData($filterCode)]);
            }
            $products->setPageSize(self::DEFAULT_LIMIT)
            ->load();

        } else {
            return false;
        }

        return $products;
    }
    
    protected function addPriceRule(
        $priceRule,
        $comparisonValue,
        $currentProductPrice,
        $collection
    ) {
        switch ($priceRule) {
            case 'gt_displayed_product':
                if ($comparisonValue) {
                    $computedPrice = $currentProductPrice + $comparisonValue;
                    $collection->addPriceDataFieldFilter('%s >= %s', ['final_price', $currentProductPrice]);
                    $collection->addPriceDataFieldFilter('%s <= %s', ['final_price', $computedPrice]);
                } else {
                    $collection->addPriceDataFieldFilter('%s >= %s', ['final_price', $currentProductPrice]);
                }
                break;
            case 'lt_displayed_product':
                if ($comparisonValue) {
                    $computedPrice = $currentProductPrice - $comparisonValue;
                    $collection->addPriceDataFieldFilter('%s >= %s', ['final_price', $computedPrice]);
                    $collection->addPriceDataFieldFilter('%s <= %s', ['final_price', $currentProductPrice]);
                } else {
                    $collection->addPriceDataFieldFilter('%s <= %s', ['final_price', $currentProductPrice]);
                }
                break;
            case 'ua_displayed_product':
                if ($comparisonValue) {
                    $from = $currentProductPrice - $comparisonValue;
                    $to = $currentProductPrice + $comparisonValue;
                    $collection->addPriceDataFieldFilter('%s >= %s', ['final_price', $from]);
                    $collection->addPriceDataFieldFilter('%s <= %s', ['final_price', $to]);
                }
                break;
            case 'gt_value':
                if ($comparisonValue) {
                    $collection->addPriceDataFieldFilter('%s >= %s', ['final_price', $comparisonValue]);
                }
                break;
            case 'lt_value':
                if ($comparisonValue) {
                    $collection->addPriceDataFieldFilter('%s <= %s', ['final_price', $comparisonValue]);
                }
                break;
        }
    }

    /**
     * Add all attributes and apply pricing logic to products collection
     * to get correct values in different products lists.
     * E.g. upsells, new products, recently viewed
     *
     * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
     * @return \Magento\Catalog\Model\ResourceModel\Product\Collection
     */
    protected function _addProductAttributesAndPrices(
        \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
    ) {
        return $collection
            ->addMinimalPrice()
            ->addFinalPrice()
            ->addTaxPercents()
            ->addAttributeToSelect($this->_catalogConfig->getProductAttributes())
            ->addUrlRewrite();
    }
}