<?php
namespace GriffinForms\Includes\Schedulers;

use GriffinForms\Traits\Singleton;

/**
 * Schedules and runs periodic cleanup for the BotLimiter rate-limit store.
 *
 * This removes stale limiter rows to keep the rate-limit backend lean.
 * By default we delete rows whose `updated_ts` is older than the window TTL
 * (with a safety multiplier). The TTL can be filtered via the filter
 * `griffinforms_ratelimiter_cleanup_cutoff` and/or made dynamic later
 * from settings.
 */
class RateLimitCleanup
{
    use Singleton;

    /** @var string Cron hook name */
    private $hook = 'griffinforms_ratelimiter_cleanup';

    /** @var string Rate-limit table name */
    protected $table;

    /** @var object|null Settings instance (optional) */
    protected $settings;

    /** @var object|null Logger instance (optional) */
    protected $log;

    /**
     * Bootstraps plugin hooks and services.
     */
    public function register()
    {
        // Register the cron task callback
        \add_action($this->hook, [$this, 'run']);

        // Ensure the task is scheduled (hourly)
        if (!\wp_next_scheduled($this->hook)) {
            $now = \time();
            // Fallback-safe random offset (avoid namespacing fatal if wp_rand not loaded yet)
            if (function_exists('wp_rand')) {
                $offset = \wp_rand(30, 300);
            } elseif (function_exists('random_int')) {
                $offset = \random_int(30, 300);
            } else {
                $offset = mt_rand(30, 300);
            }
            \wp_schedule_event($now + $offset, 'hourly', $this->hook);
        }
    }

    protected function setTable()
    {
        $this->table = \GriffinForms\Config::getInstance()->getTable('ratelimiter');
    }

    protected function setSettings()
    {
        $this->settings = \GriffinForms\Settings::getInstance();
    }

    protected function setLog()
    {
        $this->log = \GriffinForms\Log::getInstance();
    }

    /**
     * Cron callback: remove stale rows from the limiter store.
     */
    public function run(): void
    {
        global $wpdb;

        $cutoff_timestamp = $this->getCutoffTimestamp();
        // Build SQL safely: table name is trusted via Config; bind only the cutoff value
        $query_result = $wpdb->query($wpdb->prepare("DELETE FROM %i WHERE updated_ts < %d", $this->table, $cutoff_timestamp));

        if ($this->log) {
            $this->log->add(
                'info',
                'ratelimiter',
                0,
                \get_current_user_id(),
                sprintf('RateLimitCleanup removed %d stale rows (cutoff=%d)', (int) $wpdb->rows_affected, (int) $cutoff_timestamp),
                'ratelimiter'
            );
        }
    }

    /**
     * Determine a sensible cutoff. Default window TTL is 10 minutes; we multiply
     * by 3 as a safety buffer to retain some history.
     *
     * @return int UTC timestamp; rows older than this are purged.
     */
    private function getCutoffTimestamp(): int
    {
        // Defaults (align with BotLimiter defaults)
        $window_ttl = 600; // 10 minutes

        // If settings ever expose TTLs, prefer them
        if ($this->settings && method_exists($this->settings, 'getOption')) {
            $maybe = (int) $this->settings->getOption('rate_window_ttl');
            if ($maybe > 0) {
                $window_ttl = $maybe;
            }
        }

        $buffer_multiplier = 3; // keep some headroom/history
        $cutoff_timestamp = \time() - ($window_ttl * $buffer_multiplier);

        /**
         * Filter the cleanup cutoff timestamp.
         *
         * @param int $cutoff Unix timestamp to use as deletion cutoff.
         */
        $cutoff_timestamp = (int) \apply_filters('griffinforms_ratelimiter_cleanup_cutoff', $cutoff_timestamp);

        return $cutoff_timestamp;
    }
}