<?php
/**
 * SearchShifter™ Job Runner
 * Executes queued background jobs (≤3 concurrent)
 */

if ( ! defined( 'ABSPATH' ) ) exit;

class Searchshifter_JobRunner {

    const MAX_CONCURRENT = 3;
    const MAX_ATTEMPTS = 3;
    const MEMORY_LIMIT_MB = 128;
    const TIME_LIMIT_SEC = 25;

    public static function init() {
        // Schedule recurring cron job
        add_action('ss_run_jobs_event', [__CLASS__, 'process_queue']);
        if (!wp_next_scheduled('ss_run_jobs_event')) {
            wp_schedule_event(time() + 60, 'five_minutes', 'ss_run_jobs_event');
        }

        // Add custom interval (5 minutes)
        add_filter( 'cron_schedules', function ( $schedules ) {
            $schedules['five_minutes'] = [
                'interval' => 300, // 5 minutes
                'display'  => esc_html__( 'Every 5 Minutes', 'searchshifter' ),
            ];
            return $schedules;
        } );

    }

    /**
     * Process queued jobs
     */
    public static function process_queue() {
        global $wpdb;
        $start_time = microtime(true);
        $table = "{$wpdb->prefix}ss_jobs";

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $jobs = $wpdb->get_results(
            $wpdb->prepare(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                "SELECT * FROM {$table} WHERE status = %s ORDER BY id ASC LIMIT %d",
                'queued',
                self::MAX_CONCURRENT
            )
        );


        if (empty($jobs)) return;

        foreach ($jobs as $job) {
            if (self::near_limits($start_time)) break;

            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->update($table, ['status' => 'running'], ['id' => $job->id]);

            try {
                // 🧠 Handle special Smart Sync job type
                if ($job->type === 'smart_sync') {
                    $payload = maybe_unserialize($job->payload);
                    $post_id = intval($payload['post_id'] ?? 0);

                    if (class_exists('SS_AI_Protocol')) {
                        SS_AI_Protocol::on_activation(); // regenerate llms.txt / ai.txt
                    }
                    if (class_exists('SS_Schema')) {
                        SS_Schema::generate_all(); // regenerate schema
                    }
                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                    $wpdb->update($table, [
                        'status' => 'done',
                        'log'    => '✅ Smart Sync completed for Post ID ' . $post_id,
                        'last_error' => null,
                    ], ['id' => $job->id]);

                    continue; // move to next job
                }

                // 🔄 For all other job payloads
                $success = self::execute_job($job);

                if ($success) {
                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                    $wpdb->update($table, [
                        'status' => 'done',
                        'log'    => '✅ Job completed successfully',
                    ], ['id' => $job->id]);
                } else {
                    throw new Exception('Unknown or failed job action');
                }

            } catch (Exception $e) {
                $attempts = $job->attempts + 1;
                $status = ($attempts >= self::MAX_ATTEMPTS) ? 'failed' : 'queued';
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->update($table, [
                    'status'     => $status,
                    'attempts'   => $attempts,
                    'last_error' => $e->getMessage(),
                ], ['id' => $job->id]);

                if ($status === 'queued') {
                    $delay = pow(2, $attempts) * 60;
                    wp_schedule_single_event(time() + $delay, 'ss_run_jobs_event');
                }
            }
        }
    }

    /**
     * Execute individual job
     */
    private static function execute_job($job) {
        try {
            $payload = json_decode($job->payload, true);
            if (empty($payload['action'])) return false;

            switch ($payload['action']) {

                case 'sync_llms':
                    if (class_exists('SS_SyncEngine')) {
                        SS_SyncEngine::sync_llms($payload);
                    }
                    break;

                case 'crawl_url':
                    if (class_exists('SS_Scanner')) {
                        SS_Scanner::scan_single($payload['url']);
                    }
                    break;

                case 'analyze_backlink':
                    if (class_exists('SS_Analytics')) {
                        SS_Analytics::record_backlink($payload);
                    }
                    break;

                default:

                    return false;
            }

            return true;

        } catch (\Throwable $e) {

            return false;
        }
    }

    /**
     * Prevent runaway memory or time usage
     */
    private static function near_limits($start_time) {
        $elapsed = microtime(true) - $start_time;
        $used_mb = memory_get_usage(true) / 1024 / 1024;

        return ($elapsed > self::TIME_LIMIT_SEC || $used_mb > self::MEMORY_LIMIT_MB);
    }

    /**
     * Manual test trigger (optional admin button)
     */
    public static function test_enqueue_sample() {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert("{$wpdb->prefix}ss_jobs", [
            'type'    => 'test',
            'payload' => json_encode(['action' => 'sync_llms']),
            'status'  => 'queued'
        ]);
    }

}
