<?php

declare(strict_types=1);

namespace FiloBlu\Refilo\Helper;

use FiloBlu\Core\Model\Configuration;
use FiloBlu\Refilo\Model\Indexer\Locator\SnapshotMViewStateTable;
use FiloBlu\Refilo\Remote\Entity\Product;
use Magento\Bundle\Model\ResourceModel\Selection;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Mview\View\CollectionFactory;
use Magento\Framework\Mview\ViewInterface;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Api\Data\WebsiteInterface;
use Magento\Store\Model\ScopeInterface;


/**
 * Class Indexer
 * @package FiloBlu\Refilo\Helper
 */
class Indexer
{
    /** @var string */
    public const XML_SYNC_DATA_INDEX_BULK_SIZE = 'sync/data/index_bulk_size';

    /** @var string */
    public const XML_SYNC_DATA_ALLOW_INDEX_DELETION = 'sync/data/allow_index_deletion';

    /** @var string */
    public const XML_SYNC_DATA_FILTERS_WITHOUT_OUT_OF_STOCK = 'sync/data/filters_without_out_of_stock';

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

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

    /**
     * @var Configuration
     */
    private $coreConfiguration;

    /**
     * @var AdapterInterface
     */
    private $connection;

    /**
     * @var MetadataPool
     */
    private $metadataPool;
    /**
     * @var CollectionFactory
     */
    private $viewCollectionFactory;
    /**
     * @var SnapshotMViewStateTable
     */
    private $snapShooter;
    /**
     * @var Selection
     */
    private $bundleSelection;

    /**
     * Indexer constructor.
     * @param ResourceConnection $resourceConnection
     * @param ScopeConfigInterface $scopeConfig
     * @param Configuration $coreConfiguration
     * @param MetadataPool $metadataPool
     * @param CollectionFactory $viewCollectionFactory
     * @param SnapshotMViewStateTable $snapShooter
     */
    public function __construct(
        ResourceConnection      $resourceConnection,
        ScopeConfigInterface    $scopeConfig,
        Configuration           $coreConfiguration,
        MetadataPool            $metadataPool,
        CollectionFactory       $viewCollectionFactory,
        SnapshotMViewStateTable $snapShooter,
        Selection               $bundleSelection
    )
    {
        $this->resourceConnection = $resourceConnection;
        $this->scopeConfig = $scopeConfig;
        $this->coreConfiguration = $coreConfiguration;
        $this->metadataPool = $metadataPool;
        $this->viewCollectionFactory = $viewCollectionFactory;
        $this->snapShooter = $snapShooter;
        $this->bundleSelection = $bundleSelection;
    }

    /**
     * @return int
     */
    public function getIndexerBulkSize(): int
    {
        return (int)$this->scopeConfig->getValue(self::XML_SYNC_DATA_INDEX_BULK_SIZE) ?: 500;
    }

    /**
     * @return bool
     * @deprecated
     */
    public function indexerCanDelete(): bool
    {
        return (bool)$this->scopeConfig->isSetFlag(self::XML_SYNC_DATA_ALLOW_INDEX_DELETION);
    }

    /**
     *
     * @throws \Exception
     */
    public function fixPriceIndex()
    {
        $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
        $this->resourceConnection->getConnection()->query(
            "UPDATE catalog_product_index_price AS q1,
	(
	SELECT
		MIN(cpp.rule_price) AS min_price,
		MIN(cpp.rule_price) AS final_price,
	    MAX(cpp.rule_price) as max_price,
		cpp.website_id,
		cpp.customer_group_id,
		cpe.entity_id
	FROM
		catalog_product_entity cpe
	INNER JOIN catalog_product_entity_int cpei ON
		cpe.$linkField = cpei.$linkField
	INNER JOIN eav_attribute ea ON
		cpei.attribute_id = ea.attribute_id
	INNER JOIN catalog_product_super_link cpsl ON
		cpe.$linkField = cpsl.parent_id
	INNER JOIN catalogrule_product_price cpp ON
		cpp.product_id = cpsl.product_id
	INNER JOIN catalog_product_entity cpe2 ON
		cpe2.entity_id = cpp.product_id
	WHERE
		cpe.type_id = 'configurable'
		AND ea.attribute_code = 'status'
	GROUP BY
		cpe.entity_id ,
		cpp.website_id ,
		cpp.customer_group_id ) AS q2 SET
	    q1.min_price = q2.min_price,
	    q1.final_price = q2.final_price,
		q1.max_price = q2.max_price
WHERE
	q1.website_id = q2.website_id
	AND q1.customer_group_id = q2.customer_group_id
	AND q1.entity_id = q2.entity_id"
        );
    }

    /**
     * @return AdapterInterface
     */
    public function getConnection()
    {
        if ($this->connection === null) {
            $this->connection = $this->resourceConnection->getConnection();
        }

        return $this->connection;
    }

    /**
     * @param WebsiteInterface|StoreInterface $scope
     * @return string
     * @throws LocalizedException
     */
    public function getProjectId($scope = null)
    {
        return $this->coreConfiguration->getProjectId($scope);
    }

    /**
     * @param WebsiteInterface|StoreInterface $scope
     * @return bool
     * @throws LocalizedException
     */
    public function hasProjectId($scope = null)
    {
        return $this->coreConfiguration->hasProjectId($scope);
    }

    /**
     * @return Configuration
     */
    public function getCoreConfiguration()
    {
        return $this->coreConfiguration;
    }

    /**
     * @param Product[] $products
     * @param StoreInterface $store
     * @return Product[]
     */
    public function hideOutOfStocks(array $products, StoreInterface $store): array
    {
        if ($this->scopeConfig->isSetFlag(
            \Magento\CatalogInventory\Model\Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
            ScopeInterface::SCOPE_STORE,
            $store
        )) {
            return $products;
        }

        foreach ($products as $product) {
            $inventory = $product->getInventory();

            if (!isset($inventory['in_stock']) || $inventory['in_stock']) {
                continue;
            }

            $product->setData(Product::VISIBLE, '0');
        }

        return $products;
    }

    public function getChangeLogIds($indexer): array
    {
        $viewsCollection = $this->viewCollectionFactory->create();

        /** @var ViewInterface $view */
        foreach ($viewsCollection as $view) {
            if (is_a($indexer, $view->getActionClass(), true)) {
                return $view->getChangelog()->getList($this->snapShooter->getVersionId($view->getId()), $view->getChangelog()->getVersion());
            }
        }
        return [];
    }

    /**
     * Merge bundle ids when necessary
     * @param array $ids
     * @return array
     */
    public function mergeWithProductParentIds(array $ids): array
    {
        if(empty($ids)){
            return $ids;
        }

        foreach ($this->bundleSelection->getParentIdsByChild($ids) as $parentId) {
            $ids[] = $parentId;
        }

        return $ids;
    }
}
