<?php
/**
 * Alternate Cron System
 * 
 * Provides a reliable cron trigger mechanism that works without server-level
 * crontab configuration. Uses wp_remote_post to spawn cron events.
 *
 * @package AIContentGenerator
 */

namespace AICG;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Implements self-triggering cron to ensure scheduled events run reliably
 */
class Alternate_Cron
{
    /**
     * Option name for last spawn time
     */
    const LAST_SPAWN_OPTION = 'roidaico_last_cron_spawn';

    /**
     * Minimum seconds between spawn attempts
     */
    const MIN_SPAWN_INTERVAL = 60;

    /**
     * Initialize alternate cron
     */
    public static function init()
    {
        // Hook into shutdown to spawn cron if needed
        add_action('shutdown', array(__CLASS__, 'maybe_spawn_cron'));
        
        // Add AJAX endpoint for cron execution
        add_action('wp_ajax_nopriv_roidaico_cron_spawn', array(__CLASS__, 'handle_cron_spawn'));
        add_action('wp_ajax_roidaico_cron_spawn', array(__CLASS__, 'handle_cron_spawn'));
        
        // Add AJAX endpoint for immediate buffer processing
        add_action('wp_ajax_nopriv_roidaico_buffer_spawn', array(__CLASS__, 'handle_buffer_spawn'));
        add_action('wp_ajax_roidaico_buffer_spawn', array(__CLASS__, 'handle_buffer_spawn'));
        
        // Add Heartbeat API support for admin pages
        add_filter('heartbeat_received', array(__CLASS__, 'heartbeat_check_cron'), 10, 2);
    }

    /**
     * Maybe spawn cron on shutdown
     * 
     * This runs after the page is sent to the user, so it doesn't slow down page load.
     */
    public static function maybe_spawn_cron()
    {
        // Skip if doing AJAX or cron already
        if (defined('DOING_CRON') || defined('DOING_AJAX')) {
            return;
        }

        // Check if we spawned recently
        $last_spawn = get_option(self::LAST_SPAWN_OPTION, 0);
        if ((time() - $last_spawn) < self::MIN_SPAWN_INTERVAL) {
            return;
        }

        // Check if there are due events
        if (!self::has_due_events()) {
            return;
        }

        // Spawn cron via loopback
        self::spawn_cron();
    }

    /**
     * Check if there are AICG events due
     */
    private static function has_due_events()
    {
        $crons = _get_cron_array();
        if (empty($crons)) {
            return false;
        }

        $now = time();
        foreach ($crons as $timestamp => $hooks) {
            if ($timestamp > $now) {
                break;
            }
            // Check for our hooks only
            if (isset($hooks['roidaico_scheduled_publish']) || isset($hooks['roidaico_buffer_check'])) {
                return true;
            }
        }

        return false;
    }

    /**
     * Spawn cron via non-blocking loopback request
     */
    private static function spawn_cron()
    {
        // Update last spawn time immediately to prevent race conditions
        update_option(self::LAST_SPAWN_OPTION, time());

        $url = admin_url('admin-ajax.php');
        $args = array(
            'timeout'   => 0.01, // Non-blocking
            'blocking'  => false,
            'sslverify' => apply_filters('https_local_ssl_verify', false),
            'body'      => array(
                'action' => 'roidaico_cron_spawn',
                'nonce'  => wp_create_nonce('roidaico_cron_spawn'),
            ),
        );

        wp_remote_post($url, $args);
    }

    /**
     * Spawn buffer processing immediately (public, for use after keyword add)
     */
    public static function spawn_buffer_now()
    {
        $url = admin_url('admin-ajax.php');
        $args = array(
            'timeout'   => 0.01, // Non-blocking
            'blocking'  => false,
            'sslverify' => apply_filters('https_local_ssl_verify', false),
            'body'      => array(
                'action' => 'roidaico_buffer_spawn',
            ),
        );

        wp_remote_post($url, $args);
    }

    /**
     * Handle buffer spawn AJAX request (immediate buffer processing)
     */
    public static function handle_buffer_spawn()
    {
        // Execute buffer check directly
        do_action('roidaico_buffer_check');

        // Update last cron check for health notice
        update_option('roidaico_last_cron_check', time());

        wp_die('Buffer triggered');
    }

    /**
     * Handle cron spawn AJAX request
     */
    public static function handle_cron_spawn()
    {
        // Verify this is a legitimate request (loose check for non-blocking)
        if (!isset($_POST['action']) || $_POST['action'] !== 'roidaico_cron_spawn') {
            wp_die('Invalid request');
        }

        // Run due cron events
        $crons = _get_cron_array();
        if (empty($crons)) {
            wp_die('No cron events');
        }

        $now = time();
        foreach ($crons as $timestamp => $hooks) {
            if ($timestamp > $now) {
                break;
            }

            foreach ($hooks as $hook => $args) {
                // Only run our hooks
                if (strpos($hook, 'roidaico_') !== 0) {
                    continue;
                }

                foreach ($args as $sig => $data) {
                    // Execute the hook
                    do_action_ref_array($hook, $data['args']);

                    // Reschedule if recurring
                    if (!empty($data['schedule'])) {
                        $interval = wp_get_schedules()[$data['schedule']]['interval'] ?? 0;
                        if ($interval) {
                            wp_reschedule_event($timestamp, $data['schedule'], $hook, $data['args']);
                        }
                    }

                    // Unschedule the old event
                    wp_unschedule_event($timestamp, $hook, $data['args']);
                }
            }
        }

        // Update last cron check for health notice
        update_option('roidaico_last_cron_check', time());

        wp_die('OK');
    }

    /**
     * Check cron on admin heartbeat (runs every 15-60 seconds in admin)
     */
    public static function heartbeat_check_cron($response, $data)
    {
        // Only process on plugin pages or if explicitly requested
        if (!isset($data['roidaico_check_cron']) && 
            (empty($_GET['page']) || strpos($_GET['page'], 'aicg') === false)) {
            return $response;
        }

        // Check for due events
        if (self::has_due_events()) {
            // Run due events synchronously in heartbeat
            $crons = _get_cron_array();
            $now = time();
            
            foreach ($crons as $timestamp => $hooks) {
                if ($timestamp > $now) {
                    break;
                }

                foreach ($hooks as $hook => $args) {
                    if (strpos($hook, 'roidaico_') !== 0) {
                        continue;
                    }

                    foreach ($args as $sig => $data_item) {
                        do_action_ref_array($hook, $data_item['args']);
                        
                        if (!empty($data_item['schedule'])) {
                            $interval = wp_get_schedules()[$data_item['schedule']]['interval'] ?? 0;
                            if ($interval) {
                                wp_reschedule_event($timestamp, $data_item['schedule'], $hook, $data_item['args']);
                            }
                        }
                        wp_unschedule_event($timestamp, $hook, $data_item['args']);
                    }
                }
            }

            update_option('roidaico_last_cron_check', time());
            $response['roidaico_cron_ran'] = true;
        }

        return $response;
    }
}
