<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Remote;

use Exception;
use FiloBlu\Refilo\Remote\IndexerConfiguration\IndexerConfigurationInterface;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Api\Data\StoreInterface;

/**
 * Class AbstractStoreBasedIndexer
 * @package FiloBlu\Refilo\Remote
 */
abstract class AbstractStoreBasedIndexer extends AbstractIndexer
{
    /**
     * @param array $ids
     * @return void
     * @throws NoSuchEntityException|LocalizedException
     */
    public function executeAction(array $ids)
    {
        // Profiler::start( __METHOD__);

        try {
            $indexTemplate = $this->getGetRemoteIndexTemplate();
        } catch (Exception $exception) {
            $this->logger->error($exception->getMessage(), ['exception' => $exception]);
            return;
        }

        $configuration = $this->getIndexerConfiguration();
        $previousStore = $this->getStoreManager()->getStore();

        foreach ($this->getStoreManager()->getStores() as $store) {

            if (!$store->getIsActive() || !$this->getIndexerHelper()->hasProjectId($store)) {
                continue;
            }

            // Profiler::start( 'process ' . $store->getCode());

            $website = $this->getStoreManager()->getWebsite($store->getWebsiteId());

            $collection = $indexTemplate
                ->withValue('%s', $store->getCode())
                ->withValue('%p', $this->getIndexerHelper()->getProjectId($website))
                ->render();

            $this->getStoreManager()->setCurrentStore($store);

            if ($configuration->getAllowDelete() && $configuration->getDeleteOrder() === IndexerConfigurationInterface::DELETE_ORDER_BEFORE_INDEXER) {
                $this->getConnector()->delete($this->getEntitiesToRemove($ids), $collection);
            }

            $arguments = $this->dataObjectFactory->create([
                'data' => [
                    self::ARGUMENT_COLLECTION => $collection
                ]
            ]);

            $provider = $this->entityProviderFactory->create()
                ->forStore($store)
                ->withIds($ids)
                ->prepare();

            $this->walk($provider, $arguments);

            $provider->release();
            
            if ($configuration->getAllowDelete() && $configuration->getDeleteOrder() === IndexerConfigurationInterface::DELETE_ORDER_AFTER_INDEXER) {
                $this->getConnector()->delete($this->getEntitiesToRemove($ids), $collection);
            }

            // Profiler::stop( 'process ' . $store->getCode());
        }

        $this->getStoreManager()->setCurrentStore($previousStore);

        // Profiler::stop( __METHOD__);
    }

    /**
     * @param StoreInterface $store
     * @return DataObject
     * @throws LocalizedException
     */
    public function prepareConnectorMetadata(StoreInterface $store): DataObject
    {
        $metadata = parent::prepareConnectorMetadata($store);
        $metadata->setData('database', $this->getIndexerHelper()->getProjectId($store));
        return $metadata;
    }

    /**
     * @param $provider
     * @param $arguments
     * @return void
     * @throws NoSuchEntityException
     */
    public function walk($provider, $arguments)
    {
        if (!$this->getIndexerConfiguration()->getEnabledMemoryOptimization()) {
            iterator_to_array(
                $provider->attachReaderHandler($this, $arguments)
            );

            return;
        }

        $buffer = [];
        $bulkSize = $this->getIndexerHelper()->getIndexerBulkSize();
        foreach ($provider->toGenerator() as $entity) {
            if ($entity === null) {
                continue;
            }

            $buffer[] = $entity;
            if (count($buffer) % $bulkSize === 0) {
                $this->onRead($buffer, count($buffer), $arguments);
                $buffer = [];
            }
        }

        if (count($buffer) > 0 && count($buffer) < $bulkSize) {
            $this->onRead($buffer, count($buffer), $arguments);
        }
    }
}
