<?php

namespace FiloBlu\Refilo\Remote\Entity\Provider;

use FiloBlu\Refilo\Remote\Entity\EntityProviderInterface;
use FiloBlu\Refilo\Remote\Entity\MagentoToRefiloProductAdapter;
use FiloBlu\Refilo\Remote\Entity\MagentoToRefiloProductAdapterFactory;
use FiloBlu\Refilo\Remote\Entity\Product;
use LogicException;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\Data\ProductSearchResultsInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status as AttributeStatus;
use Magento\Catalog\Model\ProductRepository;
use Magento\Framework\Api\SearchCriteriaBuilderFactory;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SortOrder;
use Magento\Framework\Api\SortOrderBuilder;
use Magento\Framework\Api\SortOrderBuilderFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Api\Data\WebsiteInterface;

/**
 * Provider implements also a SPL iterator interface
 *  in foreach() the calling order<br>
 *  -> rewind<br>
 *  -> valid<br>
 *  -> current<br>
 *  -> key<br>
 * Class ProductProvider
 * @package FiloBlu\Refilo\Remote\Entity\Provider
 */
class ProductProvider extends BaseProvider
{

    /**
     * @var SearchCriteriaInterface
     */
    protected $searchCriteria;

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

    /**
     * @var false|float
     */
    protected $totalPages;

    /**
     * @var MagentoToRefiloProductAdapterFactory
     */
    private $productAdapterFactory;

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

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

    /**
     * @var SearchCriteriaBuilderFactory
     */
    private $searchCriteriaBuilderFactory;
    /**
     * @var \Magento\Framework\Api\SortOrderBuilderFactory
     */
    private $sortOrderBuilderFactory;

    /**
     * ProductProvider constructor.
     * @param MagentoToRefiloProductAdapterFactory $productAdapterFactory
     * @param SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory
     * @param \Magento\Framework\Api\SortOrderBuilderFactory $sortOrderBuilderFactory
     * @param ProductRepository $productRepository
     * @param ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        MagentoToRefiloProductAdapterFactory $productAdapterFactory,
        SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory,
        SortOrderBuilderFactory $sortOrderBuilderFactory,
        ProductRepository $productRepository,
        ScopeConfigInterface $scopeConfig
    ) {
        parent::__construct($scopeConfig);
        $this->productAdapterFactory = $productAdapterFactory;
        $this->productRepository = $productRepository;
        $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory;
        $this->sortOrderBuilderFactory = $sortOrderBuilderFactory;
    }

    /**
     * @param array $ids
     * @return \FiloBlu\Refilo\Remote\Entity\EntityProviderInterface
     */
    public function withIds(array $ids): EntityProviderInterface
    {
        $this->ids = $ids;
        return $this;
    }

    /**
     * @param StoreInterface $store
     * @return \FiloBlu\Refilo\Remote\Entity\EntityProviderInterface
     */
    public function forStore(StoreInterface $store): EntityProviderInterface
    {
        $this->store = $store;
        return $this;
    }

    /**
     * @param WebsiteInterface $website
     * @return EntityProviderInterface
     */
    public function forWebsite(WebsiteInterface $website): EntityProviderInterface
    {
        throw new LogicException('Count not filter by website');
    }

    /**
     * @return bool
     * @throws NoSuchEntityException
     */
    public function valid()
    {
        if ($this->searchCriteria->getCurrentPage() < $this->totalPages) {
            if (current($this->items) !== false) {
                return true;
            }

            $this->handleItems(
                $this->productRepository->getList(
                    $this->searchCriteria->setCurrentPage($this->searchCriteria->getCurrentPage() + 1)
                )
            );

            return true;
        }

        return current($this->items) !== false;
    }

    /**
     * @param ProductSearchResultsInterface $result
     * @throws NoSuchEntityException
     */
    public function handleItems(ProductSearchResultsInterface $result)
    {
        $this->items = [];

        /** @var ProductInterface $product */
        foreach ($result->getItems() as $product) {
            $this->items[(int)$product->getId()] = $this->productAdapter->adapt($product, new Product());
        }

        if (!$this->hasReadHandler()) {
            return;
        }

        $this->getReadHandler()->onRead($this->items, $this->getBulkSize(), $this->getReadHandlerArguments());
    }

    /**
     *
     * @throws NoSuchEntityException
     */
    #[\ReturnTypeWillChange]
    public function rewind()
    {
        $this->items = [];

        $this->productAdapter = $this->productAdapterFactory->create();

        $pageSize = $this->getBulkSize();
        /** @var SortOrderBuilder $sortOrderBuilder */
        $sortOrderBuilder = $this->sortOrderBuilderFactory->create();

        /** @var \Magento\Framework\Api\SearchCriteriaBuilder $builder */
        $builder = $this->searchCriteriaBuilderFactory->create();
        $builder->addFilter('status', AttributeStatus::STATUS_ENABLED);
        $builder->setSortOrders([
            $sortOrderBuilder->setField('updated_at')
                ->setDirection(SortOrder::SORT_DESC)
                ->create()
        ]);

        if (!empty($this->ids)) {
            $builder->addFilter('entity_id', $this->ids, 'in');
        }

        if ($this->store !== null) {
            $builder->addFilter('store_id', $this->store->getId());
        }

        $this->searchCriteria = $builder->create();
        $this->searchCriteria->setPageSize($pageSize)->setCurrentPage(1);
        $result = $this->productRepository->getList($this->searchCriteria);

        $this->totalPages = ceil($result->getTotalCount() / $pageSize);
        $this->handleItems($result);
    }
}
