<?php
/**
 * VMPFence Scan Scheduler
 *
 * This file contains the VMPFence_Scan_Scheduler class that handles
 * automatic and manual scan scheduling functionality.
 *
 * @package VMPFence
 * @subpackage Includes
 * @since 1.0.0
 * @since 2.2.4 -- Pushed comprehensive scheduled scan results to server for SIEM integration
 * @since 2.2.5 -- Added Enhanced Audit Log
 * @since 2.2.6 -- Added Email notifications for scheduled scans, GSB alert
 */

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

class VMPFence_Scan_Scheduler {

    /**
     * Singleton instance
     *
     * @var VMPFence_Scan_Scheduler
     */
    private static $instance = null;

    /**
     * Get singleton instance
     *
     * @return VMPFence_Scan_Scheduler
     */
    public static function shared() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Get singleton instance (alias for compatibility)
     *
     * @return VMPFence_Scan_Scheduler
     */
    public static function getInstance() {
        return self::shared();
    }

    /**
     * Private constructor to prevent direct instantiation
     */
    private function __construct() {
        // Initialize any necessary setup here
    }

    /**
     * Get configuration value with fallback
     *
     * @param string $key Config key
     * @param mixed $default Default value
     * @return mixed Config value
     */
    private function getConfigValue($key, $default = null) {
        return VMPFence_Config::get($key, $default);
    }

    /**
     * Get current scheduling mode
     *
     * @return string One of VMPFence_Constants::SCAN_SCHEDULING_MODE_*
     */
    public function schedulingMode() {
        $schedMode = VMPFence_Config::get('schedMode', 'auto');
        if ($schedMode === 'manual') {
            return VMPFence_Constants::SCAN_SCHEDULING_MODE_MANUAL;
        }
        return VMPFence_Constants::SCAN_SCHEDULING_MODE_AUTOMATIC;
    }

    /**
     * Get manual scheduling pattern type
     *
     * @return string One of VMPFence_Constants::MANUAL_SCHEDULING_*
     */
    public function manualSchedulingType() {
        return VMPFence_Config::get('manualScanType', VMPFence_Constants::MANUAL_SCHEDULING_ONCE_DAILY);
    }

    /**
     * Get preferred start hour for manual schedules
     *
     * @return int Hour number (0-23)
     */
    public function manualSchedulingStartHour() {
        $hour = VMPFence_Config::get('schedStartHour', 2);
        return max(0, min(23, (int) $hour));
    }

    /**
     * Get the currently defined custom schedule
     *
     * @return array 7x24 grid of enabled hours
     */
    public function customSchedule() {
        $normalizedSchedule = array_fill(0, 7, array_fill(0, 24, 0));
        $storedSchedule = VMPFence_Config::get('scanSched', array());
        
        if (is_array($storedSchedule) && !empty($storedSchedule)) {
            foreach ($storedSchedule as $dayNumber => $day) {
                if (is_array($day)) {
                    foreach ($day as $hourNumber => $enabled) {
                        if (isset($normalizedSchedule[$dayNumber][$hourNumber])) {
                            $normalizedSchedule[$dayNumber][$hourNumber] = $enabled ? 1 : 0;
                        }
                    }
                }
            }
        }
        
        return $normalizedSchedule;
    }

    /**
     * Generate schedule based on current mode
     *
     * @return array Array of timestamps for next 14 days
     */
    public function generateSchedule() {
        if ($this->schedulingMode() === VMPFence_Constants::SCAN_SCHEDULING_MODE_AUTOMATIC) {
            return $this->generateAutomaticSchedule();
        } else {
            return $this->generateManualSchedule();
        }
    }

