<?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/)
 */



declare(strict_types=1);

namespace Mirasvit\CacheWarmer\Service;

use Magento\Framework\Stdlib\DateTime;
use Mirasvit\CacheWarmer\Api\Service\StatusServiceInterface;
use Mirasvit\CacheWarmer\Api\Service\PageServiceInterface;
use Mirasvit\CacheWarmer\Api\Repository\PageRepositoryInterface;
use Mirasvit\CacheWarmer\Api\Data\PageInterface;
use Mirasvit\CacheWarmer\Api\Repository\WarmRuleRepositoryInterface;
use Mirasvit\CacheWarmer\Api\Data\WarmRuleInterface;
use Mirasvit\CacheWarmer\Model\Config;
use Mirasvit\CacheWarmer\Logger\Logger;

class StatusService implements StatusServiceInterface
{
    /**
     * @var PageServiceInterface
     */
    private $pageService;

    /**
     * @var Config
     */
    private $config;

    /**
     * @var PageRepositoryInterface
     */
    private $pageRepository;

    /**
     * @var Logger
     */
    private $logger;
    /**
     * @var WarmRuleRepositoryInterface
     */
    private $ruleRepository;

    /**
     * StatusService constructor.
     * @param PageServiceInterface $pageService
     * @param PageRepositoryInterface $pageRepository
     * @param WarmRuleRepositoryInterface $ruleRepository
     * @param Config $config
     * @param Logger $logger
     */
    public function __construct(
        PageServiceInterface $pageService,
        PageRepositoryInterface $pageRepository,
        WarmRuleRepositoryInterface $ruleRepository,
        Config $config,
        Logger $logger
    ) {
        $this->pageService = $pageService;
        $this->pageRepository = $pageRepository;
        $this->ruleRepository = $ruleRepository;
        $this->config = $config;
        $this->logger = $logger;
    }

    /**
     * @return void
     */
    public function runFullStatusUpdate()
    {
        $collection = $this->pageRepository->getCollection();
        foreach ($collection as $page) {
            $this->pageService->isCached($page);
        }
        /** mp comment start */
        echo "Updated ".$collection->count()." pages\n";
        /** mp comment end */
    }

    /**
     * @return void
     */
    public function runPartialStatusUpdate(): void
    {
        $this->logger->info("Start cache status update");

        $ruleCollection = $this->ruleRepository->getCollection();
        $ruleCollection->setOrder(WarmRuleInterface::PRIORITY, 'DESC');

        $start     = time();
        $timelimit = $this->config->getJobRunThreshold() / 2;

        foreach ($ruleCollection as $rule) {
            while (time() - $start < $timelimit) {
                if ($this->runUpdate((int)$rule->getId()) === 0) {
                    break;
                }
            }
        }

        while (time() - $start < $timelimit) {
            if ($this->runUpdate(0) === 0) {
                break;
            }
        }

        $this->logger->info("Finish cache status update");
    }

    /**
     * @param int $ruleId
     * @return int
     */
    protected function runUpdate(int $ruleId): int
    {
        $ttl          = $this->config->getCacheTtl();
        $ttlThreshold = gmdate('Y-m-d H:i:s', time() - $ttl);

        $overdueCollection = $this->pageRepository->getCollection();
        $overdueCollection->getSelect()->where('status = ?', PageInterface::STATUS_CACHED);

        if ($ruleId > 0) {
            $overdueCollection->getSelect()->where('main_rule_id = ?', $ruleId);
        } else {
            $overdueCollection->getSelect()->where("warm_rule_ids = ''");
        }

        if ($ttl > 0) {
            $overdueCollection->getSelect()->where('cached_at < ?', $ttlThreshold);
            $overdueCollection->setOrder(PageInterface::CACHED_AT, 'ASC');
        } else {
            $overdueCollection->setOrder(PageInterface::UPDATED_AT, 'ASC');
        }

        $overdueCollection->getSelect()->limit(100);

        $checkedCnt = 0;
        foreach ($overdueCollection as $page) {
            $statusBefore = $page->getStatus();

            $this->pageService->isCached($page);

            // If the page is still cached (e.g. re-cached by an organic visit),
            // refresh cached_at so it won't be picked up as overdue again until the next TTL cycle.
            if ($ttl > 0 && $page->getStatus() === PageInterface::STATUS_CACHED && $statusBefore === PageInterface::STATUS_CACHED) {
                $page->setCachedAt(gmdate('Y-m-d H:i:s'));
                $this->pageRepository->save($page);
            }

            $checkedCnt++;
        }

        if ($ttl > 0 && $checkedCnt < 100) {
            $nonOverdueCollection = $this->pageRepository->getCollection();
            $nonOverdueCollection->getSelect()->where('status = ?', PageInterface::STATUS_CACHED);

            if ($ruleId > 0) {
                $nonOverdueCollection->getSelect()->where('main_rule_id = ?', $ruleId);
            } else {
                $nonOverdueCollection->getSelect()->where("warm_rule_ids = ''");
            }

            $nonOverdueCollection->getSelect()->where('cached_at >= ?', $ttlThreshold);
            $nonOverdueCollection->setOrder(PageInterface::UPDATED_AT, 'ASC');
            $nonOverdueCollection->getSelect()->limit(100 - $checkedCnt);

            foreach ($nonOverdueCollection as $page) {
                $this->pageService->isCached($page);
                $checkedCnt++;
            }
        }

        return $checkedCnt;
    }
}
