<?php

namespace FiloBlu\Refilo\Helper;

use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\UrlInterface;
use Magento\Quote\Api\Data\ShippingMethodInterface;
use Magento\SalesRule\Api\RuleRepositoryInterface;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Api\StoreConfigManagerInterface;
use Magento\Store\Model\ResourceModel\Store\CollectionFactory as StoreCollectionFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator;

/**
 * Class Data
 * @package FiloBlu\Refilo\Helper
 */
class Data extends AbstractHelper
{

    /** @var string */
    public const XML_USE_SECURE_IN_FRONTEND = 'web/secure/use_in_frontend';
    /** @var string */
    public const XML_REFILO_SALES_CHEAPEST_CARRIER_ENABLED = 'refilo_sales/cheapest_carrier/enabled';
    /** @var string  */
    public const XML_WEB_URL_REFILO_USE_STORE_IN_STOREFRONT = 'web/url/refilo_use_store_in_storefront';
    /** @var string  */
    public const XML_WEB_UNSECURE_REFILO_STOREFRONT_BASE_URL = 'web/unsecure/refilo_storefront_unsecure_base_url';
    /** @var string  */
    public const XML_WEB_SECURE_REFILO_STOREFRONT_BASE_URL = 'web/secure/refilo_storefront_secure_base_url';
    /** @var string  */
    public const XML_REFILO_TARGET_RULE_OVERRIDE_LIMIT_ENABLE = 'refilo_catalog/target_rule/override_limit_enabled';
    /** @var string  */
    public const XML_REFILO_TARGET_RULE_OVERRIDE_LIMIT = 'refilo_catalog/target_rule/override_limit';
    /** @var string  */
    public const XML_REFILO_TARGET_RULE_TRIGGER_BEEHIVE_ENABLE = 'refilo_catalog/target_rule/trigger_beehive_enabled';
    /** @var boolean  */
    public const XML_REFILO_EMAIL_TEMPLATE_ORDER_IMAGE_ENABLE = 'refilo_sales/order_email_template/enabled';
    /** @var string  */
    public const XML_REFILO_EMAIL_TEMPLATE_ORDER_IMAGE_SIZE = 'refilo_sales/order_email_template/size_product_image';
    /** @var string */
    public const XML_REFILO_CANONICAL_URL_PLUGIN_ENABLED = 'refilo_canonical_url/general/plugin_enabled';
    /** @var string */
    public const XML_REFILO_CANONICAL_URL_OBSERVER_ENABLED = 'refilo_canonical_url/general/observer_enabled';
    /** @var string  */
    public const XML_REFILO_CANONICAL_URL_OBSERVER_CONTENT_TYPES = 'refilo_canonical_url/general/observer_content_types';

    /**
     * @var StoreCollectionFactory
     */
    protected $storeCollectionFactory;
    /**
     * @var DataObject
     */
    protected $dataObject;
    /**
     * @var RuleRepositoryInterface
     */
    private $ruleRepository;
    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;
    /**
     * @var StoreConfigManagerInterface
     */
    private $storeConfigManager;

    /**
     * Data constructor.
     * @param Context $context
     * @param StoreCollectionFactory $storeCollectionFactory
     * @param RuleRepositoryInterface $ruleRepository
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param StoreManagerInterface $storeManager
     * @param DataObject $dataObject
     * @param StoreConfigManagerInterface $storeConfigManager
     */
    public function __construct(
        Context $context,
        StoreCollectionFactory $storeCollectionFactory,
        RuleRepositoryInterface $ruleRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        StoreManagerInterface $storeManager,
        DataObject $dataObject,
        StoreConfigManagerInterface $storeConfigManager
    )
    {
        parent::__construct($context);
        $this->ruleRepository = $ruleRepository;
        $this->storeCollectionFactory = $storeCollectionFactory;
        $this->dataObject = $dataObject;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->storeManager = $storeManager;
        $this->storeConfigManager = $storeConfigManager;
    }

    /**
     * @param $field
     * @return mixed
     */
    public function getConfigValue($field)
    {
        return $this->scopeConfig->getValue($field);
    }

