<?php
/**
 * Mirasvit
 *
 * This source file is subject to the Mirasvit Software License, which is available at https://mirasvit.com/license/.
 * Do not edit or add to this file if you wish to upgrade the to newer versions in the future.
 * If you wish to customize this module for your needs.
 * Please refer to http://www.magentocommerce.com for more information.
 *
 * @category  Mirasvit
 * @package   mirasvit/module-cache-warmer
 * @version   1.11.13
 * @copyright Copyright (C) 2026 Mirasvit (https://mirasvit.com/)
 */



namespace Mirasvit\CacheWarmer\Model;

use Magento\Config\Model\ResourceModel\Config as ConfigWriter;
use Magento\Framework\App\Cache\StateInterface as CacheStateInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Filesystem;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Framework\Module\ModuleListInterface;
use Magento\Framework\Module\Manager;
use Magento\Store\Model\ScopeInterface;
use Mirasvit\CacheWarmer\Api\Data\PageInterface;
use Mirasvit\CacheWarmer\Api\Service\WarmerServiceInterface;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\App\Cache\TypeListInterface;
use Psr\Log\LoggerInterface;

class Config
{
    const DATA_VERSION = 1;

    const PERFORMANCE_CUSTOM = 0;
    const PERFORMANCE_LOW    = 1;
    const PERFORMANCE_MEDIUM = 2;
    const PERFORMANCE_HIGH   = 3;

    private $scopeConfig;

    private $filesystem;

    private $cacheState;

    private $timezone;

    private $configWriter;

    private $serializer;

    private $cacheTypeList;

    private $request;

    private $moduleList;

    private $moduleManager;

    private $logger;

