<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Remote;

use Exception;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Api\Data\StoreInterface;

/**
 * Class AbstractWebsiteBasedIndexer
 * @package FiloBlu\Refilo\Remote
 */
abstract class AbstractWebsiteBasedIndexer extends AbstractIndexer
{
    /** @var string */
    public const ARGUMENT_WEBSITE = 'website';

    /**
     * @param array $ids
     * @return void
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function executeAction(array $ids)
    {
        foreach ($this->getStoreManager()->getWebsites() as $website) {

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

            $provider = $this->entityProviderFactory->create()
                ->forWebsite($website)
                ->withIds($ids)
                ->prepare();

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

            $provider->release();
        }
    }

    /**
     * @param $entities
     * @param $size
     * @param DataObject $arguments
     * @throws NoSuchEntityException
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function onRead($entities, $size, DataObject $arguments)
    {
        $website = $arguments->getData(self::ARGUMENT_WEBSITE);
        $previousStore = $this->getStoreManager()->getStore();

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

        /** @var StoreInterface $store */
        foreach ($website->getStores() as $store) {
            $this->getStoreManager()->setCurrentStore($store);

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

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

            parent::onRead($entities, $size, $this->dataObjectFactory->create([
                'data' => [
                    self::ARGUMENT_COLLECTION => $collection
                ]
            ]));

        }

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

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

    /**
     * @param $provider
     * @param $arguments
     * @return void
     * @throws LocalizedException
     * @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);
        }
    }
}