    /**
     * @param $store
     * @return mixed
     */
    public function getStoreFrontBaseUrl($store)
    {
        $storeId = $store;

        if($store instanceof StoreInterface) {
            $storeId = $store->getId();
        }

        $useSecure = $this->scopeConfig->isSetFlag(self::XML_USE_SECURE_IN_FRONTEND,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );

        return $this->scopeConfig->getValue(
            $useSecure ? self::XML_WEB_SECURE_REFILO_STOREFRONT_BASE_URL : self::XML_WEB_UNSECURE_REFILO_STOREFRONT_BASE_URL,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    /**
     * @param int|StoreInterface $store
     * @return string
     * @throws NoSuchEntityException
     */
    public function getFrontendUrl($store)
    {
        $currentStore = $store;

        /*   list($storeConfig, $lookupConfigs) = $this->getStoreConfigs($storeId);
             if (count($lookupConfigs[$storeConfig[$storeId]['base_url']]) > 1) {
                 return $storeConfig[$storeId]['base_url'] . $storeConfig[$storeId]['code'] . '/';
             }
             return $storeConfig[$storeId]['base_url'];*/

        if(!$store instanceof StoreInterface) {
            $currentStore = $this->storeManager->getStore($store);
        }

        $useStore = $this->scopeConfig->getValue(
            self::XML_WEB_URL_REFILO_USE_STORE_IN_STOREFRONT,
            ScopeInterface::SCOPE_STORE,
            $currentStore->getId()
        );

        $baseUrl = $this->getStoreFrontBaseUrl($currentStore->getId());

        if(!$useStore) {
            return $baseUrl;
        }

        return implode('/', [trim($baseUrl ?? '', '/'), $currentStore->getCode()]) . '/';
    }

    /**
     * @return array[]
     */
    public function getStoreConfigs()
    {
        $storeConfigs = [];
        $lookupConfigs = [];
        $storeCollection = $this->storeCollectionFactory->create();

        $collection = $storeCollection->load();
        foreach ($collection as $item) {
            $config = $this->getStoreConfig($item);
            $url = ((bool)$config->getData('use_secure_url') == true) ? $config->getSecureBaseUrl() : $config->getBaseUrl();
            $storeConfigs[$config->getId()] = [
                'code'     => $config->getCode(),
                'base_url' => $url
            ];
            $lookupConfigs[$url][] = $config->getCode();
        }
        return [$storeConfigs, $lookupConfigs];
    }

    /**
     * @param Store $store
     * @return DataObject
     */
    public function getStoreConfig(Store $store)
    {
        $storeConfig = $this->dataObject;

        $storeConfig->setId($store->getId())
            ->setCode($store->getCode())
            ->setWebsiteId($store->getWebsiteId());

        $useSecure = $this->scopeConfig->isSetFlag(
            self::XML_USE_SECURE_IN_FRONTEND,
            ScopeInterface::SCOPE_STORES,
            $store->getCode()
        );

        $storeConfig->setData('use_secure_url', $useSecure);
        $storeConfig->setBaseUrl($store->getBaseUrl(UrlInterface::URL_TYPE_WEB, false));
        $storeConfig->setSecureBaseUrl($store->getBaseUrl(UrlInterface::URL_TYPE_WEB, true));
        $storeConfig->setBaseLinkUrl($store->getBaseUrl(UrlInterface::URL_TYPE_LINK, false));
        $storeConfig->setSecureBaseLinkUrl($store->getBaseUrl(UrlInterface::URL_TYPE_LINK, true));
        $storeConfig->setBaseStaticUrl($store->getBaseUrl(UrlInterface::URL_TYPE_STATIC, false));
        $storeConfig->setSecureBaseStaticUrl(
            $store->getBaseUrl(UrlInterface::URL_TYPE_STATIC, true)
        );
        $storeConfig->setBaseMediaUrl($store->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, false));
        $storeConfig->setSecureBaseMediaUrl(
            $store->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, true)
        );
        return $storeConfig;
    }

