<?php
/**
 * Copyright © 2015 FiloBlu . All rights reserved.
 */

namespace FiloBlu\Flow\Helper;

use Exception;
use FiloBlu\Flow\Api\Data\SpecialActionPoolInterface;
use FiloBlu\Flow\Model\Inboundflow;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Inventory\Model\ResourceModel\Source\CollectionFactory;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\ObjectManagerInterface;
use function sprintf;

/**
 * Class Data
 * @package FiloBlu\Flow\Helper
 */
class Data extends AbstractHelper
{
    /**
     * @var AdapterInterface
     */
    protected $_connection;

    /**
     * @var
     */
    protected $_storesByLocale;

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

    /**
     * @var SpecialActionPoolInterface
     */
    protected $specialActionPool;

    /**
     * @var ObjectManagerInterface
     */
    protected $objectManager;
    /**
     * @var Config
     */
    private $eavConfig;
    /**
     * @var MetadataPool
     */
    private $metadataPool;

    /**
     * Data constructor.
     * @param Context $context
     * @param ResourceConnection $_connection
     * @param StoreManagerInterface $storeManager
     * @param SpecialActionPoolInterface $specialActionPool
     * @param Config $eavConfig
     * @param MetadataPool $metadataPool
     * @param ObjectManagerInterface $objectManager
     */
    public function __construct(
        Context $context,
        ResourceConnection $_connection,
        StoreManagerInterface $storeManager,
        SpecialActionPoolInterface $specialActionPool,
        Config $eavConfig,
        MetadataPool $metadataPool,
        ObjectManagerInterface $objectManager
    ) {
        parent::__construct($context);
        $this->eavConfig = $eavConfig;
        $this->_connection = $_connection->getConnection();
        $this->storeManager = $storeManager;
        $this->specialActionPool = $specialActionPool;
        $this->objectManager = $objectManager;
        $this->metadataPool = $metadataPool;
    }

    /**
     * @return array
     */
    public function getModels()
    {
        $channels_in = (array)$this->scopeConfig->getValue('processor/channels/in');
        $channels_out = (array)$this->scopeConfig->getValue('processor/channels/out');

        $classes['select'] = '';

        foreach ($channels_in as $key => $channel) {
            $classes[$channel] = $key;
        }

        foreach ($channels_out as $key => $channel) {
            $classes[$channel] = $key;
        }

        return $classes;
    }

    /**
     * @param $channel_input
     * @return int|string|void
     */
    public function getChannelModels($channel_input)
    {
        $channels_in = $this->scopeConfig->getValue('processor/channels/in');

        foreach ($channels_in as $key => $channel) {
            if ($channel === $channel_input) {
                return $key;
            }
        }
    }

    /**
     * @param $idInboundflow
     * @return array
     */
    public function getChannelByInbound($idInboundflow)
    {
        if ($idInboundflow > 0) {
            $result = $this->_connection->fetchRow(
                'SELECT * FROM flow_inboundflow WHERE id = ? LIMIT 1',
                [$idInboundflow]
            );

            if (count($result) > 0) {
                return $result;
            }
        }

        return [];
    }

    /**
     * @param $text
     * @return string
     */
    public function removeTextSpaces($text)
    {
        $result = preg_replace('/\s/u', '', $text);
        $result = preg_replace('/( )+/u', '', $result);
        return trim($result);
    }

    public function getDefaultWebsiteId()
    {
        return $this->storeManager->getDefaultStoreView()->getWebsiteId();
    }

    /**
     * @return array
     */
    public function getAllStoreIds()
    {
        $storeIds = [];

        foreach ($this->storeManager->getStores($withDefault = true) as $store) {
            $storeIds[$store->getCode()] = (int)$store->getId();
        }

        return $storeIds;
    }

    /**
     * @param $id
     * @return StoreInterface
     * @throws NoSuchEntityException
     */
    public function getStoreFromId($id)
    {
        return $this->storeManager->getStore($id);
    }