    /**
     * Generate automatic schedule with intelligent optimization
     *
     * @return array Array of timestamps
     * @since 1.0.0
     * @since 2.1.1 date timezone handling and improved randomness
     */
    public function generateAutomaticSchedule() {
        $schedule = array();
        $now = time();
        $timezone = $this->getScheduleTimezone();
        
        // Get site timezone offset
        $originalTz = date_default_timezone_get();
        if (!empty($timezone) && $timezone !== '+00:00') {
            date_default_timezone_set($timezone);
        }
        
        try {
            // Analyze site activity patterns for intelligent scheduling
            $this->refreshActivityAnalysis();
            
            // Generate scans for next 14 days with intelligent timing
            for ($day = 1; $day <= 14; $day++) {
                $targetDate = $now + ($day * 86400);
                
                // Find optimal scan time for this day using intelligent analysis
                $optimalHour = $this->calculateOptimalScanHour($targetDate);
                
                // Add smart random offset based on site load distribution
                $randomOffset = $this->calculateSmartRandomOffset($day);
                
                // Create timestamp for scan
                $scanTime = mktime($optimalHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                $scanTime += $randomOffset;
                
                // Ensure scan time is in the future
                if ($scanTime > $now) {
                    $schedule[] = $scanTime;
                }
            }
            
            // Sort schedule chronologically
            sort($schedule);
            
            // Store generated schedule with metadata
            $this->saveAutomaticSchedule($schedule);
            
        } finally {
            // Restore original timezone
            date_default_timezone_set($originalTz);
        }
        
        return $schedule;
    }

    /**
     * Refresh activity analysis data if needed
     *
     * @return bool Whether analysis was refreshed
     */
    private function refreshActivityAnalysis() {
        $lastAnalysis = VMPFence_Config::get('lastActivityAnalysis', 0);
        $now = time();
        $oneWeek = 7 * 24 * 3600;
        
        // Refresh analysis weekly or if never done
        if (!$lastAnalysis || ($now - $lastAnalysis) > $oneWeek) {
            // Force refresh of activity patterns
            $this->analyzeSiteActivityPatterns();
            VMPFence_Config::set('lastActivityAnalysis', $now);
            return true;
        }
        
        return false;
    }

    /**
     * Calculate smart random offset based on load distribution
     *
     * @param int $day Day number in schedule
     * @return int Offset in seconds (0-3600)
     */
    private function calculateSmartRandomOffset($day) {
        $randomOffset = VMPFence_Config::get('randomOffset', false);
        
        if (!$randomOffset) {
            return 0;
        }
        
        // Use site URL and day to create deterministic but distributed randomness
        $siteHash = crc32(get_site_url());
        $dayHash = crc32($day . $siteHash);
        $maxOffset = 3600; // 1 hour maximum
        
        // Create pseudo-random offset that's consistent for the same day
        $offset = abs($dayHash) % $maxOffset;
        
        return $offset;
    }

    /**
     * Generate manual schedule based on user pattern
     *
     * @return array Array of timestamps
     */
    public function generateManualSchedule() {
        $pattern = $this->manualSchedulingType();
        
        switch ($pattern) {
            case VMPFence_Constants::MANUAL_SCHEDULING_ONCE_DAILY:
                return $this->generateOnceDaily();
            case VMPFence_Constants::MANUAL_SCHEDULING_TWICE_DAILY:
                return $this->generateTwiceDaily();
            case VMPFence_Constants::MANUAL_SCHEDULING_EVERY_OTHER_DAY:
                return $this->generateEveryOtherDay();
            case VMPFence_Constants::MANUAL_SCHEDULING_WEEKDAYS:
                return $this->generateWeekdays();
            case VMPFence_Constants::MANUAL_SCHEDULING_WEEKENDS:
                return $this->generateWeekends();
            case VMPFence_Constants::MANUAL_SCHEDULING_ODD_DAYS_WEEKENDS:
                return $this->generateOddDaysWeekends();
            case VMPFence_Constants::MANUAL_SCHEDULING_CUSTOM:
                return $this->generateCustomSchedule();
            default:
                return $this->generateOnceDaily();
        }
    }

    /**
     * Generate once daily schedule
     *
     * @return array Array of timestamps
     */
    public function generateOnceDaily() {
        $schedule = array();
        $startHour = $this->manualSchedulingStartHour();
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        
        try {
            for ($day = 1; $day <= 14; $day++) {
                $targetDate = time() + ($day * 86400);
                $scanTime = mktime($startHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                
                // Apply random offset if enabled
                $randomOffset = $this->calculateSmartRandomOffset($day);
                $scanTime += $randomOffset;
                
                $schedule[] = $scanTime;
            }
        } finally {
            date_default_timezone_set($originalTz);
        }
        
        return $schedule;
    }

    /**
     * Generate twice daily schedule
     *
     * @return array Array of timestamps
     */
    public function generateTwiceDaily() {
        $schedule = array();
        $startHour = $this->manualSchedulingStartHour();
        $secondHour = ($startHour + 12) % 24;
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        
        try {
            for ($day = 1; $day <= 14; $day++) {
                $targetDate = time() + ($day * 86400);
                
                // Apply random offset if enabled (same offset for both scans on same day)
                $randomOffset = $this->calculateSmartRandomOffset($day);
                
                // First scan
                $scanTime1 = mktime($startHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                $scanTime1 += $randomOffset;
                $schedule[] = $scanTime1;
                
                // Second scan 12 hours later
                $scanTime2 = mktime($secondHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                $scanTime2 += $randomOffset;
                $schedule[] = $scanTime2;
            }
        } finally {
            date_default_timezone_set($originalTz);
        }
        
        sort($schedule);
        return $schedule;
    }

    /**
     * Generate every other day schedule
     *
     * @return array Array of timestamps
     */
    public function generateEveryOtherDay() {
        $schedule = array();
        $startHour = $this->manualSchedulingStartHour();
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        
        try {
            for ($day = 1; $day <= 14; $day += 2) {
                $targetDate = time() + ($day * 86400);
                $scanTime = mktime($startHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                
                // Apply random offset if enabled
                $randomOffset = $this->calculateSmartRandomOffset($day);
                $scanTime += $randomOffset;
                
                $schedule[] = $scanTime;
            }
        } finally {
            date_default_timezone_set($originalTz);
        }
        
        return $schedule;
    }

    /**
     * Generate weekdays only schedule (Monday-Friday)
     *
     * @return array Array of timestamps
     */
    public function generateWeekdays() {
        $schedule = array();
        $startHour = $this->manualSchedulingStartHour();
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        
        try {
            for ($day = 1; $day <= 14; $day++) {
                $targetDate = time() + ($day * 86400);
                $dayOfWeek = gmdate('w', $targetDate); // 0 = Sunday, 6 = Saturday
                
                if ($dayOfWeek >= 1 && $dayOfWeek <= 5) { // Monday to Friday
                    $scanTime = mktime($startHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                    
                    // Apply random offset if enabled
                    $randomOffset = $this->calculateSmartRandomOffset($day);
                    $scanTime += $randomOffset;
                    
                    $schedule[] = $scanTime;
                }
            }
        } finally {
            date_default_timezone_set($originalTz);
        }
        
        return $schedule;
    }

    /**
     * Generate weekends only schedule (Saturday-Sunday)
     *
     * @return array Array of timestamps
     */
    public function generateWeekends() {
        $schedule = array();
        $startHour = $this->manualSchedulingStartHour();
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        
        try {
            for ($day = 1; $day <= 14; $day++) {
                $targetDate = time() + ($day * 86400);
                $dayOfWeek = gmdate('w', $targetDate); // 0 = Sunday, 6 = Saturday
                
                if ($dayOfWeek == 0 || $dayOfWeek == 6) { // Saturday or Sunday
                    $scanTime = mktime($startHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                    
                    // Apply random offset if enabled
                    $randomOffset = $this->calculateSmartRandomOffset($day);
                    $scanTime += $randomOffset;
                    
                    $schedule[] = $scanTime;
                }
            }
        } finally {
            date_default_timezone_set($originalTz);
        }
        
        return $schedule;
    }

    /**
     * Generate odd days and weekends schedule
     * Runs on: Monday, Wednesday, Friday, Saturday, Sunday
     * Skips: Tuesday, Thursday
     *
     * @return array Array of timestamps
     */
    public function generateOddDaysWeekends() {
        $schedule = array();
        $startHour = $this->manualSchedulingStartHour();
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        
        try {
            for ($day = 1; $day <= 14; $day++) {
                $targetDate = time() + ($day * 86400);
                $dayOfWeek = gmdate('w', $targetDate); // 0 = Sunday, 6 = Saturday
                
                //  weekends (0,6) + odd-numbered weekdays (1,3,5)
                if ($dayOfWeek == 0 || $dayOfWeek == 6 || ($dayOfWeek % 2 == 1)) {
                    $scanTime = mktime($startHour, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
                    
                    // Apply random offset if enabled
                    $randomOffset = $this->calculateSmartRandomOffset($day);
                    $scanTime += $randomOffset;
                    
                    $schedule[] = $scanTime;
                }
            }
        } finally {
            date_default_timezone_set($originalTz);
        }
        
        return $schedule;
    }

    /**
     * Generate custom schedule from 7x24 grid
     *
     * @return array Array of timestamps
     */
    public function generateCustomSchedule() {
        $schedule = array();
        $customGrid = $this->customSchedule();
        $timezone = $this->getScheduleTimezone();
        
        $originalTz = date_default_timezone_get();
        
        try {
            // Use DateTime for proper timezone handling
            $scheduleTimezone = new DateTimeZone($timezone);
            $utcTimezone = new DateTimeZone('UTC');
            
            // Get current time in schedule timezone
            $now = new DateTime('now', $scheduleTimezone);
            $todayDayOfWeek = (int)$now->format('w'); // 0 = Sunday, 6 = Saturday
            $currentHour = (int)$now->format('G'); // 24-hour format
            
            // Add remaining hours for TODAY in schedule timezone
            for ($hour = $currentHour + 1; $hour < 24; $hour++) {
                if (isset($customGrid[$todayDayOfWeek][$hour]) && $customGrid[$todayDayOfWeek][$hour]) {
                    // Create scan time in schedule timezone
                    $scanDateTime = new DateTime($now->format('Y-m-d') . ' ' . sprintf('%02d:00:00', $hour), $scheduleTimezone);
                    
                    // Convert to UTC for WordPress cron
                    $scanDateTime->setTimezone($utcTimezone);
                    $utcTimestamp = $scanDateTime->getTimestamp();
                    
                    // Only schedule if it's in the future
                    if ($utcTimestamp > time()) {
                        $schedule[] = $utcTimestamp;
                    }
                }
            }
            
            // Add future days (1-14)
            for ($day = 1; $day <= 14; $day++) {
                $futureDate = clone $now;
                $futureDate->add(new DateInterval('P' . $day . 'D'));
                $dayOfWeek = (int)$futureDate->format('w'); // 0 = Sunday, 6 = Saturday
                
                // Check each hour for this day of week
                for ($hour = 0; $hour < 24; $hour++) {
                    if (isset($customGrid[$dayOfWeek][$hour]) && $customGrid[$dayOfWeek][$hour]) {
                        // Create scan time in schedule timezone
                        $scanDateTime = new DateTime($futureDate->format('Y-m-d') . ' ' . sprintf('%02d:00:00', $hour), $scheduleTimezone);
                        
                        // Convert to UTC for WordPress cron
                        $scanDateTime->setTimezone($utcTimezone);
                        $utcTimestamp = $scanDateTime->getTimestamp();
                        
                        $schedule[] = $utcTimestamp;
                    }
                }
            }
        } finally {
            // No need to reset timezone since we used DateTime objects
        }
        
        sort($schedule);
        return $schedule;
    }

    /**
     * Get human-readable description of manual scheduling pattern
     *
     * @param string $pattern The scheduling pattern constant
     * @return string Description of the pattern
     */
    public function getManualPatternDescription($pattern = null) {
        if ($pattern === null) {
            $pattern = $this->manualSchedulingType();
        }
        
        $startHour = $this->manualSchedulingStartHour();
        $timeStr = sprintf('%02d:00', $startHour);
        
        switch ($pattern) {
            case VMPFence_Constants::MANUAL_SCHEDULING_ONCE_DAILY:
                return "Once daily at {$timeStr}";
            
            case VMPFence_Constants::MANUAL_SCHEDULING_TWICE_DAILY:
                $secondHour = ($startHour + 12) % 24;
                $timeStr2 = sprintf('%02d:00', $secondHour);
                return "Twice daily at {$timeStr} and {$timeStr2}";
            
            case VMPFence_Constants::MANUAL_SCHEDULING_EVERY_OTHER_DAY:
                return "Every other day at {$timeStr}";
            
            case VMPFence_Constants::MANUAL_SCHEDULING_WEEKDAYS:
                return "Weekdays only (Mon-Fri) at {$timeStr}";
            
            case VMPFence_Constants::MANUAL_SCHEDULING_WEEKENDS:
                return "Weekends only (Sat-Sun) at {$timeStr}";
            
            case VMPFence_Constants::MANUAL_SCHEDULING_ODD_DAYS_WEEKENDS:
                return "Odd weekdays + weekends (Mon,Wed,Fri,Sat,Sun) at {$timeStr}";
            
            case VMPFence_Constants::MANUAL_SCHEDULING_CUSTOM:
                return "Custom schedule (see grid below)";
            
            default:
                return "Unknown scheduling pattern";
        }
    }

    /**
     * Get preview of manual schedule (next 7 scan times)
     *
     * @param string $pattern Optional pattern to preview
     * @param int $startHour Optional start hour to preview
     * @return array Array of formatted scan times
     */
    public function previewManualSchedule($pattern = null, $startHour = null) {
        // Temporarily override settings for preview
        $originalPattern = $this->manualSchedulingType();
        $originalHour = $this->manualSchedulingStartHour();
        
        if ($pattern !== null) {
            VMPFence_Config::set('manualScanType', $pattern);
        }
        if ($startHour !== null) {
            VMPFence_Config::set('manualScanStartHour', $startHour);
        }
        
        try {
            $schedule = $this->generateManualSchedule();
            $preview = array();
            
            foreach (array_slice($schedule, 0, 7) as $timestamp) {
                $preview[] = array(
                    'timestamp' => $timestamp,
                    'formatted' => gmdate('l, F j, Y \a\t g:i A', $timestamp),
                    'relative' => $this->getRelativeTime($timestamp)
                );
            }
            
            return $preview;
            
        } finally {
            // Restore original settings
            VMPFence_Config::set('manualScanType', $originalPattern);
            VMPFence_Config::set('manualScanStartHour', $originalHour);
        }
    }

    /**
     * Validate manual scheduling configuration
     *
     * @param array $config Configuration to validate
     * @return array Validation result with 'valid' and 'errors' keys
     */
    public function validateManualScheduleConfig($config) {
        $result = array('valid' => true, 'errors' => array());
        
        // Validate scheduling pattern
        if (empty($config['manualScanType'])) {
            $result['valid'] = false;
            $result['errors'][] = 'Manual scheduling pattern is required';
        } else {
            $validPatterns = array(
                VMPFence_Constants::MANUAL_SCHEDULING_ONCE_DAILY,
                VMPFence_Constants::MANUAL_SCHEDULING_TWICE_DAILY,
                VMPFence_Constants::MANUAL_SCHEDULING_EVERY_OTHER_DAY,
                VMPFence_Constants::MANUAL_SCHEDULING_WEEKDAYS,
                VMPFence_Constants::MANUAL_SCHEDULING_WEEKENDS,
                VMPFence_Constants::MANUAL_SCHEDULING_ODD_DAYS_WEEKENDS,
                VMPFence_Constants::MANUAL_SCHEDULING_CUSTOM
            );
            
            if (!in_array($config['manualScanType'], $validPatterns)) {
                $result['valid'] = false;
                $result['errors'][] = 'Invalid manual scheduling pattern';
            }
        }
        
        // Validate start hour
        if (!isset($config['manualScanStartHour'])) {
            $result['valid'] = false;
            $result['errors'][] = 'Manual scan start hour is required';
        } else {
            $hour = intval($config['manualScanStartHour']);
            if ($hour < 0 || $hour > 23) {
                $result['valid'] = false;
                $result['errors'][] = 'Manual scan start hour must be between 0 and 23';
            }
        }
        
        // Validate custom schedule if applicable
        if (isset($config['manualScanType']) && 
            $config['manualScanType'] === VMPFence_Constants::MANUAL_SCHEDULING_CUSTOM) {
            
            if (empty($config['scanSched']) || !is_array($config['scanSched'])) {
                $result['valid'] = false;
                $result['errors'][] = 'Custom schedule grid is required for custom pattern';
            } else {
                $hasScheduledScans = false;
                foreach ($config['scanSched'] as $day => $hours) {
                    if (is_array($hours) && !empty(array_filter($hours))) {
                        $hasScheduledScans = true;
                        break;
                    }
                }
                
                if (!$hasScheduledScans) {
                    $result['valid'] = false;
                    $result['errors'][] = 'Custom schedule must have at least one scan time selected';
                }
            }
        }
        
        return $result;
    }

    /**
     * Get relative time description
     *
     * @param int $timestamp Future timestamp
     * @return string Relative time description
     */
    private function getRelativeTime($timestamp) {
        $now = time();
        $diff = $timestamp - $now;
        
        if ($diff < 0) {
            return 'Past';
        }
        
        $days = floor($diff / 86400);
        $hours = floor(($diff % 86400) / 3600);
        
        if ($days > 0) {
            return $days === 1 ? 'Tomorrow' : "In {$days} days";
        } elseif ($hours > 0) {
            return $hours === 1 ? 'In 1 hour' : "In {$hours} hours";
        } else {
            $minutes = floor(($diff % 3600) / 60);
            return $minutes <= 1 ? 'Very soon' : "In {$minutes} minutes";
        }
    }

    /**
     * Schedule all scans using WordPress cron
     *
     * @return bool Success status
     */
    public function scheduleAllScans() {
        // First, clear any existing scheduled scans
        $this->unscheduleAllScans();
        
        $schedule = $this->generateSchedule();
        $scheduled = array();
        
        foreach ($schedule as $timestamp) {
            if ($timestamp > time()) {
                wp_schedule_single_event($timestamp, VMPFence_Constants::CRON_SCHEDULED_SCAN);
                $scheduled[] = $timestamp;
            }
        }
        
        // Store list of scheduled events
        VMPFence_Config::set('allScansScheduled', $scheduled);
        
        return !empty($scheduled);
    }

    /**
     * Remove all scheduled scans
     *
     * @return bool Success status
     */
    public function unscheduleAllScans() {
        $hookName = VMPFence_Constants::CRON_SCHEDULED_SCAN;
        
        // Get all scheduled instances
        $scheduled = wp_next_scheduled($hookName);
        while ($scheduled) {
            wp_unschedule_event($scheduled, $hookName);
            $scheduled = wp_next_scheduled($hookName);
        }
        
        // Clear stored schedule
        VMPFence_Config::set('allScansScheduled', array());
        
        return true;
    }

    /**
     * Get upcoming scheduled scan times
     *
     * @param int $limit Maximum number of scans to return
     * @return array Array of timestamps
     */
    public function getScheduledScanTimes($limit = 10) {
        $scheduled = VMPFence_Config::get('allScansScheduled', array());
        $upcoming = array();
        
        foreach ($scheduled as $timestamp) {
            if ($timestamp > time()) {
                $upcoming[] = $timestamp;
            }
        }
        
        sort($upcoming);
        return array_slice($upcoming, 0, $limit);
    }

    /**
     * Get next scheduled scan time
     *
     * @return int|null Timestamp of next scan or null if none scheduled
     */
    public function nextScheduledScanTime() {
        $upcoming = $this->getScheduledScanTimes(1);
        return !empty($upcoming) ? $upcoming[0] : null;
    }

    /**
     * Execute scheduled scan
     *
     * @return bool Success status
     */
    public function executeScheduledScan() {
        try {
            // Check if scans are enabled
            if (!VMPFence_Config::get('scheduledScansEnabled', true)) {
                return false;
            }
            
            // Get scan configuration
            $scanType = $this->determineScheduledScanType();
            $scanOptions = $this->buildScanOptions($scanType);
            
            // Record scan start
            $scanStartTime = time();
            $this->recordScanEvent('started', $scanType, $scanStartTime);
            do_action('vmpfence_scan_started', $scanType);
            
            $result = $this->executeComprehensiveScan($scanOptions);
            
            // Record scan completion
            $scanEndTime = time();
            $scanDuration = $scanEndTime - $scanStartTime;
            $this->recordScanEvent('completed', $scanType, $scanEndTime, array(
                'duration' => $scanDuration,
                'result' => $result ? 'success' : 'failed'
            ));
            do_action('vmpfence_scan_completed', $scanType, $result, $scanDuration);
            
            // Send notifications if enabled and scan found issues
            $this->handleScanNotifications($scanType, $result, $scanDuration);
            
            // Update last scan statistics
            $this->updateLastScanStats($scanType, $result, $scanDuration);
            
            // CRITICAL: Regenerate schedule if running low on future scans
            $this->ensureFutureSchedules();
            
            // Log scan completion
            $status = $result ? 'completed successfully' : 'completed with errors';
            
            return $result;
            
        } catch (Exception $e) {
            // Record scan failure
            $this->recordScanEvent('failed', $scanType ?? 'unknown', time(), array(
                'error' => $e->getMessage()
            ));
            return false;
        }
    }

    /**
     * Execute comprehensive scan using the same system as "START NEW SCAN"
     * This calls the exact same scan logic as the AJAX handler without code duplication
     *
     * @param array $scanOptions Scan options
     * @return bool Success status
     */
    private function executeComprehensiveScan($scanOptions) {
        try {
            $result = $this->runComprehensiveScanViaAjaxHandler($scanOptions);
            
            return $result;
            
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Execute comprehensive scan using the exact same logic as the AJAX handler
     * This replicates the complete scan system from handle_start_scan() without AJAX dependencies
     *
     * @since 1.0.0
     * @since 2.2.4 -- Push final scan results to server for SIEM integration
     * @param array $scanOptions Scan options (not used - we get fresh scan options from VMPFence_Scan_Config)
     * @return bool Success status
     */
    private function runComprehensiveScanViaAjaxHandler($scanOptions) {
        try {
            // IMPLEMENTATION: Load scan options from VMPFence_Scan_Config (same as AJAX handler)
            require_once VMPFENCE_PLUGIN_DIR . 'lib/class-vmpfence-scan-config.php';
            $scan_options = VMPFence_Scan_Config::getScanOptions();
            
            // Initialize IPv4-only HTTP interceptor if enabled (same as AJAX handler)
            $force_ipv4 = VMPFence_Config::get('scan_force_ipv4_start', false);
            if ($force_ipv4) {
                require_once VMPFENCE_PLUGIN_DIR . 'includes/class-curl-interceptor.php';
                $ipv4_interceptor = new VMPFence_Curl_Interceptor($force_ipv4);
                $ipv4_interceptor->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
            }
            
            // Initialize scan monitoring (same as AJAX handler)
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-scan-monitor.php';
            $scan_type = 'scheduled';
            VMPFence_Scan_Monitor::handleScanStart($scan_type);
            VMPFence_Scan_Monitor::logLastAttempt(false); // Not a fork process
            
            // GLOBAL DATABASE CLEANUP: Clear all previous scan data (same as AJAX handler)
            $this->clearAllPreviousScanData();
            
            // AUTOMATIC BACKUP CLEANUP: Clean up old backup files (same as AJAX handler)  
            $this->cleanupOldBackupFiles();
            
            // Initialize performance management (same as AJAX handler)
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-performance-manager.php';
            VMPFence_Performance_Manager::init();
            
            // Log performance mode
            // if (VMPFence_Performance_Manager::use_low_resource_scanning()) {
            //     // error_log("VMPFence: Starting scheduled scan in LOW RESOURCE mode");
            // }
            
            // Initialize scan statistics collector (same as AJAX handler)
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-scan-statistics-collector.php';
            $stats_collector = VMPFence_Scan_Statistics_Collector::get_instance();
            $stats_collector->start_scan('Comprehensive Scan');
            $stats_collector->collect_wordpress_counts();
            
            // Set initial progress state (same as AJAX handler)
            update_option('vmpfence_scan_progress', [
                'status' => 'starting',
                'files_scanned' => 0,
                'total_files' => 0,
                'current_section' => 'Initializing comprehensive scan...',
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            // Use performance-managed time limits (same as AJAX handler)
            ignore_user_abort(true); // Continue even if user closes browser
            
            $scan_results = [];
            $total_issues = 0;
            
            // Initialize debug logs array (same as AJAX handler)
            $debug_logs = get_option('vmpfence_last_scan_debug_logs', []);
            
            // Execute all 11 scanners using exact same logic as AJAX handler.
            // After each stage, push progress to server so the portal updates in real-time.
            $scan_sync = class_exists('VmpFence_Scan_Sync') ? new VmpFence_Scan_Sync() : null;

            $total_issues += $this->executeServerStateScanner($scan_options, $scan_results, $debug_logs, $ipv4_interceptor ?? null);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeFileChangesScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeUserSecurityScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeContentSafetyScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executePublicFilesScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeUserOptionAuditScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeVulnerabilityScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeMalwareScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeDomainBlocklistScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeSpamvertsingScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }

            $total_issues += $this->executeIPSpamScanner($scan_options, $scan_results, $debug_logs);
            if ($scan_sync) { $scan_sync->force_push_progress(); }
            
            // Process debug logs the same way as AJAX handler
            $this->processDebugLogs($debug_logs, $scan_results);
            
            // Store scan completion data (same as AJAX handler)
            $this->storeScanCompletionData($scan_results, $total_issues);
            
            // Complete scan statistics collection (same as AJAX handler)
            $final_statistics = $stats_collector->end_scan();
            
            // Log performance summary (same as AJAX handler)
            VMPFence_Performance_Manager::log_performance_summary();
            
            // SIEM: Push final scan results (issues + statistics) to server
            if (class_exists('VmpFence_Scan_Sync')) {
                try {
                    $scan_sync_final = new VmpFence_Scan_Sync();
                    $push_ok = $scan_sync_final->push_scan_results();
                    if ($push_ok) {
                        delete_option('vmpfence_scan_results_pending_push');
                    } else {
                        // FIX-5 (v6.6): API returned WP_Error — set retry flag
                        update_option('vmpfence_scan_results_pending_push', true, false);
                    }
                } catch (\Exception $e) {
                    update_option('vmpfence_scan_results_pending_push', true, false);
                    if (class_exists('VMPFence_Secure_Logger')) {
                        VMPFence_Secure_Logger::log('error', 'SIEM scan results push failed after scheduled scan — will retry on next cron', array(
                            'error' => $e->getMessage(),
                        ));
                    }
                }
            }
            
            // Clear progress state when done (same as AJAX handler)
            delete_option('vmpfence_scan_progress');
            
            // Log scan success for monitoring (same as AJAX handler)
            if (class_exists('VMPFence_Scan_Monitor')) {
                VMPFence_Scan_Monitor::logLastSuccess();
            }
            return true;
            
        } catch (VMPFence_Scan_Duration_Exception $e) {
            // Handle scan duration limit exceeded (same as AJAX handler)
            
            VMPFence_Performance_Manager::log_performance_summary();
            delete_option('vmpfence_scan_progress');
            
            return true; // Still consider it successful even with timeout
            
        } catch (Exception $e) {
            // Clear progress state on error (same as AJAX handler)
            delete_option('vmpfence_scan_progress');
            
            // Log performance summary even on error (same as AJAX handler)
            VMPFence_Performance_Manager::log_performance_summary();
            
            return false;
        }
    }
    
    
    /**
     * Execute Server State Scanner (exact replica of AJAX handler logic)
     */
    private function executeServerStateScanner($scan_options, &$scan_results, &$debug_logs, $ipv4_interceptor = null) {
        if ($scan_options['scansEnabled_checkHowGetIPs'] || 
            $scan_options['scansEnabled_wafStatus'] || 
            $scan_options['scansEnabled_diskSpace'] || 
            $scan_options['scansEnabled_checkReadableConfig'] ||
            $scan_options['scansEnabled_oldVersions']) {
            
            // Performance: Check time limits and handle stage transition
            VMPFence_Performance_Manager::stage_transition('server_state', false);
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Checking server state...',
                'scan_progress' => 1,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-server-state-scanner.php';
            $server_scanner = new VMPFence_Server_State_Scanner();
            
            // Apply IPv4 interceptor to server scanner if enabled
            if ($ipv4_interceptor) {
                $server_scanner->set_ipv4_interceptor($ipv4_interceptor);
            }
            
            $server_results = $server_scanner->run_scan($scan_options);
            $scan_results['server_state'] = $server_results;
            
            // Store debug logs for later retrieval (append to existing logs)
            $debug_logs['server_state'] = $this->limitDebugLogSize($server_results['debug_log'] ?? []);
            
            return $server_results['results']['issues_found'] ?? 0;
        } else {
            // error_log('VMPFence: Server State Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute File Changes Scanner (exact replica of AJAX handler logic)
     */
    private function executeFileChangesScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_core'] || 
            $scan_options['scansEnabled_plugins'] || 
            $scan_options['scansEnabled_themes'] || 
            $scan_options['scansEnabled_coreUnknown'] ||
            $scan_options['other_scanOutside']) {
            
            // Performance: Check time limits and handle stage transition
            VMPFence_Performance_Manager::stage_transition('file_changes', false);
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Scanning file changes...',
                'scan_progress' => 2,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-file-scanner.php';
            $file_scanner = new VMPFence_File_Scanner();
            $file_results = $file_scanner->start_scan($scan_options);
            $scan_results['file_changes'] = $file_results;
            
            // Update debug logs with file scanner logs (limit size to prevent database errors)
            $debug_logs['file_changes'] = $this->limitDebugLogSize($file_results['debug_log'] ?? []);
            
            return $file_results['issues_found'] ?? 0;
        } else {
            // error_log('VMPFence: File Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute User Security Scanner (exact replica of AJAX handler logic)
     */
    private function executeUserSecurityScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_passwds'] || 
            $scan_options['scansEnabled_suspiciousAdminUsers']) {
            
            // Performance: Check time limits and handle stage transition
            VMPFence_Performance_Manager::stage_transition('user_security', false);
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Analyzing user account security...',
                'scan_progress' => 3,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-user-security-scanner.php';
            $user_security_scanner = new VMPFence_User_Security_Scanner();
            $user_security_results = $user_security_scanner->run_scan($scan_options);
            $scan_results['user_security'] = $user_security_results;
            
            // Update debug logs with user security scanner logs (limit size to prevent database errors)
            $debug_logs['user_security'] = $this->limitDebugLogSize($user_security_results['debug_log'] ?? []);
            
            return $user_security_results['issues_found'] ?? 0;
        } else {
            // error_log('VMPFence: User Security Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute Content Safety Scanner (exact replica of AJAX handler logic)
     */
    private function executeContentSafetyScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_posts'] || 
            $scan_options['scansEnabled_comments']) {
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Analyzing content safety violations...',
                'scan_progress' => 4,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-content-safety-scanner.php';
            $content_safety_scanner = new VMPFence_Content_Safety_Scanner();
            $content_safety_results = $content_safety_scanner->run_scan($scan_options);
            $scan_results['content_safety'] = $content_safety_results;
            
            // Update debug logs with content safety scanner logs (limit size to prevent database errors)
            $debug_logs['content_safety'] = $this->limitDebugLogSize($content_safety_results['debug_log'] ?? []);
            
            return $content_safety_results['issues_found'] ?? 0;
        } else {
            // error_log('VMPFence: Content Safety Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute Public Files Scanner (exact replica of AJAX handler logic)
     */
    private function executePublicFilesScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_checkReadableConfig'] || 
            $scan_options['scansEnabled_suspectedFiles']) {
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Checking for publicly accessible files...',
                'scan_progress' => 5,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-public-files-scanner.php';
            $public_files_scanner = new VMPFence_Public_Files_Scanner();
            $public_files_results = $public_files_scanner->scan($scan_options);
            $scan_results['public_files'] = [
                'issues_found' => count($public_files_results['issues']),
                'issues' => $public_files_results['issues'],
                'scanned_count' => $public_files_results['scanned_count']
            ];
            
            // Update debug logs with public files scanner logs (limit size to prevent database errors)
            $debug_logs['public_files'] = $this->limitDebugLogSize($public_files_results['debug_log'] ?? []);
            
            return count($public_files_results['issues']);
        } else {
            // error_log('VMPFence: Public Files Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute User & Option Audit Scanner (exact replica of AJAX handler logic)
     */
    private function executeUserOptionAuditScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_suspiciousOptions']) {
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Auditing suspicious options...',
                'scan_progress' => 6,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-user-option-audit-scanner.php';
            $user_audit_scanner = new VMPFence_User_Option_Audit_Scanner();
            $user_audit_results = $user_audit_scanner->scan($scan_options);
            $scan_results['user_audit'] = [
                'issues_found' => count($user_audit_results['issues']),
                'issues' => $user_audit_results['issues'],
                'scanned_count' => $user_audit_results['scanned_count']
            ];
            
            // Update debug logs with user audit scanner logs (limit size to prevent database errors)
            $debug_logs['user_audit'] = $this->limitDebugLogSize($user_audit_results['debug_log'] ?? []);
            
            return count($user_audit_results['issues']);
        } else {
            // error_log('VMPFence: User Option Audit Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute Vulnerability Scanner (exact replica of AJAX handler logic)
     */
    private function executeVulnerabilityScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_oldVersions']) {
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Scanning for known vulnerabilities...',
                'scan_progress' => 7,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-vulnerability-scanner.php';
            $vulnerability_scanner = new VMPFence_Vulnerability_Scanner();
            $vulnerability_results = $vulnerability_scanner->run_scan($scan_options);
            $scan_results['vulnerability'] = $vulnerability_results;
            
            // Update debug logs with vulnerability scanner logs
            $debug_logs['vulnerability'] = $this->limitDebugLogSize($vulnerability_results['debug_log'] ?? []);
            
            return $vulnerability_results['issues_found'] ?? 0;
        } else {
            // error_log('VMPFence: Vulnerability Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute Malware Scanner (exact replica of AJAX handler logic)
     */
    private function executeMalwareScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_malware'] || 
            $scan_options['scansEnabled_fileContents'] || 
            $scan_options['scansEnabled_fileContentsGSB'] ||
            $scan_options['scansEnabled_scanImages']) {
            
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Scanning for malware and suspicious code...',
                'scan_progress' => 8,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-malware-scanner.php';
            $malware_scanner = new VMPFence_Malware_Scanner();
            $malware_results = $malware_scanner->scan($scan_options);
            $scan_results['malware'] = $malware_results;
            
            // Update debug logs with malware scanner logs
            $debug_logs['malware'] = $this->limitDebugLogSize($malware_results['debug_log'] ?? []);
            
            return $malware_results['issues_found'] ?? 0;
        } else {
            // error_log('VMPFence: Malware Scanner skipped due to scan options');
            return 0;
        }
    }
    
    /**
     * Execute Domain Blocklist Scanner (exact replica of AJAX handler logic)
     */
    private function executeDomainBlocklistScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['scansEnabled_checkGSB']) {
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Checking domain blocklist status...',
                'scan_progress' => 9,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-reputation-scanner.php';
            $reputation_scanner = new VMPFence_Reputation_Scanner();
            $domain_results = $reputation_scanner->check_domain_blocklist();
            $scan_results['domain_blocklist'] = $domain_results;
            
            $debug_logs['domain_blocklist'] = $this->limitDebugLogSize($domain_results['debug_log'] ?? []);
            
            // Phase 3: Fire GSB blocklist detection email alert
            $issues_found = $domain_results['issues_found'] ?? 0;
            if ($issues_found > 0 && class_exists('VMPFence_Email_Alert_Hooks')) {
                $threat_types = array();
                if (!empty($domain_results['threats']) && is_array($domain_results['threats'])) {
                    foreach ($domain_results['threats'] as $threat) {
                        $threat_types[] = is_array($threat) && isset($threat['type']) ? $threat['type'] : (string) $threat;
                    }
                }
                $domain = isset($domain_results['domain']) ? $domain_results['domain'] : wp_parse_url(home_url(), PHP_URL_HOST);
                VMPFence_Email_Alert_Hooks::send_gsb_blocklist_alert($domain, $threat_types);
            }
            
            return $domain_results['issues_found'] ?? 0;
        }
        return 0;
    }
    
    /**
     * Execute Spamvertising Scanner (exact replica of AJAX handler logic)
     */
    private function executeSpamvertsingScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['spamvertizeCheck']) {
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Checking for spamvertising activity...',
                'scan_progress' => 10,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            if (!isset($reputation_scanner)) {
                require_once VMPFENCE_PLUGIN_DIR . 'includes/class-reputation-scanner.php';
                $reputation_scanner = new VMPFence_Reputation_Scanner();
            }
            $spamvertising_results = $reputation_scanner->check_spamvertising();
            $scan_results['spamvertising'] = $spamvertising_results;
            
            $debug_logs['spamvertising'] = $this->limitDebugLogSize($spamvertising_results['debug_log'] ?? []);
            
            return $spamvertising_results['issues_found'] ?? 0;
        }
        return 0;
    }
    
    /**
     * Execute IP Spam Scanner (exact replica of AJAX handler logic)
     */
    private function executeIPSpamScanner($scan_options, &$scan_results, &$debug_logs) {
        if ($scan_options['checkSpamIP']) {
            update_option('vmpfence_scan_progress', [
                'status' => 'scanning',
                'current_section' => 'Checking IP spam reputation...',
                'scan_progress' => 11,
                'total_scans' => 11,
                'last_update' => time(),
                'scheduled_scan' => true
            ]);
            
            if (!isset($reputation_scanner)) {
                require_once VMPFENCE_PLUGIN_DIR . 'includes/class-reputation-scanner.php';
                $reputation_scanner = new VMPFence_Reputation_Scanner();
            }
            $ip_spam_results = $reputation_scanner->check_ip_spam();
            $scan_results['ip_spam'] = $ip_spam_results;
            
            $debug_logs['ip_spam'] = $this->limitDebugLogSize($ip_spam_results['debug_log'] ?? []);
            
            return $ip_spam_results['issues_found'] ?? 0;
        }
        return 0;
    }
    
    /**
     * Process debug logs the same way as AJAX handler
     */
    private function processDebugLogs(&$debug_logs, &$scan_results) {
        
        // Calculate total size to debug the issue
        $total_entries = 0;
        foreach ($debug_logs as $key => $logs) {
            if (is_array($logs)) {
                $total_entries += count($logs);
            }
        }
        
        // Save debug logs separately to avoid size limits (same as AJAX handler)
        foreach ($debug_logs as $scan_type => $logs) {
            if ($scan_type !== 'timestamp' && is_array($logs)) {
                
                // Add timestamp to logs to ensure they're unique each scan
                $timestamped_logs = array_map(function($log) {
                    return '[' . current_time('H:i:s') . '] ' . $log;
                }, $logs);
                
                $save_result = update_option("vmpfence_debug_logs_{$scan_type}", $timestamped_logs);
                
                // Note: update_option returns false if the value hasn't changed, which is normal
                $status = $save_result ? 'SUCCESS' : 'NO_CHANGE';
            }
        }
        
        // Save a lightweight index of available debug logs (same as AJAX handler)
        $debug_index = [
            'timestamp' => current_time('mysql'),
            'available_logs' => array_keys(array_filter($debug_logs, function($key) { 
                return $key !== 'timestamp'; 
            }, ARRAY_FILTER_USE_KEY))
        ];
        $update_result = update_option('vmpfence_last_scan_debug_logs', $debug_index);
        
        // Remove debug logs from scan results before storing to prevent database packet size errors
        // Debug logs are already stored separately in vmpfence_last_scan_debug_logs
        foreach ($scan_results as $scan_type => $results) {
            if (isset($scan_results[$scan_type]['debug_log'])) {
                unset($scan_results[$scan_type]['debug_log']);
            }
        }
    }
    
    /**
     * Store scan completion data (same as AJAX handler)
     */
    private function storeScanCompletionData(&$scan_results, $total_issues) {
        // Get user statistics from scanners if available
        $user_security_results = $scan_results['user_security'] ?? [];
        $content_safety_results = $scan_results['content_safety'] ?? [];
        
        // Store scan completion data including user statistics (same as AJAX handler)
        $scan_completion_data = [
            'completed_time' => current_time('mysql'),
            'total_issues' => $total_issues,
            'scans_completed' => count($scan_results), // Dynamic count of actually executed scanners
            'users_checked' => $user_security_results['users_checked'] ?? 0,
            'content_items_checked' => $content_safety_results['content_items_checked'] ?? 0,
            'scan_results' => $scan_results
        ];
        update_option('vmpfence_last_scan_data', $scan_completion_data);
    }
    

    


    /**
     * Clear all previous scan data from database before starting new scan
     * This ensures that only results from currently selected scanners appear
     */
    private function clearAllPreviousScanData() {
        global $wpdb;
        
        // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from WordPress prefix
        $scan_issues_table = $wpdb->prefix . 'vmpfence_scan_issues';
        $scan_results_table = $wpdb->prefix . 'vmpfence_scan_results';
        
        // Check if tables exist
        $scan_issues_exists = $wpdb->get_var("SHOW TABLES LIKE '$scan_issues_table'");
        $scan_results_exists = $wpdb->get_var("SHOW TABLES LIKE '$scan_results_table'");
        
        if (!$scan_issues_exists || !$scan_results_exists) {
            return;
        }
        
        // Clear all previous scan issues (non-ignored only) from all scanners
        $deleted_issues = $wpdb->query("DELETE FROM {$scan_issues_table} WHERE status != 'ignored'");
        
        // Clear all previous scan results from all scanners
        $deleted_results = $wpdb->query("DELETE FROM {$scan_results_table}");
        // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    }

    /**
     * Clean up old backup files (30+ days old)
     * Same logic as AJAX handler
     */
    private function cleanupOldBackupFiles() {
        $upload_dir = wp_upload_dir();
        $backup_dir = $upload_dir['basedir'] . '/vmpfence-backups';
        
        if (!is_dir($backup_dir)) {
            return;
        }
        
        // Define cutoff time (30 days ago)
        $cutoff_time = time() - (30 * 24 * 60 * 60); // 30 days in seconds
        try {
            // Get all files in the backup directory
            $files = scandir($backup_dir);
            
            if ($files === false) {
                return;
            }
            
            $deleted_count = 0;
            $total_size_deleted = 0;
            
            foreach ($files as $file) {
                // Skip . and .. directory entries
                if ($file === '.' || $file === '..') {
                    continue;
                }
                
                $file_path = $backup_dir . '/' . $file;
                
                // Skip if it's not a file
                if (!is_file($file_path)) {
                    continue;
                }
                
                // Check file modification time
                $file_mtime = filemtime($file_path);
                
                if ($file_mtime !== false && $file_mtime < $cutoff_time) {
                    // File is older than 1 month, delete it
                    $file_size = filesize($file_path);
                    
                    if (unlink($file_path)) {
                        $deleted_count++;
                        $total_size_deleted += $file_size;
                        
                    }
                }
            }
            
            // Log summary if any files were deleted
            // if ($deleted_count > 0) {
            //     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional for debugging
            //     // error_log("VMPFence: Backup cleanup completed - Deleted {$deleted_count} old backup files, freed " . size_format($total_size_deleted) . " of space");
            // }
            
        } catch (Exception $e) {
            // Log error but don't fail the main operation
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional for debugging
            // error_log("VMPFence: Error during backup files cleanup: " . $e->getMessage());
        }
    }
    
    /**
     * Limit debug log size to prevent database errors (same as AJAX handler)
     *
     * @param array $debug_log Debug log entries
     * @return array Limited debug log
     */
    private function limitDebugLogSize($debug_log) {
        if (!is_array($debug_log)) {
            return [];
        }
        
        // Since we're saving debug logs separately now, we can keep more entries
        // Limit to maximum 200 entries per scanner
        $max_entries = 200;
        
        if (count($debug_log) > $max_entries) {
            // Keep first 100 and last 100 entries
            $first_half = array_slice($debug_log, 0, 100);
            $last_half = array_slice($debug_log, -100);
            $truncated_log = array_merge(
                $first_half,
                ['... [' . (count($debug_log) - 200) . ' entries truncated] ...'],
                $last_half
            );
            return $truncated_log;
        }
        
        return $debug_log;
    }

    /**
     * Determine which scan type to use for scheduled scan
     *
     * @return string Scan type
     */
    private function determineScheduledScanType() {
        // Get configured scan type
        $configuredType = VMPFence_Config::get('scanType', 'standard');
        
        // For manual mode, ALWAYS use configured type (ignore quick scan logic)
        if ($this->schedulingMode() === VMPFence_Constants::SCAN_SCHEDULING_MODE_MANUAL) {
            return $configuredType;
        }
        
        // For automatic mode, check if we should run a quick scan based on timing
        if ($this->shouldRunQuickScan()) {
            return 'limited';
        }
        
        // For automatic mode, use intelligent scan type selection
        $selectedType = $this->selectAutomaticScanType($configuredType);
        return $selectedType;
    }

    /**
     * Check if we should run a quick scan
     *
     * @return bool True if quick scan should run
     */
    private function shouldRunQuickScan() {
        $currentHour = intval(current_time('H'));
        
        // Quick scans during peak hours (9 AM - 6 PM)
        if ($currentHour >= 9 && $currentHour <= 18) {
            // 70% chance of quick scan during business hours
            return (rand(1, 100) <= 70);
        }
        
        // Full scans during off-hours (night time)
        return false;
    }

    /**
     * Select intelligent scan type for automatic mode
     *
     * @param string $baseType Base configured scan type
     * @return string Selected scan type
     */
    private function selectAutomaticScanType($baseType) {
        // Get last scan statistics
        $lastScanTime = VMPFence_Config::get('lastScheduledScanTime', 0);
        $lastScanDuration = VMPFence_Config::get('lastScheduledScanDuration', 0);
        
        // If last scan was too long ago (over 7 days), do full scan
        if (time() - $lastScanTime > (7 * 24 * 3600)) {
            return 'standard';
        }
        
        // If last scan took too long (over 10 minutes), consider lighter scan
        if ($lastScanDuration > 600) {
            return $baseType === 'high_sensitivity' ? 'standard' : $baseType;
        }
        
        // Use configured type
        return $baseType;
    }

    /**
     * Build scan options array based on scan type and configuration
     *
     * @param string $scanType Type of scan to perform
     * @return array Scan options
     */
    private function buildScanOptions($scanType) {
        $options = array();
        
        // Get base scan configuration
        $baseOptions = array(
            'scanWordPressCore',
            'scanPlugins', 
            'scanThemes',
            'scanUploads',
            'excludePattern',
            'maxMem',
            'scan_maxExecTime',
            'scan_maxIssues',
            'scan_maxDuration'
        );
        
        foreach ($baseOptions as $option) {
            $value = VMPFence_Config::get($option);
            if ($value !== null) {
                $options[$option] = $value;
            }
        }
        
        // Apply scan type specific modifications
        switch ($scanType) {
            case 'limited':
                // Limited scan - focus on critical areas only
                $options['scanUploads'] = true;
                $options['scanWordPressCore'] = false;
                $options['scan_maxDuration'] = min($options['scan_maxDuration'] ?? 300, 180); // Max 3 minutes
                break;
                
            case 'high_sensitivity':
                // High sensitivity - enable all options
                $options['scanWordPressCore'] = true;
                $options['scanPlugins'] = true;
                $options['scanThemes'] = true;
                $options['scanUploads'] = true;
                break;
                
            case 'standard':
            default:
                // Standard scan - balanced approach
                $options['scanWordPressCore'] = $options['scanWordPressCore'] ?? true;
                $options['scanPlugins'] = $options['scanPlugins'] ?? true;
                $options['scanThemes'] = $options['scanThemes'] ?? true;
                $options['scanUploads'] = $options['scanUploads'] ?? true;
                break;
        }
        
        // Add scheduled scan identifier
        $options['_scheduled_scan'] = true;
        $options['_scan_trigger'] = 'scheduled';
        
        return $options;
    }

    /**
     * Initialize appropriate scanner class
     *
     * @param string $scanType Type of scan
     * @return object|false Scanner instance or false on failure
     */
    private function initializeScanner($scanType) {
        // Ensure the malware scanner class is loaded
        if (!class_exists('VMPFence_Malware_Scanner')) {
            $scanner_file = plugin_dir_path(__FILE__) . 'class-malware-scanner.php';
            if (file_exists($scanner_file)) {
                require_once($scanner_file);
            } else {
                // error_log('VMPFence: Malware scanner file not found at: ' . $scanner_file);
                return false;
            }
        }
        
        // Double check if class is now available
        if (!class_exists('VMPFence_Malware_Scanner')) {
            return false;
        }
        
        try {
            $scanner = new VMPFence_Malware_Scanner();
            
            // Set scan type if scanner supports it
            if (method_exists($scanner, 'set_scan_type')) {
                $scanner->set_scan_type($scanType);
            }
            return $scanner;
            
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Record scan event for monitoring and statistics
     *
     * @param string $event Event type (started, completed, failed)
     * @param string $scanType Scan type
     * @param int $timestamp Event timestamp
     * @param array $metadata Additional event data
     */
    private function recordScanEvent($event, $scanType, $timestamp, $metadata = array()) {
        $eventData = array(
            'event' => $event,
            'scan_type' => $scanType,
            'timestamp' => $timestamp,
            'date' => gmdate('Y-m-d H:i:s', $timestamp),
            'scheduling_mode' => $this->schedulingMode(),
            'metadata' => $metadata
        );
        
        // Get existing scan events (keep last 50)
        $scanEvents = VMPFence_Config::get('scheduledScanEvents', array());
        array_unshift($scanEvents, $eventData);
        $scanEvents = array_slice($scanEvents, 0, 50);
        
        // Save updated events
        VMPFence_Config::set('scheduledScanEvents', $scanEvents);
        
        // Update event counters
        $this->updateEventCounters($event, $scanType);
    }

    /**
     * Update event counters for statistics
     *
     * @param string $event Event type
     * @param string $scanType Scan type
     */
    private function updateEventCounters($event, $scanType) {
        $counters = VMPFence_Config::get('scheduledScanCounters', array(
            'total_scans' => 0,
            'successful_scans' => 0,
            'failed_scans' => 0,
            'scan_types' => array()
        ));
        
        if ($event === 'started') {
            $counters['total_scans']++;
        } elseif ($event === 'completed') {
            $counters['successful_scans']++;
        } elseif ($event === 'failed') {
            $counters['failed_scans']++;
        }
        
        // Track scan type usage
        if (!isset($counters['scan_types'][$scanType])) {
            $counters['scan_types'][$scanType] = 0;
        }
        $counters['scan_types'][$scanType]++;
        
        VMPFence_Config::set('scheduledScanCounters', $counters);
    }

    /**
     * Handle scan completion notifications
     *
     * @param string $scanType Type of scan performed
     * @param bool $result Scan result
     * @param int $duration Scan duration in seconds
     */
    private function handleScanNotifications($scanType, $result, $duration) {
        // Check if notifications are enabled
        if (!VMPFence_Config::get('enableScheduledScanNotifications', false)) {
            return;
        }
        
        // Only send notifications for failed scans or if issues were found
        $found_issues = $this->scanFoundIssues();
        if (!$result || $found_issues) {
            $this->sendScanNotification($scanType, $result, $duration, $found_issues);
        }
    }

    /**
     * Check if the last scan found any issues
     *
     * @return bool True if issues were found
     */
    private function scanFoundIssues() {
        // This would typically check the scan results database
        // For now, we'll implement a basic check
        global $wpdb;
        
        // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from WordPress prefix
        $table_name = $wpdb->prefix . 'vmpfence_scan_results';
        if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name) {
            $recent_issues = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT COUNT(*) FROM $table_name WHERE scan_time > %d",
                    time() - 3600 // Last hour
                )
            );
            // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            return $recent_issues > 0;
        }
        // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        
        return false;
    }

    /**
     * Send scan completion notification
     *
     * @param string $scanType Type of scan
     * @param bool $result Scan result
     * @param int $duration Scan duration
     * @param bool $found_issues Whether issues were found
     */
    private function sendScanNotification($scanType, $result, $duration, $found_issues = false) {
        $status = $result ? 'Completed' : 'Failed';

        // Send via centralized Email Service (API → branded template)
        $context = array(
            'scan_type'             => ucfirst($scanType),
            'status'                => $status,
            'scan_duration_seconds' => $duration,
            'threats_found'         => $found_issues ? 1 : 0,
            'sent_at'               => current_time('Y-m-d H:i:s'),
            'dashboard_url'         => admin_url('admin.php?page=vmpfence-scan'),
        );

        $notificationEmail = VMPFence_Config::get('notification_email', get_option('admin_email'));

        VMPFence_Email_Service::send_alert('scan_completion', $context, array($notificationEmail));
    }

    /**
     * Update last scan statistics
     *
     * @param string $scanType Type of scan
     * @param bool $result Scan result
     * @param int $duration Scan duration
     */
    private function updateLastScanStats($scanType, $result, $duration) {
        VMPFence_Config::set('lastScheduledScanTime', time());
        VMPFence_Config::set('lastScheduledScanType', $scanType);
        VMPFence_Config::set('lastScheduledScanResult', $result ? 'success' : 'failed');
        VMPFence_Config::set('lastScheduledScanDuration', $duration);
        
        // Update success rate
        $this->updateScanSuccessRate($result);
    }

    /**
     * Update scan success rate statistics
     *
     * @param bool $result Latest scan result
     */
    private function updateScanSuccessRate($result) {
        $stats = VMPFence_Config::get('scheduledScanStats', array(
            'total_runs' => 0,
            'successful_runs' => 0,
            'success_rate' => 100.0
        ));
        
        $stats['total_runs']++;
        if ($result) {
            $stats['successful_runs']++;
        }
        
        $stats['success_rate'] = ($stats['successful_runs'] / $stats['total_runs']) * 100;
        
        VMPFence_Config::set('scheduledScanStats', $stats);
    }

    /**
     * Get schedule timezone
     *
     * @return string Timezone string
     */
    private function getScheduleTimezone() {
        $timezone = VMPFence_Config::get('scheduleTimezone', '');
        if (empty($timezone)) {
            $timezone = wp_timezone_string();
            VMPFence_Config::set('scheduleTimezone', $timezone);
        }
        return $timezone;
    }

    /**
     * Calculate optimal scan hour for automatic mode
     *
     * @param int $timestamp Target date timestamp
     * @return int Optimal hour (0-23)
     */
    private function calculateOptimalScanHour($timestamp) {
        // Get site-specific optimal hours based on traffic patterns
        $optimalHours = $this->getOptimalScanHours();
        
        // Use day of week to vary scan times
        $dayOfWeek = gmdate('w', $timestamp); // 0 = Sunday, 6 = Saturday
        
        // Select hour based on day with some randomization
        $siteHash = crc32(get_site_url());
        $dayHash = crc32($dayOfWeek . $siteHash);
        $index = abs($dayHash) % count($optimalHours);
        
        return $optimalHours[$index];
    }

    /**
     * Get optimal scan hours based on site activity patterns
     *
     * @return array Array of preferred hours (0-23)
     */
    private function getOptimalScanHours() {
        // Try to analyze site activity patterns for intelligent scheduling
        $activityHours = $this->analyzeSiteActivityPatterns();
        
        if (!empty($activityHours)) {
            return $activityHours;
        }
        
        // Fallback to default low-traffic hours
        return $this->generateFallbackAutomaticSchedule();
    }

    /**
     * Analyze site activity patterns to find low-traffic hours
     *
     * @return array Array of optimal hours based on site activity
     */
    private function analyzeSiteActivityPatterns() {
        // Check if we have WordPress statistics data
        $stats = $this->getWordPressActivityStats();
        
        if ($stats && !empty($stats['hourly_activity'])) {
            // Find hours with lowest activity
            $hourlyActivity = $stats['hourly_activity'];
            asort($hourlyActivity); // Sort by activity level (ascending)
            
            // Get the 4-6 hours with lowest activity
            $lowActivityHours = array_slice(array_keys($hourlyActivity), 0, 6, true);
            
            // Ensure we have at least some nighttime hours for safety
            $nightHours = [0, 1, 2, 3, 4, 5];
            $combined = array_unique(array_merge($lowActivityHours, $nightHours));
            
            return array_slice($combined, 0, 7); // Return top 7 optimal hours
        }
        
        return array(); // No data available
    }

    /**
     * Get WordPress activity statistics
     *
     * @return array|false Activity statistics or false if unavailable
     */
    private function getWordPressActivityStats() {
        // Check for popular analytics plugins that might have hourly data
        
        // Method 1: Check VMPFence's own traffic data if available
        if (class_exists('VMPFence_Traffic_Monitor')) {
            $traffic = VMPFence_Traffic_Monitor::getHourlyStats();
            if ($traffic) {
                return array('hourly_activity' => $traffic);
            }
        }
        
        // Method 2: Analyze WordPress comment timestamps for activity patterns
        $commentStats = $this->analyzeCommentActivityPatterns();
        if ($commentStats) {
            return array('hourly_activity' => $commentStats);
        }
        
        // Method 3: Analyze post publication patterns
        $postStats = $this->analyzePostActivityPatterns();
        if ($postStats) {
            return array('hourly_activity' => $postStats);
        }
        
        return false; // No activity data available
    }

    /**
     * Analyze comment activity patterns to determine site usage
     *
     * @return array|false Hourly activity data or false
     */
    private function analyzeCommentActivityPatterns() {
        global $wpdb;
        
        try {
            // Get comment timestamps from last 30 days
            // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- Static query with WordPress core table, no user input
            $sql = "SELECT HOUR(comment_date) as hour, COUNT(*) as count 
                    FROM {$wpdb->comments} 
                    WHERE comment_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
                    AND comment_approved = '1'
                    GROUP BY HOUR(comment_date)
                    ORDER BY hour";
            
            $results = $wpdb->get_results($sql, ARRAY_A);
            // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
            
            if ($results && count($results) >= 12) { // Need decent sample size
                $hourlyData = array_fill(0, 24, 0);
                
                foreach ($results as $row) {
                    $hourlyData[intval($row['hour'])] = intval($row['count']);
                }
                
                return $hourlyData;
            }
        } catch (Exception $e) {
            // Silently fail - this is just optimization
        }
        
        return false;
    }

    /**
     * Analyze post publication patterns for activity insights
     *
     * @return array|false Hourly activity data or false
     */
    private function analyzePostActivityPatterns() {
        global $wpdb;
        
        try {
            // Get post timestamps from last 90 days
            $sql = "SELECT HOUR(post_date) as hour, COUNT(*) as count 
                    FROM {$wpdb->posts} 
                    WHERE post_date >= DATE_SUB(NOW(), INTERVAL 90 DAY)
                    AND post_status = 'publish'
                    AND post_type IN ('post', 'page')
                    GROUP BY HOUR(post_date)
                    ORDER BY hour";
            
            // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- Static query with WordPress core table, no user input
            $results = $wpdb->get_results($sql, ARRAY_A);
            // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
            
            if ($results && count($results) >= 8) { // Need some sample size
                $hourlyData = array_fill(0, 24, 0);
                
                foreach ($results as $row) {
                    $hourlyData[intval($row['hour'])] = intval($row['count']);
                }
                
                return $hourlyData;
            }
        } catch (Exception $e) {
            // Silently fail - this is just optimization
        }
        
        return false;
    }

    /**
     * Generate fallback automatic schedule when optimization unavailable
     *
     * @return array Array of preferred hours for fallback scheduling
     */
    private function generateFallbackAutomaticSchedule() {
        // Default low-traffic hours based on typical website patterns
        // Prefer late night/early morning hours when traffic is lowest
        $fallbackHours = [2, 3, 4, 1, 5, 0, 23];
        
        // Add some variation based on site characteristics
        $siteUrl = get_site_url();
        $siteHash = crc32($siteUrl);
        
        // Adjust hours slightly based on site hash for distribution
        $adjustment = ($siteHash % 3) - 1; // -1, 0, or 1 hour adjustment
        
        $adjustedHours = array();
        foreach ($fallbackHours as $hour) {
            $newHour = ($hour + $adjustment + 24) % 24;
            $adjustedHours[] = $newHour;
        }
        
        return array_unique($adjustedHours);
    }

    /**
     * Save automatic schedule timestamps
     *
     * @param array $schedule Array of timestamps
     * @return bool Success status
     */
    public function saveAutomaticSchedule($schedule) {
        if (!is_array($schedule)) {
            return false;
        }
        
        // Store the generated schedule
        VMPFence_Config::set('vmpfenceScanSchedule', $schedule);
        VMPFence_Config::set('lastScheduleUpdate', time());
        
        // Also update the activity analysis cache timestamp
        VMPFence_Config::set('lastActivityAnalysis', time());
        
        return true;
    }

    /**
     * Initialize scheduling system during plugin activation
     *
     * @return bool Success status
     */
    public function initializeScheduling() {
        // Generate and schedule scans based on current mode
        return $this->scheduleAllScans();
    }

    /**
     * Ensure we have enough future scheduled scans (CRITICAL FIX)
     * 
     * This method regenerates the schedule when running low on future scans
     * to ensure continuous scheduled scanning operation.
     *
     * @return bool Whether schedule was regenerated
     */
    public function ensureFutureSchedules() {
        $scheduledEvents = VMPFence_Config::get('allScansScheduled', array());
        $currentTime = time();
        
        // Count how many future scans are scheduled
        $futureScans = 0;
        foreach ($scheduledEvents as $timestamp) {
            if ($timestamp > $currentTime) {
                $futureScans++;
            }
        }
        
        // If we have less than 3 days worth of scans, regenerate
        $minimumScans = 3; // Should have at least 3 future scans
        
        if ($futureScans < $minimumScans) {
            
            // Clear old scheduled events
            $this->unscheduleAllScans();
            
            // Generate and schedule new events
            $result = $this->scheduleAllScans();
            return $result;
        }
        
        return false; // No regeneration needed
    }

    /**
     * Generate and schedule automatic scans (called daily by cron)
     *
     * @return bool Success status
     */
    public function generateAndScheduleAutomaticScans() {
        // Only regenerate if in automatic mode
        if ($this->schedulingMode() !== VMPFence_Constants::SCAN_SCHEDULING_MODE_AUTOMATIC) {
            return false;
        }
        
        try {
            // Generate new optimized automatic schedule
            $schedule = $this->generateAutomaticSchedule();
            
            if (empty($schedule)) {
                return false;
            }
            
            // Schedule all the generated scan times
            return $this->scheduleAllScans();
            
        } catch (Exception $e) {
            // Fallback to basic daily scan
            $fallbackSchedule = $this->generateFallbackSchedule();
            $this->saveAutomaticSchedule($fallbackSchedule);
            
            return $this->scheduleAllScans();
        }
    }

    /**
     * Generate fallback schedule if automatic generation fails
     *
     * @return array Basic daily schedule
     */
    private function generateFallbackSchedule() {
        $schedule = array();
        $now = time();
        
        // Generate simple daily scans at 3 AM for next 14 days
        for ($day = 1; $day <= 14; $day++) {
            $targetDate = $now + ($day * 86400);
            $scanTime = mktime(3, 0, 0, gmdate('m', $targetDate), gmdate('d', $targetDate), gmdate('Y', $targetDate));
            
            if ($scanTime > $now) {
                $schedule[] = $scanTime;
            }
        }
        
        return $schedule;
    }

    /**
     * Get scheduled scan statistics for admin display
     *
     * @return array Statistics array
     */
    public function getScheduledScanStats() {
        $stats = VMPFence_Config::get('scheduledScanStats', array(
            'total_runs' => 0,
            'successful_runs' => 0,
            'success_rate' => 100.0
        ));
        
        $counters = VMPFence_Config::get('scheduledScanCounters', array(
            'total_scans' => 0,
            'successful_scans' => 0,
            'failed_scans' => 0,
            'scan_types' => array()
        ));
        
        return array(
            'total_scans' => $counters['total_scans'],
            'successful_scans' => $counters['successful_scans'],
            'failed_scans' => $counters['failed_scans'],
            'success_rate' => round($stats['success_rate'], 1),
            'last_scan_time' => VMPFence_Config::get('lastScheduledScanTime', 0),
            'last_scan_type' => VMPFence_Config::get('lastScheduledScanType', 'unknown'),
            'last_scan_result' => VMPFence_Config::get('lastScheduledScanResult', 'unknown'),
            'last_scan_duration' => VMPFence_Config::get('lastScheduledScanDuration', 0),
            'scan_type_usage' => $counters['scan_types'] ?? array()
        );
    }

    /**
     * Get recent scheduled scan events for monitoring
     *
     * @param int $limit Number of events to return
     * @return array Recent scan events
     */
    public function getRecentScanEvents($limit = 10) {
        $events = VMPFence_Config::get('scheduledScanEvents', array());
        return array_slice($events, 0, $limit);
    }

    /**
     * Check if scheduled scanning system is healthy
     *
     * @return array Health check results
     */
    public function getScheduledScanHealth() {
        $health = array(
            'overall_status' => 'healthy',
            'issues' => array(),
            'recommendations' => array()
        );
        
        // Check if scans are enabled
        if (!VMPFence_Config::get('scheduledScansEnabled', true)) {
            $health['overall_status'] = 'disabled';
            $health['issues'][] = 'Scheduled scans are disabled';
            return $health;
        }
        
        // Check for upcoming scans
        $upcomingScans = $this->getScheduledScanTimes(1);
        if (empty($upcomingScans)) {
            $health['overall_status'] = 'warning';
            $health['issues'][] = 'No upcoming scans scheduled';
            $health['recommendations'][] = 'Check scheduling configuration';
        }
        
        // Check last scan time
        $lastScanTime = VMPFence_Config::get('lastScheduledScanTime', 0);
        if ($lastScanTime > 0) {
            $daysSinceLastScan = (time() - $lastScanTime) / (24 * 3600);
            
            if ($daysSinceLastScan > 7) {
                $health['overall_status'] = 'warning';
                $health['issues'][] = 'Last scan was over 7 days ago';
                $health['recommendations'][] = 'Consider running a manual scan';
            }
        }
        
        // Check success rate
        $stats = $this->getScheduledScanStats();
        if ($stats['success_rate'] < 80 && $stats['total_scans'] > 5) {
            $health['overall_status'] = 'warning';
            $health['issues'][] = 'Low scan success rate (' . $stats['success_rate'] . '%)';
            $health['recommendations'][] = 'Check server resources and scan configuration';
        }
        
        // Check for failed scans
        $recentEvents = $this->getRecentScanEvents(5);
        $recentFailures = array_filter($recentEvents, function($event) {
            return $event['event'] === 'failed';
        });
        
        if (count($recentFailures) >= 3) {
            $health['overall_status'] = 'critical';
            $health['issues'][] = 'Multiple recent scan failures detected';
            $health['recommendations'][] = 'Review error logs and system resources';
        }
        
        return $health;
    }

    /**
     * Get scan execution performance metrics
     *
     * @return array Performance metrics
     */
    public function getScanPerformanceMetrics() {
        $events = $this->getRecentScanEvents(20);
        $completedScans = array_filter($events, function($event) {
            return $event['event'] === 'completed' && isset($event['metadata']['duration']);
        });
        
        if (empty($completedScans)) {
            return array(
                'average_duration' => 0,
                'min_duration' => 0,
                'max_duration' => 0,
                'total_scans_analyzed' => 0
            );
        }
        
        $durations = array_map(function($event) {
            return $event['metadata']['duration'];
        }, $completedScans);
        
        return array(
            'average_duration' => round(array_sum($durations) / count($durations), 1),
            'min_duration' => min($durations),
            'max_duration' => max($durations),
            'total_scans_analyzed' => count($completedScans)
        );
    }

    /**
     * Reset scheduled scan statistics
     *
     * @return bool Success status
     */
    public function resetScanStatistics() {
        VMPFence_Config::set('scheduledScanStats', array(
            'total_runs' => 0,
            'successful_runs' => 0,
            'success_rate' => 100.0
        ));
        
        VMPFence_Config::set('scheduledScanCounters', array(
            'total_scans' => 0,
            'successful_scans' => 0,
            'failed_scans' => 0,
            'scan_types' => array()
        ));
        
        VMPFence_Config::set('scheduledScanEvents', array());
        
        return true;
    }

    /**
     * Force execute a scheduled scan (for testing/manual trigger)
     *
     * @param string $scanType Optional scan type override
     * @return bool Execution result
     */
    public function forceExecuteScheduledScan($scanType = null) {
        // Temporarily override scan type if provided
        $originalScanType = null;
        if ($scanType) {
            $originalScanType = VMPFence_Config::get('scanType');
            VMPFence_Config::set('scanType', $scanType);
        }
        
        try {
            $result = $this->executeScheduledScan();
            
            // Restore original scan type
            if ($originalScanType !== null) {
                VMPFence_Config::set('scanType', $originalScanType);
            }
            
            return $result;
            
        } catch (Exception $e) {
            // Restore original scan type on error
            if ($originalScanType !== null) {
                VMPFence_Config::set('scanType', $originalScanType);
            }
            
            throw $e;
        }
    }

    /**
     * Handle forked scan execution from monitor
     */
    public static function handleMonitorForkScan($scan_type = 'standard') {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional for debugging
        // error_log("VMPFence Scheduler: Handling monitor fork scan - Type: $scan_type");
        
        // Set monitor context
        VMPFence_Config::set('scan_initiated_by_monitor', true);
        
        // Execute the scan
        $scheduler = self::shared();
        $result = $scheduler->executeScheduledScan();
        
        // Clear monitor flag
        VMPFence_Config::set('scan_initiated_by_monitor', false);
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional for debugging
        // error_log("VMPFence Scheduler: Monitor fork scan completed with result: " . ($result ? 'success' : 'failure'));
        return $result;
    }
}