<?php

declare(strict_types=1);

namespace FiloBlu\Refilo\Setup;

use Exception;
use FiloBlu\Refilo\Helper\Data;
use FiloBlu\Refilo\Model\Importer\Translation;
use FiloBlu\Refilo\Remote\AbstractIndexer;
use FiloBlu\Refilo\Remote\IndexerConfiguration\IndexerConfigurationInterface;
use FiloBlu\Refilo\Remote\IndexerConfiguration\IndexerConfigurationInterfaceFactory;
use FiloBlu\Refilo\Remote\IndexerConfiguration\IndexerConfigurationRepository;
use FiloBlu\Refilo\Remote\IndexerConfiguration\IndexerResolver;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Storage\WriterInterface;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Psr\Log\LoggerInterface;

/**
 * Class RecurringData
 * @author Stefano Pallozzi <stefano.pallozzi@filoblu.com>
 * @package Vendor\Module\Setup
 */
class RecurringData implements InstallDataInterface
{
    /**
     * @var IndexerConfigurationRepository
     */
    private $indexerConfigurationRepository;
    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;
    /**
     * @var IndexerConfigurationInterfaceFactory
     */
    private $indexerConfigurationFactory;
    /**
     * @var IndexerResolver
     */
    private $indexerResolver;
    /**
     * @var Translation
     */
    private $translation;
    /**
     * @var array
     */
    private $defaultRemoteIndex;
    /**
     * @var Data
     */
    private $helperData;
    /**
     * @var LoggerInterface
     */
    private $logger;
    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;
    /**
     * @var WriterInterface
     */
    private $configWriter;


    /**
     * RecurringData constructor.
     * @param IndexerResolver $indexerResolver
     * @param IndexerConfigurationInterfaceFactory $indexerConfigurationFactory
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param IndexerConfigurationRepository $indexerConfigurationRepository
     * @param Translation $translation
     * @param Data $helperData
     * @param LoggerInterface $logger
     * @param ScopeConfigInterface $scopeConfig
     * @param WriterInterface $configWriter
     * @param array $defaultRemoteIndex
     */
    public function __construct(
        IndexerResolver                      $indexerResolver,
        IndexerConfigurationInterfaceFactory $indexerConfigurationFactory,
        SearchCriteriaBuilder                $searchCriteriaBuilder,
        IndexerConfigurationRepository       $indexerConfigurationRepository,
        Translation                          $translation,
        Data                                 $helperData,
        LoggerInterface                      $logger,
        ScopeConfigInterface                 $scopeConfig,
        WriterInterface                      $configWriter,
        array                                $defaultRemoteIndex
    )
    {
        $this->indexerConfigurationRepository = $indexerConfigurationRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->indexerConfigurationFactory = $indexerConfigurationFactory;
        $this->indexerResolver = $indexerResolver;
        $this->defaultRemoteIndex = $defaultRemoteIndex;
        $this->translation = $translation;
        $this->helperData = $helperData;
        $this->logger = $logger;
        $this->scopeConfig = $scopeConfig;
        $this->configWriter = $configWriter;
    }

    /**
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $this->updateIndexer();
        $this->updateCountryTranslations();
        $this->updateTrigger();
        $this->updateCronGroupConfig();
    }

    /**
     * @return void
     */
    public function updateIndexer()
    {
        $defaultRemoteIndexIds = array_keys($this->defaultRemoteIndex);

        $searchCriteria = $this->searchCriteriaBuilder->create();

        $result = $this->indexerConfigurationRepository->getList($searchCriteria);

        $indexerIds = array_unique(array_merge($this->indexerResolver->getIndexerIds(), $defaultRemoteIndexIds));
        $existing = [];

        /** @var IndexerConfigurationInterface $indexerConfiguration */
        foreach ($result->getItems() as $indexerConfiguration) {
            $existing[] = $indexerConfiguration->getIndexerId();
        }

        $indexerToCreate = array_diff($indexerIds, $existing);

        foreach ($indexerToCreate as $indexerId) {
            $indexerConfiguration = $this->indexerConfigurationFactory->create();
            $indexerConfiguration->setIndexerId($indexerId);
            $this->indexerConfigurationRepository->save($indexerConfiguration);
        }

        $result = $this->indexerConfigurationRepository->getList($searchCriteria);

        /** @var IndexerConfigurationInterface $indexerConfiguration */
        foreach ($result->getItems() as $indexerConfiguration) {
            if ($indexerConfiguration->getIndexName()) {
                continue;
            }

            if (!isset($this->defaultRemoteIndex[$indexerConfiguration->getIndexerId()])) {
                continue;
            }

            $indexerConfiguration->setIndexName($this->defaultRemoteIndex[$indexerConfiguration->getIndexerId()]);
            $this->indexerConfigurationRepository->save($indexerConfiguration);
        }
    }

    /**
     * @return void
     */
    public function updateCountryTranslations()
    {
        foreach ($this->helperData->getLocales() as $locale) {
            try {
                $this->translation->importFromFileOnly($locale);
            } catch (Exception $e) {
                $this->logger->critical($e->getMessage());
            }
        }
    }


    /**
     * Updates triggers for all indexers by calling the installation method
     * on each indexer's DeletedItemFinder instance.
     *
     * Iterates through the indexer collection, checks if the action class
     * implements IndexerInterface, retrieves the indexer from the registry,
     * and installs the deleted item trigger.
     *
     * @return void
     */
    public function updateTrigger(): void
    {
        $indexerPool = $this->indexerResolver->getIndexerPool();
        foreach ($indexerPool->getAll() as $id => $indexerClass) {

            if (!is_a($indexerClass, AbstractIndexer::class, true)) {
                continue;
            }

            /** @var AbstractIndexer $indexerInstance */
            $indexerInstance = $indexerPool->get($id);
            $indexerInstance->getDeletedItemFinder()->install();
        }
    }

    /**
     * Updates the cron group configuration to ensure that
     * the `use_separate_process` flag is set to '0' for all cron groups.
     * This enforces that cron jobs do not use separate processes.
     *
     * Iterates through all cron groups defined in the system configuration,
     * and updates the value if it is not already set to '0'.
     *
     * @return void
     */
    public function updateCronGroupConfig()
    {
        $cronGroups = $this->scopeConfig->getValue('system/cron');

        foreach ($cronGroups as $groupId => $cronGroup) {
            if (!isset($cronGroup['use_separate_process']) || $cronGroup['use_separate_process'] !== '0') {
                $this->configWriter->save('system/cron/' . $groupId . '/use_separate_process', '0');
            }
        }
    }
}
