<?php

namespace WPSN\Core\Queue;

use WPSN\Core\Logger;
use WPSN\Core\Settings;

defined('ABSPATH') || exit;

/**
 * Queue maintenance helpers.
 *
 * Goals:
 * - Keep the queue table small on large sites.
 * - Avoid expensive full-table scans on every request.
 */
final class QueueMaintenance
{
    private const GC_LOCK_TRANSIENT = 'wpsn_queue_gc_lock';

    /**
     * Best-effort garbage collection for old queue rows.
     *
     * - Deletes DONE jobs older than keep_done_days
     * - Deletes FAILED jobs older than keep_failed_days
     *
     * Throttled via transient so it runs at most once per gc_interval_seconds.
     */
    public static function maybeGarbageCollect(Settings $settings, Logger $logger): void
    {
        $enabled = (bool) $settings->get('performance.queue.gc.enabled', true);
        if (!$enabled) {
            return;
        }

        $interval = (int) $settings->get('performance.queue.gc.interval_seconds', DAY_IN_SECONDS);
        if ($interval < HOUR_IN_SECONDS) {
            $interval = HOUR_IN_SECONDS;
        }

        // Throttle across requests.
        if (get_transient(self::GC_LOCK_TRANSIENT)) {
            return;
        }
        set_transient(self::GC_LOCK_TRANSIENT, '1', $interval);

        $keepDoneDays = (int) $settings->get('performance.queue.gc.keep_done_days', 7);
        $keepFailedDays = (int) $settings->get('performance.queue.gc.keep_failed_days', 30);

        $keepDoneDays = max(1, min(90, $keepDoneDays));
        $keepFailedDays = max(1, min(365, $keepFailedDays));

        $limit = (int) $settings->get('performance.queue.gc.limit', 5000);
        $limit = max(100, min(50000, $limit));

        global $wpdb;
        $table = $wpdb->prefix . 'wpsn_queue_jobs';

        $doneBefore = gmdate('Y-m-d H:i:s', time() - ($keepDoneDays * DAY_IN_SECONDS));
        $failedBefore = gmdate('Y-m-d H:i:s', time() - ($keepFailedDays * DAY_IN_SECONDS));

        try {
            // DONE
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
            $deletedDone = (int) $wpdb->query(
                $wpdb->prepare(
                    // LIMIT is injected as an int to avoid placeholder issues.
                    "DELETE FROM {$table} WHERE status = %s AND updated_at < %s LIMIT {$limit}",
                    'done',
                    $doneBefore
                )
            );

            // FAILED
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
            $deletedFailed = (int) $wpdb->query(
                $wpdb->prepare(
                    "DELETE FROM {$table} WHERE status = %s AND updated_at < %s LIMIT {$limit}",
                    'failed',
                    $failedBefore
                )
            );

            if ($deletedDone > 0 || $deletedFailed > 0) {
                $logger->info(sprintf(
                    'Queue GC deleted %d done / %d failed rows (done<%s, failed<%s).',
                    $deletedDone,
                    $deletedFailed,
                    $doneBefore,
                    $failedBefore
                ));
            }
        } catch (\Throwable $e) {
            // Best-effort: never break the tick.
            $logger->error('Queue GC failed: ' . $e->getMessage());
        }
    }
}