    /**
     * @return array
     */
    public function getStoreInWebsiteId()
    {
        $storeIds = [];

        $listStores = $this->storeManager->getStores(true);
        $listWebsites = $this->storeManager->getWebsites(true);

        foreach ($listWebsites as $website) {
            foreach ($listStores as $store) {
                if ($website->getId() === $store->getWebsiteId()) {
                    $storeIds[$website->getCode()] = [
                        'website_id' => $website->getId(),
                        'store_id' => $store->getId()
                    ];
                    break;
                }
            }
        }

        return $storeIds;
    }

    /**
     * TODO : to be tested
     *
     * @return array
     */
    public function getAllStoresCodeByLocale()
    {
        $stores = $this->storeManager->getStores($withDefault = true);

        //Locale code
        $storesByLocale = [];

        //Try to get list of locale for all stores;
        foreach ($stores as $store) {
            $localeCode = $this->scopeConfig->getValue(
                'general/locale/code',
                ScopeInterface::SCOPE_STORE,
                $store->getId()
            );
            $localeCodeFirstPart = substr($localeCode, 0, 2);
            $localeCodeSecondPart = strtolower(substr($localeCode, 3, 2));
            $storeCode = $store->getCode();
            $storesByLocale[$localeCodeFirstPart][] = $storeCode;

            /* To cover more cases during the import, check if the first two letters of the ISO2 code are different from the last two;
               in this case I also add the latter as a language code. */
            if ($localeCodeFirstPart != $localeCodeSecondPart) {
                $storesByLocale[$localeCodeSecondPart][] = $storeCode;
            }
        }

        return $storesByLocale;
    }

    /**
     * @param $locale
     * @return array|mixed
     */
    public function getStoresCodeByLocale($locale)
    {
        if ($locale === '') {
            return [];
        }

        if (count($this->_storesByLocale) === 0) {
            $stores = $this->storeManager->getStores($withDefault = false);

            //Locale code
            $this->_storesByLocale = [];

            //Try to get list of locale for all stores;
            foreach ($stores as $store) {
                $localeCode = $this->scopeConfig->getValue(
                    'general/locale/code',
                    ScopeInterface::SCOPE_STORE,
                    $store->getId()
                );
                $localeCode = substr($localeCode, 0, 2);

                if (!isset($this->_storesByLocale[$localeCode])) {
                    $this->_storesByLocale[$localeCode] = [];
                }
                $storeCode = $store->getCode();
                $this->_storesByLocale[$localeCode][] = $storeCode;
            }
        }

        if (isset($this->_storesByLocale[$locale])) {
            return $this->_storesByLocale[$locale];
        }
        return [];
    }

    /**
     * @return mixed
     */
    public function getSkeleton()
    {
        return str_replace(
            ["\n", ' '],
            '',
            '{"field":{"from":"","to":"","type":"text","validators":[],"filters":[],"stores":"","locale":"","actions":[]} }'
        );
    }

    /**
     * @return array
     */
    public function getSpecialActionsPriorities()
    {
        return $this->specialActionPool->getActions();
    }

    /**
     * @return array
     */
    public function getListLanguageForAllStores()
    {
        //Locale code
        $allLocale = [];

        $stores = $this->storeManager->getStores($withDefault = false);
        //Try to get list of locale for all stores;
        foreach ($stores as $store) {
            //$locale[] = $this->scopeConfig->getValue('general/locale/code', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $store->getStoreId());
            $locale = $this->scopeConfig->getValue(
                'general/locale/code',
                ScopeInterface::SCOPE_STORE,
                $store->getId()
            );
            $langCode = explode('_', $locale);
            $lang = trim($langCode[0]);
            if (!isset($allLocale[$lang])) {
                $allLocale[$lang] = [];
            }
            $allLocale[$lang][] = $store->getId();
        }
        return $allLocale;
    }

    /**
     * @return array
     */
    public function getSources()
    {

        $sources = [];

        if ($this->_moduleManager->isEnabled('Magento_Inventory')) {

            $inventorySourceCollection = $this->objectManager->create(CollectionFactory::class);

            foreach ($inventorySourceCollection->create()->getItems() as $source) {
                $sourceCode = $source->getSourceCode();
                $sourceName = $source->getName();
                $sources[$sourceCode] = $sourceName;
            }
        } else {
            $sources['default'] = 'Default';
        }

        return $sources;
    }

