<?php

declare(strict_types=1);

namespace Akeneo\Connector\Helper;

use Akeneo\Connector\Helper\Config as ConfigHelper;
use Exception;
use Magento\Directory\Helper\Data as DirectoryHelper;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DB\Select;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Locale\Resolver;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Store\Model\ResourceModel\Website as WebsiteResource;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
 * @author    Agence Dn'D <contact@dnd.fr>
 * @copyright 2004-present Agence Dn'D
 * @license   https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://www.dnd.fr/
 */
class Store
{
    /**
     * This variable contains a ConfigHelper
     *
     * @var ConfigHelper $configHelper
     */
    protected $configHelper;
    /**
     * This variable contains a StoreManagerInterface
     *
     * @var StoreManagerInterface $storeManager
     */
    protected $storeManager;
    /**
     * This variable contains a Json
     *
     * @var Json $jsonSerializer
     */
    protected $jsonSerializer;
    /**
     * Website Resource Model
     *
     * @var WebsiteResource $websiteResource
     */
    protected $websiteResource;
    /**
     * Description $scopeConfig field
     *
     * @var ScopeConfigInterface $scopeConfig
     */
    protected $scopeConfig;

    /**
     * Store constructor
     *
     * @param ConfigHelper          $configHelper
     * @param Json                  $jsonSerializer
     * @param StoreManagerInterface $storeManager
     * @param WebsiteResource       $websiteResource
     * @param ScopeConfigInterface  $scopeConfig
     */
    public function __construct(
        ConfigHelper $configHelper,
        Json $jsonSerializer,
        StoreManagerInterface $storeManager,
        WebsiteResource $websiteResource,
        ScopeConfigInterface $scopeConfig
    ) {
        $this->configHelper    = $configHelper;
        $this->jsonSerializer  = $jsonSerializer;
        $this->storeManager    = $storeManager;
        $this->websiteResource = $websiteResource;
        $this->scopeConfig     = $scopeConfig;
    }

    /**
     * Retrieve all stores information
     *
     * @param string|string[] $arrayKey
     * @param bool $ignoreSingleStore
     *
     * @return mixed[]
     */
    public function getStores($arrayKey = 'store_id', bool $ignoreSingleStore = false)
    {
        if (!is_array($arrayKey)) {
            $arrayKey = [$arrayKey];
        }

        /** @var mixed[] $data */
        $data = [];
        /** @var string[] $websiteDefaultStores */
        $websiteDefaultStores = $this->getWebsiteDefaultStores(true);
        /** @var mixed[] $mapping */
        $mapping = $this->configHelper->getWebsiteMapping();
        /** @var string[] $match */
        foreach ($mapping as $match) {
            if (empty($match['channel']) || empty($match['website'])) {
                continue;
            }
            $channel = $match['channel'];
            $websiteCode = $match['website'];
            $website = $this->storeManager->getWebsite($websiteCode);
            $websiteId = $website->getId();
            if ($websiteId === null) {
                continue;
            }

            if (!$ignoreSingleStore && $this->isSingleStoreMode() && (int)$websiteId !== 0) {
                continue;
            }

            /** @var string $currency */
            $currency = $website->getBaseCurrencyCode();
            /** @var string[] $siblings */
            $siblings = $website->getStoreIds();

            $stores = $website->getStores();
            foreach ($stores as $store) {
                /** @var int $storeId */
                $storeId = $store->getId();
                /** @var string $storeCode */
                $storeCode = $store->getCode();
                /** @var string $storeLang */
                $storeLang = $this->scopeConfig->getValue(
                    DirectoryHelper::XML_PATH_DEFAULT_LOCALE,
                    ScopeInterface::SCOPE_STORE,
                    $storeId
                );
                /** @var bool $isDefault */
                $isDefault = false;
                if (in_array($storeId, $websiteDefaultStores)) {
                    $isDefault = true;
                }

                /** @var mixed[] $combine */
                $combine = [];
                /** @var string $key */
                foreach ($arrayKey as $key) {
                    switch ($key) {
                        case 'store_id':
                            $combine[] = $storeId;
                            break;
                        case 'store_code':
                            $combine[] = $storeCode;
                            break;
                        case 'website_id':
                            $combine[] = $websiteId;
                            break;
                        case 'website_code':
                            $combine[] = $websiteCode;
                            break;
                        case 'channel_code':
                            $combine[] = $channel;
                            break;
                        case 'lang':
                            $combine[] = $storeLang;
                            break;
                        case 'currency':
                            $combine[] = $currency;
                            break;
                        default:
                            $combine[] = $storeId;
                            break;
                    }
                }

                /** @var string $key */
                $key = implode('-', $combine);

                if (!isset($data[$key])) {
                    $data[$key] = [];
                }

                $data[$key][] = [
                    'store_id'           => $storeId,
                    'store_code'         => $storeCode,
                    'is_website_default' => $isDefault,
                    'siblings'           => $siblings,
                    'website_id'         => $websiteId,
                    'website_code'       => $websiteCode,
                    'channel_code'       => $channel,
                    'lang'               => $storeLang,
                    'currency'           => $currency,
                ];
            }
        }

        return $data;
    }

