<?php
/**
 * Background Warmup Service
 *
 * Manages asynchronous cache warmup jobs using WP-Cron for
 * non-blocking cache population in the background.
 *
 * @package Mamba\Modules\Caching\Services\Preload
 * @since   1.0.0
 */

namespace Mamba\Modules\Caching\Services\Preload;

use Mamba\Modules\Caching\Services\Preload\Warmup\ErrorTracker;
use Mamba\Support\Logger;

/**
 * Class BackgroundWarmup
 *
 * Handles background cache warmup job scheduling, execution,
 * progress tracking, and job state management.
 *
 * @since 1.0.0
 */
final class BackgroundWarmup {
    
    /**
     * Schedule a background warmup job
     * Note: URL validation is deferred to the background job to keep scheduling fast
     */
    public static function scheduleJob(): string {
        try {
            $jobId = uniqid('warmup_');
            
            // Use public methods instead of reflection
            $urls = Preloader::buildUrls();
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('Mamba BackgroundWarmup: Built ' . count($urls) . ' URLs');
            }
            
            if (empty($urls)) {
                throw new \Exception('No URLs found to warm up');
            }
            
            // Expand URLs with variants using public method
            $expanded = Preloader::expandVariants($urls);
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('Mamba BackgroundWarmup: Expanded to ' . count($expanded) . ' URLs');
            }
            
            if (empty($expanded)) {
                throw new \Exception('Failed to expand URLs with variants');
            }

            // Skip synchronous URL validation - it will be done during background processing
            // This makes scheduling instant instead of waiting for HTTP requests
            
            $job = [
                'id' => $jobId,
                'status' => 'pending',
                'created_at' => time(),
                'updated_at' => time(),
                'urls' => $expanded,
                'total_urls' => count($expanded),
                'processed' => 0,
                'success' => 0,
                'failed' => 0,
                'skipped' => 0,
                'user_id' => get_current_user_id(),
                'concurrency' => (int)get_option('mamba_preload_concurrency', 10),
                'batch_size' => (int)get_option('mamba_warmup_batch_size', 50),
                'needs_validation' => true
            ];
            
            // Store job data (non-autoloaded to prevent options table bloat)
            update_option("mamba_warmup_job_{$jobId}", $job, false);
            
            // Schedule background processing immediately (reduced from 30s to 5s)
            wp_schedule_single_event(time() + 5, 'mamba_background_warmup', [$jobId]);
            
            Logger::warmup('job_scheduled', [
                'job_id' => $jobId,
                'total_urls' => count($expanded),
                'scheduled_in' => '5s'
            ]);
            