    public function __construct(
        ScopeConfigInterface $scopeConfig,
        Filesystem $filesystem,
        CacheStateInterface $cacheState,
        TimezoneInterface $timezone,
        ConfigWriter $configWriter,
        TypeListInterface $cacheTypeList,
        SerializerInterface $serializer,
        RequestInterface $request,
        ?ModuleListInterface $moduleList = null,
        ?Manager $moduleManager = null,
        ?LoggerInterface $logger = null
    ) {
        $this->scopeConfig   = $scopeConfig;
        $this->filesystem    = $filesystem;
        $this->cacheState    = $cacheState;
        $this->timezone      = $timezone;
        $this->configWriter  = $configWriter;
        $this->cacheTypeList = $cacheTypeList;
        $this->serializer    = $serializer;
        $this->request       = $request;
        $this->moduleList    = $moduleList ?: ObjectManager::getInstance()->get(ModuleListInterface::class);
        $this->moduleManager = $moduleManager ?: ObjectManager::getInstance()->get(Manager::class);
        $this->logger        = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class);
    }

    /**
     * @return bool
     */
    public function isEnabled()
    {
        return $this->scopeConfig->getValue('cache_warmer/general/enabled', ScopeInterface::SCOPE_STORE);
    }

    /**
     * @return bool
     */
    public function isPageCacheEnabled()
    {
        return $this->cacheState->isEnabled(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER);
    }

    /**
     * @return \DateTime
     */
    public function getDateTime()
    {
        return $this->timezone->date();
    }

    public function isWarmVariations()
    {
        return $this->scopeConfig->getValue(
            'cache_warmer/performance/is_warm_variations',
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function getWarmThreads()
    {
        switch ($this->getPerformanceLevel()) {
            case self::PERFORMANCE_HIGH:
                return 3;
            case self::PERFORMANCE_MEDIUM:
                return 2;
            case self::PERFORMANCE_LOW:
                return 1;
            default:
                $value = $this->scopeConfig->getValue(
                    'cache_warmer/performance/threads',
                    ScopeInterface::SCOPE_STORE
                );

                $value = (int)$value;

                return $value ? $value : 1;
        }
    }

    /**
     * @return int
     */
    public function getJobRunThreshold()
    {
        switch ($this->getPerformanceLevel()) { // we are using every 2 minutes cron for warming
            case self::PERFORMANCE_HIGH:
                return 115;
            case self::PERFORMANCE_MEDIUM:
                return 90;
            case self::PERFORMANCE_LOW:
                return 60;
            default:
                $warmTime = $this->scopeConfig->getValue(
                    'cache_warmer/performance/job_time',
                    ScopeInterface::SCOPE_STORE
                );

                return $warmTime ?: 100;
        }
    }

    /**
     * 100 - no limits
     * @return int
     */
    public function getServerLoadThreshold()
    {
        switch ($this->getPerformanceLevel()) {
            case self::PERFORMANCE_HIGH:
                return 100;
            case self::PERFORMANCE_MEDIUM:
                return 80;
            case self::PERFORMANCE_LOW:
                return 60;
            default:
                $value = (int)$this->scopeConfig->getValue(
                    'cache_warmer/performance/system_load_limit',
                    ScopeInterface::SCOPE_STORE
                );

                if (!$value) { // backward compatibility
                    $value = (int)$this->scopeConfig->getValue(
                        'cache_warmer/extended/system_load_limit',
                        ScopeInterface::SCOPE_STORE
                    );
                }

                return $value > 25 && $value < 100 ? $value : 100;
        }
    }

    /**
     * delay between cURL multirequests 10s max
     *
     * @return int
     */
    public function getDelay()
    {
        switch ($this->getPerformanceLevel()) {
            case self::PERFORMANCE_HIGH:
            case self::PERFORMANCE_MEDIUM:
                return 1;
            case self::PERFORMANCE_LOW:
                return 100;
            default:
                $value = $value = (int)$this->scopeConfig->getValue(
                    'cache_warmer/performance/delay',
                    ScopeInterface::SCOPE_STORE
                );

                if (!$value || $value < 1) {
                    $value = 1;
                } elseif ($value > 10000) {
                    $value = 10000;
                }

                return $value;
        }
    }

    /**
     * @return int
     */
    private function getPerformanceLevel()
    {
        return $this->scopeConfig->getValue('cache_warmer/performance/level');
    }

    /**
     * @param PageInterface $page
     *
     * @return bool
     */
    public function isIgnoredPage(PageInterface $page)
    {
        if (
            $this->isIgnoredUri($page->getUri())
            || $this->isIgnoredPageType($page->getPageType())
            || $this->isIgnoredUserAgent($page->getUserAgent())
        ) {
            return true;
        }

        if (in_array($page->getPageType(), ['cms_noroute_index', 'cms_noroute_index_*'])) {
            return true;
        }

        return false;
    }

    /**
     * @param string $uri
     *
     * @return bool
     */
    public function isIgnoredUri($uri)
    {
        foreach ($this->getIgnoredUriExpressions() as $expression) {
            if (preg_match($expression, $uri)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @return array
     */
    public function getIgnoredUriExpressions()
    {
        $expressions = [];
        $config      = $this->scopeConfig->getValue(
            'cache_warmer/general/ignored_uri_expressions',
            ScopeInterface::SCOPE_STORE
        );

        if ($this->isEmptySerialized($config)) {
            return $expressions;
        }

        try {
            $config = $this->serializer->unserialize((string)$config);
        } catch (\InvalidArgumentException $e) {
            $this->logger->warning('CacheWarmer: failed to unserialize ignored_uri_expressions config', [
                'error' => $e->getMessage(),
            ]);
            $config = [];
        }

        foreach ($config as $item) {
            if ($this->isValidExpression($item['expression'])) {
                $expressions[] = $item['expression'];
            }
        }

        return $expressions;
    }

    /**
     * @param string $userAgent
     *
     * @return bool
     */
    public function isIgnoredUserAgent($userAgent)
    {
        foreach ($this->getIgnoredUserAgents() as $expression) {
            if (preg_match($expression, $userAgent)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param string $pageType
     *
     * @return bool
     */
    public function isIgnoredPageType($pageType)
    {
        return in_array($pageType, $this->getIgnoredPageTypes());
    }

    /**
     * @return array
     */
    public function getIgnoredUserAgents()
    {
        $expressions = [];
        $config      = $this->scopeConfig->getValue(
            'cache_warmer/general/ignored_user_agents',
            ScopeInterface::SCOPE_STORE
        );

        if ($this->isEmptySerialized($config)) {
            return $expressions;
        }

        try {
            $config = $this->serializer->unserialize((string)$config);
        } catch (\InvalidArgumentException $e) {
            $this->logger->warning('CacheWarmer: failed to unserialize ignored_user_agents config', [
                'error' => $e->getMessage(),
            ]);
            $config = [];
        }

        foreach ($config as $item) {
            $item          = (array)$item;
            if ($this->isValidExpression($item['expression'])) {
                $expressions[] = $item['expression'];
            }
        }

        return $expressions;
    }

    /**
     * @return array
     */
    public function getIgnoredCookies()
    {
        $cookies = [];
        $config = $this->scopeConfig->getValue(
            'cache_warmer/general/ignored_cookies',
            ScopeInterface::SCOPE_STORE
        );

        if ($this->isEmptySerialized($config)) {
            return $cookies;
        }

        try {
            $config = $this->serializer->unserialize((string)$config);
        } catch (\InvalidArgumentException $e) {
            $this->logger->warning('CacheWarmer: failed to unserialize ignored_cookies config', [
                'error' => $e->getMessage(),
            ]);
            $config = [];
        }

        foreach ($config as $item) {
            $cookies[] = $item['expression'];
        }

        return $cookies;
    }

    /**
     * @return array
     */
    public function getIgnoredPageTypes()
    {
        $ignoredPageTypes = [];
        $config = (string)$this->scopeConfig->getValue(
            'cache_warmer/general/ignored_page_types',
            ScopeInterface::SCOPE_STORE
        );

        $ignoredPageTypes = explode(',', $config);

        return $ignoredPageTypes;
    }

    /**
     * @return string
     */
    public function getCacheType()
    {
        return $this->scopeConfig->getValue(\Magento\PageCache\Model\Config::XML_PAGECACHE_TYPE);
    }

    /**
     * @return int
     */
    public function getCacheTtl()
    {
        return $this->scopeConfig->getValue(\Magento\PageCache\Model\Config::XML_PAGECACHE_TTL);
    }

    /**
     * @return bool
     */
    public function isWarmRetryEnabled()
    {
        return (bool)$this->scopeConfig->getValue(
            'cache_warmer/performance/warm_retry_enabled',
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return string
     */
    public function getTmpPath()
    {
        $path = $this->filesystem->getDirectoryRead(DirectoryList::VAR_DIR)->getAbsolutePath();

        return $path;
    }

    /**
     * @return string
     */
    public function getWarmerUniquePart()
    {
        return $this->scopeConfig->getValue(
            WarmerServiceInterface::WARMER_UNIQUE_VALUE,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @param null $store
     * @return bool
     */
    public function isStoreCodeToUrlEnabled($store = null)
    {
        return $this->scopeConfig->getValue(
            \Magento\Store\Model\Store::XML_PATH_STORE_IN_URL,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
            $store
        );
    }

    /**
     * @return int
     */
    public function getCacheFillThreshold()
    {
        $value = $this->scopeConfig->getValue('cache_warmer/extended/crawler_limit', ScopeInterface::SCOPE_STORE);

        $value = (int)$value;

        return $value > 0 && $value < 100 ? $value : 100;
    }

    public function isUsePhpSessionCookie(): bool
    {
        return (bool)$this->scopeConfig->getValue(
            'cache_warmer/extended/is_use_phpsessid',
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return bool
     */
    public function isDebugToolbarEnabled()
    {
        return $this->scopeConfig->getValue('cache_warmer/debug/info', ScopeInterface::SCOPE_STORE)
            || $this->request->getParam('debug') === 'warmer'
            || strpos((string)$this->request->getParam('uri'), 'debug=warmer') !== false;
    }

    /**
     * @return string
     */
    public function getHttpAuthUsername()
    {
        $authName = $this->scopeConfig->getValue(
            'cache_warmer/http_auth/username',
            ScopeInterface::SCOPE_STORE
        );

        if (!$authName) {
            $authName = $this->scopeConfig->getValue(
                'cache_warmer/extended/http_auth/username',
                ScopeInterface::SCOPE_STORE
            );
        }

        return $authName;
    }

    /**
     * @return string
     */
    public function getHttpAuthPassword()
    {
        $authPassword = $this->scopeConfig->getValue(
            'cache_warmer/http_auth/password',
            ScopeInterface::SCOPE_STORE
        );

        if (!$authPassword) {
            $authPassword = $this->scopeConfig->getValue(
                'cache_warmer/extended/http_auth/password',
                ScopeInterface::SCOPE_STORE
            );
        }

        return $authPassword;
    }

    /**
     * @return int
     */
    public function isRequestLogEnabled()
    {
        return $this->scopeConfig->getValue(
            'cache_warmer/debug/request_log',
            ScopeInterface::SCOPE_STORE
        );
    }


    /**
     * @return int
     */
    public function isTagLogEnabled()
    {
        return $this->scopeConfig->getValue(
            'cache_warmer/debug/tag_log',
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function isBacktraceLogEnabled()
    {
        return $this->scopeConfig->getValue(
            'cache_warmer/debug/backtrace_log_main',
            ScopeInterface::SCOPE_STORE
        );
    }

    public function isBacktraceLogFileEnabled()
    {
        return $this->scopeConfig->getValue(
            'cache_warmer/debug/backtrace_log',
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function getDataVersion()
    {
        return (int)$this->scopeConfig->getValue(
            'cache_warmer/data_version'
        );
    }

    /**
     * @param int $version
     */
    public function setDataVersion($version)
    {
        $this->configWriter->saveConfig(
            'cache_warmer/data_version',
            $version,
            ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
            0
        );
        $this->cacheTypeList->cleanType("config");
    }

    /**
     * @param  string $expression
     * @return bool
     */
    private function isEmptySerialized($value): bool
    {
        return $value === null || $value === false || $value === '' || $value === '[]';
    }

    private function isValidExpression($expression)
    {
        set_error_handler(function () {
        }, E_WARNING);
        $isValidExpression = preg_match($expression, "") !== false;
        restore_error_handler();

        return $isValidExpression;
    }

    /**
     * @return int
     */
    public function getCleanupPagesState()
    {
        return (int)$this->scopeConfig->getValue('cache_warmer/cleanup_pages/state');
    }

    /**
     * @param int $state
     */
    public function setCleanupPagesState($state)
    {
        $oldState = $this->getCleanupPagesState();

        if ($oldState == $state) {
            return;
        }

        $this->configWriter->saveConfig(
            'cache_warmer/cleanup_pages/state',
            $state,
            ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
            0
        );
        $this->cacheTypeList->cleanType("config");
    }

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

    public function isFishPigFpcEnabled(): bool
    {
        return ($this->moduleList->has('FishPig_Bolt') || $this->moduleManager->isEnabled('FishPig_Bolt'))
            && $this->scopeConfig->getValue('bolt/settings/enabled');
    }

    public function isNitroPackCacheEnabled(): bool
    {
        return $this->moduleManager->isEnabled('NitroPack_NitroPack')
            && $this->getCacheType() == 3;
    }
}