    /**
     * @return bool
     */
    public function isSingleStoreMode(): bool
    {
        return $this->storeManager->isSingleStoreMode();
    }

    /**
     * Retrieve all store combination
     *
     * @return mixed[]
     * @throws LocalizedException
     */
    public function getAllStores()
    {
        /** @var mixed[] $stores */
        $stores = array_merge(
            $this->getStores(['lang']), // en_US
            $this->getStores(['lang', 'channel_code']), // en_US-channel
            $this->getStores(['channel_code']), // channel
            $this->getStores(['currency']), // USD
            $this->getStores(['channel_code', 'currency']), // channel-USD
            $this->getStores(['lang', 'currency']), // en_US-USD
            $this->getStores(['lang', 'channel_code', 'currency']) // en_US-channel-USD
        );

        return $stores;
    }

    /**
     * Retrieve all stores from website/channel mapping
     *
     * @return mixed[]
     * @throws Exception
     */
    public function getMappedWebsitesStores()
    {
        /** @var mixed[] $stores */
        $stores = $this->getStores('website_code');

        return $stores;
    }

    /**
     * Retrieve needed store ids from website/channel mapping
     *
     * @return string[]
     * @throws Exception
     */
    public function getMappedWebsitesStoreIds()
    {
        /** @var mixed[] $websites */
        $websites = $this->getStores('website_code');
        /** @var string[] $storeIds */
        $storeIds = [];
        /** @var mixed[] $website */
        foreach ($websites as $website) {
            /** @var string[] $websiteStoreIds */
            $websiteStoreIds = array_column($website, 'store_id');
            $storeIds        = array_merge($storeIds, array_diff($websiteStoreIds, $storeIds));
        }

        return $storeIds;
    }

    /**
     * Retrieve needed store languages from website/channel mapping
     *
     * @return string[]
     * @throws Exception
     */
    public function getMappedWebsitesStoreLangs()
    {
        /** @var mixed[] $websites */
        $websites = $this->getStores('website_code');
        /** @var string[] $langs */
        $langs = [];
        /** @var mixed[] $website */
        foreach ($websites as $website) {
            /** @var string[] $websiteStoreIds */
            $websiteStoreIds = array_column($website, 'lang');
            $langs           = array_merge($langs, array_diff($websiteStoreIds, $langs));
        }

        return $langs;
    }

    /**
     * Get given channel Magento languages
     *
     * @param string $channel
     *
     * @return string[]
     * @throws Exception
     */
    public function getChannelStoreLangs($channel)
    {
        /** @var string[] $langs */
        $langs = [];

        if (empty($channel) || !in_array($channel, $this->configHelper->getMappedChannels())) {
            return $langs;
        }

        /** @var mixed[] $stores */
        $stores = $this->getStores('channel_code');
        if (empty($stores)) {
            return $langs;
        }

        /** @var mixed[] $channelStores */
        $channelStores = $stores[$channel];
        if (empty($channelStores)) {
            return $langs;
        }

        $langs = array_column($channelStores, 'lang', 'lang');

        return $langs;
    }

    /**
     * Get websites default stores
     *
     * @param bool $withAdmin
     *
     * @return string[]
     */
    public function getWebsiteDefaultStores($withAdmin = false)
    {
        /** @var WebsiteResource $websiteResource */
        $websiteResource = $this->websiteResource;
        /** @var Select $select */
        $select = $websiteResource->getDefaultStoresSelect($withAdmin);
        /** @var string[] $websiteDefaultStores */
        $websiteDefaultStores = $websiteResource->getConnection()->fetchPairs($select);

        return $websiteDefaultStores;
    }

    /**
     * Retrieve admin store lang setting
     *
     * Default: return Mage_Core_Model_Locale::DEFAULT_LOCALE
     *
     * @return string
     */
    public function getAdminLang()
    {
        /** @var string $adminLang */
        $adminLang = Resolver::DEFAULT_LOCALE;

        if ($this->scopeConfig->isSetFlag(DirectoryHelper::XML_PATH_DEFAULT_LOCALE)) {
            $adminLang = $this->scopeConfig->getValue(DirectoryHelper::XML_PATH_DEFAULT_LOCALE);
        }

        return $adminLang;
    }
}