            return $jobId;
        } catch (\Exception $e) {
            Logger::error('Background warmup scheduling failed', ['error' => $e->getMessage()]);
            throw $e;
        }
    }
    
    /**
     * Process background warmup job
     */
    public static function processJob(string $jobId): void {
        $job = get_option("mamba_warmup_job_{$jobId}");
        if (!$job || $job['status'] !== 'pending') {
            return;
        }
        
        // Update job status to running
        $job['status'] = 'running';
        $job['updated_at'] = time();
        update_option("mamba_warmup_job_{$jobId}", $job, false);
        
        try {
            // Perform deferred URL validation if needed
            if (!empty($job['needs_validation'])) {
                $validationConcurrency = (int)get_option('mamba_warmup_validation_concurrency', 5);
                $validatedUrls = \Mamba\Modules\Caching\Services\Preload\Warmup\Warmer::preValidateUrls($job['urls'], $validationConcurrency);
                $cacheableUrls = \Mamba\Modules\Caching\Services\Preload\Warmup\Warmer::filterCacheableUrls($validatedUrls);
                
                if (empty($cacheableUrls)) {
                    throw new \Exception('No cacheable URLs found after filtering');
                }
                
                $job['urls'] = $cacheableUrls;
                $job['total_urls'] = count($cacheableUrls);
                $job['skipped'] = count($job['urls']) - count($cacheableUrls);
                $job['needs_validation'] = false;
                update_option("mamba_warmup_job_{$jobId}", $job, false);
                
                if (defined('WP_DEBUG') && WP_DEBUG) {
                    error_log('Mamba BackgroundWarmup: Validated to ' . count($cacheableUrls) . ' cacheable URLs');
                }
            }
            
            $results = self::processUrlsInBatches($job);
            
            // Update job with results
            $job['status'] = 'completed';
            $job['updated_at'] = time();
            $job['success'] = $results['success'];
            $job['failed'] = $results['failed'];
            $job['processed'] = $results['processed'];
            $job['completion_time'] = time();
            
            update_option("mamba_warmup_job_{$jobId}", $job, false);
            
            Logger::warmup('job_completed', [
                'job_id' => $jobId,
                'success' => $results['success'],
                'failed' => $results['failed'],
                'processed' => $results['processed'],
                'duration_seconds' => time() - $job['created_at']
            ]);
            
            // Trigger completion notification
            self::notifyCompletion($jobId, $job);
            
        } catch (\Exception $e) {
            // Update job with error
            $job['status'] = 'failed';
            $job['updated_at'] = time();
            $job['error'] = $e->getMessage();
            update_option("mamba_warmup_job_{$jobId}", $job, false);
            
            Logger::error('Background warmup job failed', [
                'job_id' => $jobId,
                'error' => $e->getMessage()
            ]);
            
            // Track error
            ErrorTracker::trackError('background_job', 'job_failed', [
                'message' => $e->getMessage(),
                'job_id' => $jobId
            ]);
        }
    }
    
    /**
     * Process URLs in batches
     */
    private static function processUrlsInBatches(array $job): array {
        $urls = $job['urls'];
        $batchSize = $job['batch_size'];
        $concurrency = $job['concurrency'];
        $jobId = $job['id'];
        
        $totalProcessed = 0;
        $totalSuccess = 0;
        $totalFailed = 0;
        
        // Process URLs in batches
        for ($offset = 0; $offset < count($urls); $offset += $batchSize) {
            // Check if job has been cancelled before processing each batch
            $currentJob = get_option("mamba_warmup_job_{$jobId}");
            if ($currentJob && $currentJob['status'] === 'cancelled') {
                if (defined('WP_DEBUG') && WP_DEBUG) {
                    error_log('Mamba BackgroundWarmup: Job ' . $jobId . ' was cancelled, stopping batch processing');
                }
                break;
            }
            
            $batch = array_slice($urls, $offset, $batchSize);
            
            // Process batch
            $results = \Mamba\Modules\Caching\Services\Preload\Warmup\Warmer::processBatchWithRetry($batch, (int)get_option('mamba_warmup_max_retries', 2));
            
            // Count results
            foreach ($results as $result) {
                $totalProcessed++;
                if ($result === true) {
                    $totalSuccess++;
                } else {
                    $totalFailed++;
                }
            }
            
            // Check error rate and pause if too high
            $errorRateThreshold = (float)get_option('mamba_warmup_error_rate_threshold', 0.5); // default 50%
            if ($totalProcessed > 0 && ($totalFailed / $totalProcessed) > $errorRateThreshold) {
                // Update job with paused status
                $job['processed'] = $totalProcessed;
                $job['success'] = $totalSuccess;
                $job['failed'] = $totalFailed;
                $job['status'] = 'paused';
                $job['pause_reason'] = 'High error rate detected';
                $job['updated_at'] = time();
                update_option("mamba_warmup_job_{$job['id']}", $job, false);
                
                // Trigger pause notification
                self::notifyPause($jobId, $job, 'High error rate: ' . round(($totalFailed / $totalProcessed) * 100, 1) . '% failed');
                
                if (defined('WP_DEBUG') && WP_DEBUG) {
                    error_log('Mamba BackgroundWarmup: Paused job ' . $jobId . ' due to high error rate');
                }
                break; // Stop processing further batches
            }
            
            // Update job progress
            $job['processed'] = $totalProcessed;
            $job['success'] = $totalSuccess;
            $job['failed'] = $totalFailed;
            $job['updated_at'] = time();
            update_option("mamba_warmup_job_{$job['id']}", $job, false);
            
            // Small delay to prevent overwhelming the server
            $batchDelay = (int)get_option('mamba_warmup_batch_delay', 100000); // microseconds, default 0.1s
            usleep($batchDelay);
        }
        
        return [
            'processed' => $totalProcessed,
            'success' => $totalSuccess,
            'failed' => $totalFailed
        ];
    }
    
    /**
     * Get job status
     */
    public static function getJobStatus(string $jobId): ?array {
        return get_option("mamba_warmup_job_{$jobId}");
    }
    
    /**
     * Get all recent jobs
     */
    public static function getRecentJobs(int $limit = 10): array {
        global $wpdb;
        
        $jobs = [];
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $options = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT option_name, option_value FROM {$wpdb->options} 
                 WHERE option_name LIKE %s 
                 ORDER BY option_id DESC 
                 LIMIT %d",
                'mamba_warmup_job_%',
                $limit
            )
        );
        
        foreach ($options as $option) {
            $jobId = str_replace('mamba_warmup_job_', '', $option->option_name);
            $job = maybe_unserialize($option->option_value);
            if ($job && is_array($job)) {
                $jobs[] = $job;
            }
        }
        
        return $jobs;
    }
    
    /**
     * Cancel a running job
     */
    public static function cancelJob(string $jobId): bool {
        $job = self::getJobStatus($jobId);
        if (!$job || !in_array($job['status'], ['running', 'pending'], true)) {
            return false;
        }
        
        $job['status'] = 'cancelled';
        $job['updated_at'] = time();
        $job['cancel_reason'] = 'Cancelled by user or settings change';
        update_option("mamba_warmup_job_{$jobId}", $job, false);
        
        // Also unschedule any pending cron events for this job
        wp_unschedule_hook('mamba_background_warmup');
        
        return true;
    }
    
    /**
     * Cancel all running or pending warmup jobs
     * Used when settings change to ensure fresh warmup with new settings
     */
    public static function cancelAllJobs(): int {
        global $wpdb;
        
        $cancelled = 0;
        
        // Find all running or pending jobs
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $options = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT option_name, option_value FROM {$wpdb->options} 
                 WHERE option_name LIKE %s",
                'mamba_warmup_job_%'
            )
        );
        
        foreach ($options as $option) {
            $job = maybe_unserialize($option->option_value);
            if ($job && is_array($job) && in_array($job['status'] ?? '', ['running', 'pending'], true)) {
                $job['status'] = 'cancelled';
                $job['updated_at'] = time();
                $job['cancel_reason'] = 'Settings changed - restarting warmup';
                update_option($option->option_name, $job, false);
                $cancelled++;
            }
        }
        
        // Unschedule any pending warmup cron events
        wp_unschedule_hook('mamba_background_warmup');
        wp_unschedule_hook('mamba_cache_preload');
        
        // Clear the scheduling lock to allow immediate rescheduling
        delete_transient('mamba_sched_mamba_cache_preload');
        delete_transient('mamba_sched_mamba_background_warmup');
        
        if ($cancelled > 0 && defined('WP_DEBUG') && WP_DEBUG) {
            error_log('Mamba BackgroundWarmup: Cancelled ' . $cancelled . ' warmup job(s) due to settings change');
        }
        
        return $cancelled;
    }
    
    /**
     * Clean up old jobs
     */
    public static function cleanupOldJobs(): void {
        $jobs = self::getRecentJobs(100);
        $cutoff = time() - (24 * 60 * 60); // 24 hours ago
        
        foreach ($jobs as $job) {
            if (($job['created_at'] ?? 0) < $cutoff) {
                delete_option("mamba_warmup_job_{$job['id']}");
            }
        }
    }
    
    /**
     * Notify pause
     */
    private static function notifyPause(string $jobId, array $job, string $reason): void {
        // Store pause notification
        $notification = [
            'type' => 'warmup_paused',
            'job_id' => $jobId,
            'reason' => $reason,
            'success' => $job['success'],
            'failed' => $job['failed'],
            'total' => $job['total_urls'],
            'timestamp' => time(),
            'user_id' => $job['user_id']
        ];
        
        $notifications = get_option('mamba_admin_notifications', []);
        $notifications[] = $notification;
        
        // Keep only last 20 notifications
        if (count($notifications) > 20) {
            $notifications = array_slice($notifications, -20);
        }
        
        update_option('mamba_admin_notifications', $notifications);
    }
    
    /**
     * Notify completion
     */
    private static function notifyCompletion(string $jobId, array $job): void {
        // Store completion notification
        $notification = [
            'type' => 'warmup_completed',
            'job_id' => $jobId,
            'success' => $job['success'],
            'failed' => $job['failed'],
            'total' => $job['total_urls'],
            'timestamp' => time(),
            'user_id' => $job['user_id']
        ];
        
        $notifications = get_option('mamba_admin_notifications', []);
        $notifications[] = $notification;
        
        // Keep only last 20 notifications
        if (count($notifications) > 20) {
            $notifications = array_slice($notifications, -20);
        }
        
        update_option('mamba_admin_notifications', $notifications);
    }
    

}
