<?php

namespace FiloBlu\Refilo\Model\Exporter;

use FiloBlu\Core\Model\Configuration;
use FiloBlu\Refilo\Model\Adapter\Mongo;
use FiloBlu\Refilo\Model\Config\ConfigProviderInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObjectFactory;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use MongoDB\Driver\Exception\Exception;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class Config
 * @package FiloBlu\Refilo\Model\Exporter
 */
class Config extends AbstractExporter
{
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var array
     */
    private $providers;

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

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

    public function preExecute(OutputInterface $output = null)
    {
        // No Drops for configs
    }

    public function getCollections(): iterable
    {
        yield 'configs';
    }

    /**
     *
     */
    public function execute(OutputInterface $output = null)
    {
        $isMultiDomain = $this->coreConfiguration->isMultiDomain();

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

        $projectIds = [];
        $defaults = [];

        foreach ($this->coreConfiguration->getWebsiteMapping() as $map) {
            $storeCode = $map->getDefaultStore()->getCode();
            $projectIds[$map->getProjectId()] = $storeCode;
            $defaults[$storeCode] = [];
        }

        $stores = $this->storeManager->getStores(false, true);

        foreach ($stores as $code => $store) {

            $bulkData = [];
            $configurations = $this->scopeConfig->getValue(null, ScopeInterface::SCOPE_STORE, $code);
            $processed = $this->process($configurations, $store);

            if (array_key_exists($code, $defaults)) {
                $defaults[$code] = $processed;
                $defaults[$code]['metacode'] = 'default';
                $defaults[$code]['_id'] = 'default_config';
            }

            $bulkData[] = $processed;
            try {
                $this->getConnection()->update(
                    '_id',
                    $bulkData,
                    'configs',
                    true,
                    $this->getCoreConfiguration()->getProjectId($store->getWebsite())
                );
            } catch (\Exception $e) {
                echo $e->getMessage();
            }
        }

        foreach ($projectIds as $projectId => $store) {
            try {

                $this->getConnection()->update(
                    '_id',
                    [$defaults[$store]],
                    'configs',
                    true,
                    $projectId
                );

            } catch (\Exception $e) {
                echo $e->getMessage();
            }
        }
    }

    public function executeSingleDomain()
    {
        $stores = $this->storeManager->getStores(false, true);
        $bulkData = [];

        foreach ($stores as $code => $store) {
            $configurations = $this->scopeConfig->getValue(null, ScopeInterface::SCOPE_STORE, $code);
            $bulkData[] = $this->process($configurations, $store);
        }

        $configurations = $this->scopeConfig->getValue(null);
        $bulkData[] = $this->process($configurations);

        try {
            $this->getConnection()->update(
                '_id',
                $bulkData,
                'configs',
                true,
                $this->getDatabase()
            );

        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }

    /**
     * @param array $configurations
     * @param StoreInterface| null $store
     * @return array
     */
    protected function process(array $configurations, $store = null)
    {
        $data = [];
        /** @var ConfigProviderInterface $provider */
        foreach ($this->providers as $provider) {
            $result = $provider->getConfigs($this->dataObjectFactory->create(['data' => $configurations]), $store);
            $data = array_merge_recursive($data, $result);
        }

        return $this->encodeData($data, $store ? $store->getCode() : null);
    }

    /**
     * @param array $data
     * @param string|null $storeCode
     * @return array
     */
    protected function encodeData(array $data, $storeCode = null)
    {
        return [
            '_id'      => $storeCode ? "country_{$storeCode}" : 'default_config',
            'metacode' => $storeCode ? 'store' : 'default',
            'metadata' => $data
        ];
    }
}