    /**
     * @return bool
     */
    public function isCheapestCarrierConfigEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(self::XML_REFILO_SALES_CHEAPEST_CARRIER_ENABLED);
    }

    /**
     * @param $quote
     * @return bool
     * @throws LocalizedException
     */
    public function hasFreeShipping($quote): bool
    {
        if (!$quote === null) {
            return false;
        }

        $quoteRules = explode(',', $quote->getAppliedRuleIds() ?? '');

        if (!$quoteRules) {
            return false;
        }

        $searchCriteria = $this->searchCriteriaBuilder->addFilter('rule_id', $quoteRules, 'in')->create();
        $salesRules = $this->ruleRepository->getList($searchCriteria);

        foreach ($salesRules->getItems() as $rule) {
            if ($rule->getSimpleFreeShipping()) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param ShippingMethodInterface[] $carriers
     * @param string[] $allowedCarrierMethods
     * @param bool $fromLowest
     * @return ShippingMethodInterface|null
     */
    public function getCarrier($carriers, $allowedCarrierMethods = [], $fromLowest = true) :? ShippingMethodInterface
    {
        $result = [];
        $filter = !empty($allowedCarrierMethods);
        foreach ($carriers as $carrier) {
            $methodCode = $carrier->getMethodCode();

            if ($filter && !\in_array($methodCode, $allowedCarrierMethods)) {
                continue;
            }

            $result[$methodCode] = $carrier;
        }

        uasort($result, static function ($a, $b) use ($fromLowest) {
            /** @var ShippingMethodInterface $a */
            /** @var ShippingMethodInterface $b */
            return $fromLowest ? $a->getAmount() - $b->getAmount() : $b->getAmount() - $a->getAmount();
        });

        return array_shift($result);
    }

    /**
     * @param $product
     * @param $storeId
     * @return string
     * TODO: Add parameters management by url builder
     * @throws NoSuchEntityException
     */
    public function getProductStorefrontUrl($product, $storeId)
    {
        $frontendUrl = $this->getFrontendUrl($storeId);
        $productUrlKey = $product->getUrlKey();
        $suffix = $this->scopeConfig->getValue(ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX);

        return $frontendUrl . rtrim($productUrlKey, '/') . $suffix;
    }

    /**
     * @return bool
     */
    public function isTargetRuleOverrideLimitEnabled() {
        return $this->scopeConfig->isSetFlag(self::XML_REFILO_TARGET_RULE_OVERRIDE_LIMIT_ENABLE);
    }

    /**
     * @return int
     */
    public function getTargetRuleOverrideLimit() {
        return (int)$this->scopeConfig->getValue(self::XML_REFILO_TARGET_RULE_OVERRIDE_LIMIT);
    }

    /**
     * @return bool
     */
    public function isTargetRuleTriggerBeehiveEnabled() {
        return $this->scopeConfig->isSetFlag(self::XML_REFILO_TARGET_RULE_TRIGGER_BEEHIVE_ENABLE);
    }

    /**
     * @return bool
     */
    public function getOrderEmailTemplateImage() {
        return $this->scopeConfig->getValue(self::XML_REFILO_EMAIL_TEMPLATE_ORDER_IMAGE_ENABLE);
    }

    /**
     * @return int
     */
    public function getOrderEmailTemplateImageSize() {
        return (int)$this->scopeConfig->getValue(self::XML_REFILO_EMAIL_TEMPLATE_ORDER_IMAGE_SIZE);
    }

    /**
     * @return array
     */
    public function getLocales()
    {
        $locales = [];

        foreach ($this->storeConfigManager->getStoreConfigs() as $storeConfig) {
            $locales[] = $storeConfig->getLocale();
        }

        return array_unique($locales);
    }

    /**
     * @return bool
     */
    public function isCanonicalUrlPluginEnabled()
    {
        return $this->scopeConfig->isSetFlag(self::XML_REFILO_CANONICAL_URL_PLUGIN_ENABLED);
    }

    /**
     * @return bool
     */
    public function isCanonicalUrlObserverEnabled()
    {
        return $this->scopeConfig->isSetFlag(self::XML_REFILO_CANONICAL_URL_OBSERVER_ENABLED);
    }

    public function getCanonicalUrlObserverContentTypes()
    {
        return $this->scopeConfig->getValue(self::XML_REFILO_CANONICAL_URL_OBSERVER_CONTENT_TYPES);
    }
}
