<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Model\Exporter;

use FiloBlu\Core\Model\Configuration;
use FiloBlu\Refilo\Model\Adapter\Mongo;
use FiloBlu\Refilo\Remote\Entity\EntityProviderInterface;
use FiloBlu\Refilo\Remote\Entity\EntityProviderReaderHandlerInderface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObject;
use Magento\Framework\DataObjectFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

/**
 * Class Urls
 * @package FiloBlu\Refilo\Model\Exporter
 */
class Urls extends AbstractExporter implements EntityProviderReaderHandlerInderface
{
    /**
     * @var EntityProviderInterface[]
     */
    private $urlProviders;

    /**
     * @var DataObjectFactory
     */
    private $dataObjectFactory;

    /**
     * @var LoggerInterface
     */
    private $logger;

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

    /**
     * @var array
     */
    private $dropStatus = [];

    /**
     * Urls constructor.
     * @param ScopeConfigInterface $scopeConfig
     * @param Mongo $mongo
     * @param Configuration $coreConfiguration
     * @param DataObjectFactory $dataObjectFactory
     * @param LoggerInterface $logger
     * @param StoreManagerInterface $storeManager
     * @param EntityProviderInterface[] $urlProviders
     */
    public function __construct(
        ScopeConfigInterface  $scopeConfig,
        Mongo                 $mongo,
        Configuration         $coreConfiguration,
        DataObjectFactory     $dataObjectFactory,
        LoggerInterface       $logger,
        StoreManagerInterface $storeManager,
        array                 $urlProviders
    )
    {
        parent::__construct($mongo, $coreConfiguration, $scopeConfig);
        $this->urlProviders = $urlProviders;
        $this->dataObjectFactory = $dataObjectFactory;
        $this->logger = $logger;
        $this->storeManager = $storeManager;
    }

    public function preExecute(OutputInterface $output = null)
    {
        // Avoid dropping urls
    }

    /**
     * @return iterable
     */
    public function getCollections(): iterable
    {
        yield 'urls';
    }

    /**
     * @param OutputInterface|null $output
     * @throws NoSuchEntityException
     * @throws LocalizedException
     */
    public function execute(OutputInterface $output = null)
    {
        foreach ($this->storeManager->getStores() as $store) {
            try {
                $this->getCoreConfiguration()->getProjectId($store);
            } catch (\Exception $exception) {
                $this->logger->error($exception->getMessage());
                continue;
            }
            $this->exportStoreUrls($store, $output);
        }
    }

    /**
     * @param StoreInterface $store
     * @param OutputInterface|null $output
     * @throws NoSuchEntityException
     * @throws LocalizedException
     */
    public function exportStoreUrls(StoreInterface $store, OutputInterface $output = null)
    {
        $projectId = $this->getCoreConfiguration()->getProjectId($store);

        foreach ($this->urlProviders as $urlProvider) {

            $arguments = $this->dataObjectFactory->create([
                'data' => [
                    'project_id' => $projectId,
                    'output'     => $output,
                    'store'      => $store
                ]]);

            $urlProvider->forStore($store)->attachReaderHandler($this, $arguments);
            iterator_to_array($urlProvider);
            $urlProvider->detachReaderHandler();
        }
    }

    /**
     * @param $block
     * @param $size
     * @param DataObject $arguments
     * @return void
     */
    public function onRead($block, $size, DataObject $arguments)
    {
        if (empty($block)) {
            return;
        }

        /** @var OutputInterface $output */
        $output = $arguments->getOutput();
        $projectId = $arguments->getProjectId();
        /** @var StoreInterface $store */
        $store = $arguments->getStore();
        $storeCode = $store->getCode();
        $type = $block[0]->getType();

        if (!isset($this->dropStatus[$storeCode][$type])) {

            if (!isset($this->dropStatus[$storeCode])) {
                $this->dropStatus[$storeCode] = [];
            }

            $this->dropStatus[$storeCode][$type] = true;
            $this->getConnection()->deleteDocumentsByProperties($projectId, 'urls', [
                'store' => $store->getCode(),
                'type'  => $type
            ]);
        }

        try {
            $this->getConnection()->update('_id', $block, 'urls', true, $projectId);
            $this->getConnection()->ensureIndex(['url'], 'urls', $projectId);
        } catch (Throwable $throwable) {
            $this->logger->error($throwable->getMessage(), ['exception' => $throwable]);
            if ($output) {
                $output->writeln($throwable->getMessage());
            }
        }
    }
}