    /**
     * @param $code
     * @return AbstractAttribute|null
     * @throws LocalizedException
     */
    public function getProductAttributeByCode($code)
    {
        return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $code);
    }

    /**
     * @return string
     * @throws Exception
     */
    public function getProductLinkField()
    {
        return $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
    }

    /**
     * @param Inboundflow $item
     * @param $status
     * @param bool $updateFlow
     * @param bool $resetOnlyErred
     * @return void
     * @throws Exception
     */
    public function resetItemToState(Inboundflow $item, $status, $updateFlow = true, $resetOnlyErred = false)
    {
        $mainTable = $this->objectManager
            ->create(sprintf('FiloBlu\Flow\Model\%s', $item->getFlow()))
            ->getResource()
            ->getMainTable();

        $connection = $this->_connection;
        $where = $connection->quoteInto('meta_file = ?', $item->getId());

        switch ($status) {
            case Inboundflow::STATUS_PARSED:
                $bind = [ 'meta_processed' => 0 ];
                if ($item->getFlow() === 'Imagesflow') {
                    $bind['image_status'] = $status;
                    $bind['image_process_time'] = null;
                } else {
                    $where = $connection->quoteInto('meta_file = ? AND meta_processed = 2', $item->getId());
                }

                $connection->update($mainTable, $bind, $where);
                break;
            case Inboundflow::STATUS_RECEIVED:
                if ($item->getFlow() === 'Imagesflow') {
                    $connection->update($mainTable, [
                        'image_status'       => $status,
                        'meta_processed'     => 0,
                        'image_hide'         => 0,
                        'image_type'         => null,
                        'image_alt'          => null,
                        'image_position'     => null,
                        'image_process_time' => null,
                        'log'                => null
                    ], $where);
                } else {
                    $connection->delete($mainTable, $where);
                }
                break;
            default:
        }

        if ($updateFlow) {
            $item->setStatus($status);
            $item->setRetry(0);
            $item->setLog('');
            $item->setComment('');
            $item->save();
        }
    }

    /**
     * @param $code
     * @return bool
     * @throws LocalizedException
     */
    public function websiteExist($code)
    {
        try {
            $this->storeManager->getWebsite($code);
            return true;
        } catch (\Magento\Framework\Exception\NoSuchEntityException $noSuchEntityException) {
            return false;
        }
    }

    public function getFlowModuleName($config, $channelName, $flowType = 'in')
    {
        $flowModuleName = 'Flow';
        if (isset($config['channels_config']->{$flowType}[$channelName]['config']['custom_module_model']) && strlen($config['channels_config']->{$flowType}[$channelName]['config']['custom_module_model']) > 0) {
            $flowModuleName = $config['channels_config']->{$flowType}[$channelName]['config']['custom_module_model'];
        }

        return $flowModuleName;
    }

    /**
     * Retrieve the configured custom sender email (ident_custom2).
     *
     * Returns the email address configured at `trans_email/ident_custom2/email`
     * for the current store scope.
     *
     * @return string|null Email address or null if not configured.
     */
    public function getCustomEmail2(): ?string
    {
        return $this->scopeConfig->getValue('trans_email/ident_custom2/email', ScopeInterface::SCOPE_STORE);
    }

    /**
     * Retrieve the configured custom sender name (ident_custom2).
     *
     * Returns the sender name configured at `trans_email/ident_custom2/name`
     * for the current store scope.
     *
     * @return string|null Email address or null if not configured.
     */
    public function getCustomName2(): ?string
    {
        return $this->scopeConfig->getValue('trans_email/ident_custom2/name', ScopeInterface::SCOPE_STORE);
    }


    /**
     * @param $module
     * @return bool
     */
    public function isModuleEnabled($module)
    {
        return $this->_moduleManager->isEnabled($module);
    }

    /**
     * @return ScopeConfigInterface
     */
    public function getScopeConfig()
    {
        return $this->scopeConfig;
    }

    /**
     * Get product entity id by SKU.
     *
     * @param string $sku
     * @return int|null Returns product entity id or null if not found.
     */
    public function getProductIdBySku($sku)
    {
        return $this->_connection->fetchOne(
            $this->_connection->select()->from('catalog_product_entity', 'entity_id')
                ->where('sku = ?', $sku));
    }
}
