<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Remote\Entity\Provider;

use Exception;
use FiloBlu\Refilo\Helper\Catalog\ProductHelper;
use FiloBlu\Refilo\Remote\Entity\AbstractEntity;
use FiloBlu\Refilo\Remote\Entity\EntityProviderInterface;
use FiloBlu\Refilo\Remote\Entity\MagentoToDisabledRefiloProductAdapter;
use FiloBlu\Refilo\Remote\Entity\Product;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status as AttributeStatus;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Api\Data\WebsiteInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
 * Class DisabledProductProvider
 * @package FiloBlu\Refilo\Remote\Entity\Provider
 */
class DisabledProductProvider extends AbstractFromCollectionProvider
{
    /**
     * @var CollectionFactory
     */
    private $productCollectionFactory;

    /**
     * @var WebsiteInterface
     */
    private $website;

    /**
     * @var StoreInterface
     */
    private $store;

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

    /**
     * @var ProductHelper
     */
    private $productHelper;

    /**
     * @var Collection
     */
    private $productCollection;

    /**
     * @var MagentoToDisabledRefiloProductAdapter
     */
    private $productAdapter;

    /**
     * @param ScopeConfigInterface $scopeConfig
     * @param StoreManagerInterface $storeManager
     * @param ProductHelper $productHelper
     * @param MagentoToDisabledRefiloProductAdapter $magentoToDisabledRefiloProductAdapter
     * @param CollectionFactory $productCollectionFactory
     */
    public function __construct(
        ScopeConfigInterface                  $scopeConfig,
        StoreManagerInterface                 $storeManager,
        ProductHelper                         $productHelper,
        MagentoToDisabledRefiloProductAdapter $magentoToDisabledRefiloProductAdapter,
        CollectionFactory                     $productCollectionFactory
    ) {
        parent::__construct($scopeConfig);
        $this->productCollectionFactory = $productCollectionFactory;
        $this->storeManager = $storeManager;
        $this->productHelper = $productHelper;
        $this->productAdapter = $magentoToDisabledRefiloProductAdapter;
    }

    /**
     * @param array $ids
     * @return EntityProviderInterface
     */
    public function withIds(array $ids): EntityProviderInterface
    {
        if (empty($ids)) {
            return $this;
        }

        $this->getCollection()->addFieldToFilter('entity_id', ['in' => $ids]);
        return $this;
    }

    /**
     * @return Collection
     */
    public function getCollection()
    {
        if ($this->productCollection === null) {
            $this->productCollection = $this->productCollectionFactory->create()
                ->addAttributeToSelect('url_key')
                ->addAttributeToSelect('status')
                ->addAttributeToSelect('name');
        }

        return $this->productCollection;
    }

    /**
     * @param $items
     * @return array
     * @throws Exception
     */
    public function adapt($items)
    {
        // Pre-carica in UNA sola query l'appartenenza al website dei soli prodotti del batch
        // (abilitati), invece di caricare/consultare l'intera mappa website->prodotti.
        $toCheck = [];
        foreach ($items as $magentoProduct) {
            if ($this->website && (int)$magentoProduct->getStatus() !== AttributeStatus::STATUS_DISABLED) {
                $toCheck[] = (int)$magentoProduct->getEntityId();
            }
        }

        $inWebsite = ($this->website && $toCheck)
            ? $this->productHelper->getProductsInWebsite($toCheck, (int)$this->website->getId())
            : [];

        $output = [];
        /** @var ProductInterface $magentoProduct */
        foreach ($items as $magentoProduct) {
            $entityId = $magentoProduct->getEntityId();

            if ((int)$magentoProduct->getStatus() === AttributeStatus::STATUS_DISABLED) {
                $output[$entityId] = $this->productAdapter->adapt($magentoProduct, new Product());
                continue;
            }

            if (!$this->website) {
                continue;
            }

            if (!array_key_exists((int)$entityId, $inWebsite)) {
                $output[$entityId] = $this->productAdapter->adapt($magentoProduct, new Product());
                continue;
            }

            // Fallback for updated products
            $output[$entityId] = new Product([AbstractEntity::ID => (int)$entityId]);
        }

        return $output;
    }

    /**
     * Override dello streaming per pagina: passa l'intera pagina ad adapt() così la verifica
     * di appartenenza al website è una sola query per pagina (no N+1, memoria ∝ bulkSize).
     *
     * @return \Generator
     */
    public function toGenerator(): \Generator
    {
        $currentPage = 1;
        $pageSize = $this->getBulkSize();
        $collection = $this->getCollection()->clear()->setPageSize($pageSize)->setCurPage($currentPage);
        $lastPage = $collection->getLastPageNumber();

        while ($currentPage <= $lastPage) {
            $collection->setPageSize($pageSize);
            $collection->setCurPage($currentPage);

            foreach ($this->adapt($collection->getItems()) as $result) {
                yield $result;
            }

            $collection->clear();
            $currentPage++;
        }
    }

    /**
     * @param StoreInterface $store
     * @return EntityProviderInterface
     * @throws LocalizedException
     */
    public function forStore(StoreInterface $store): EntityProviderInterface
    {
        $this->store = $store;
        $this->website = $this->storeManager->getWebsite($store->getWebsiteId());
        return $this;
    }

    /**
     * @param WebsiteInterface $website
     * @return EntityProviderInterface
     */
    public function forWebsite(WebsiteInterface $website): EntityProviderInterface
    {
        $this->website = $website;
        return $this;
    }

    public function release(): EntityProviderInterface
    {
        $this->items = [];
        return $this;
    }
}
