<?php
/**
 * VMPFence Scan Monitor Class
 *
 *
 * @package VMPFence
 * @since 1.0.0
 */

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

class VMPFence_Scan_Monitor {

    const CRON_INTERVAL_NAME = 'vmpfence_scan_monitor_interval';
    const CRON_INTERVAL_AMOUNT = 60; // Seconds
    const CRON_HOOK = 'vmpfence_scan_monitor';

    const CONFIG_LAST_ATTEMPT = 'scanMonitorLastAttempt';
    const CONFIG_LAST_ATTEMPT_WAS_FORK = 'scanMonitorLastAttemptWasFork';
    const CONFIG_LAST_ATTEMPT_MODE = 'scanMonitorLastAttemptMode';
    const CONFIG_LAST_SUCCESS = 'scanMonitorLastSuccess';
    const CONFIG_MAX_RESUME_ATTEMPTS = 'scan_max_resume_attempts';
    const CONFIG_REMAINING_RESUME_ATTEMPTS = 'scanMonitorRemainingResumeAttempts';

    const DEFAULT_RESUME_ATTEMPTS = 2;
    const MAX_RESUME_ATTEMPTS = 5;
    const SCAN_START_TIMEOUT = 30; // Seconds

    /**
     * Begin monitoring scans
     */
    public static function beginMonitoring() {
        if (wp_next_scheduled(self::CRON_HOOK)) {
            return;
        }
        wp_schedule_event(time(), self::CRON_INTERVAL_NAME, self::CRON_HOOK);
    }

    /**
     * End monitoring scans
     */
    public static function endMonitoring() {
        $timestamp = wp_next_scheduled(self::CRON_HOOK);
        if ($timestamp !== false)
            wp_unschedule_event($timestamp, self::CRON_HOOK);
    }

    /**
     * Validate resume attempts value
     */
    public static function validateResumeAttempts($attempts, &$valid = null) {
        if ($attempts < 0 || $attempts > self::MAX_RESUME_ATTEMPTS) {
            $valid = false;
            return self::DEFAULT_RESUME_ATTEMPTS;
        }
        $valid = true;
        return $attempts;
    }

    /**
     * Set remaining resume attempts
     */
    private static function setRemainingResumeAttempts($attempts) {
        VMPFence_Config::set(self::CONFIG_REMAINING_RESUME_ATTEMPTS, $attempts);
    }

    /**
     * Get configured resume attempts from settings
     */
    public static function getConfiguredResumeAttempts() {
        $attempts = (int) VMPFence_Config::get(self::CONFIG_MAX_RESUME_ATTEMPTS, self::DEFAULT_RESUME_ATTEMPTS);
        return self::validateResumeAttempts($attempts);
    }

    /**
     * Reset resume attempt counter to max configured value
     */
    private static function resetResumeAttemptCounter() {
        $attempts = self::getConfiguredResumeAttempts();
        self::setRemainingResumeAttempts($attempts);
        return $attempts;
    }

    /**
     * Get remaining resume attempts
     */
    private static function getRemainingResumeAttempts() {
        $attempts = (int) VMPFence_Config::get(self::CONFIG_REMAINING_RESUME_ATTEMPTS, 0);
        return self::validateResumeAttempts($attempts);
    }

    /**
     * Handle scan start - initialize monitoring
     */
    public static function handleScanStart($mode) {
        VMPFence_Config::set(self::CONFIG_LAST_ATTEMPT_MODE, $mode);
        $maxAttempts = self::resetResumeAttemptCounter();
        if ($maxAttempts > 0)
            self::beginMonitoring();
    }

    /**
     * Handle stage start - reset counter for forked processes
     */
    public static function handleStageStart($fork) {
        if ($fork)
            self::resetResumeAttemptCounter();
    }

    /**
     * Monitor scan progress and resume if stalled
     */
    public static function monitorScan() {
        
        // Monitor scheduled scans
        self::monitorScheduledScans();
        
        $remainingAttempts = self::getRemainingResumeAttempts();
        
        if ($remainingAttempts > 0) {
            $now = time();
            $lastAttempt = VMPFence_Config::get(self::CONFIG_LAST_ATTEMPT);
            
            // Check if scan has been running for more than timeout period
            if ($lastAttempt === null || $now - $lastAttempt < self::SCAN_START_TIMEOUT) {
                return;
            }
                
            $lastSuccess = VMPFence_Config::get(self::CONFIG_LAST_SUCCESS);
            
            self::setRemainingResumeAttempts(--$remainingAttempts);
            
            // Only resume if there was no recent successful activity
            if ($lastSuccess === null || $lastAttempt > $lastSuccess) {
                self::resumeScan();
            } else {
                // error_log("VMPFence Monitor: Scan not stalled - recent success detected");
            }
        }
        else {
            self::endMonitoring();
        }
    }

    /**
     * Resume scan execution
     */
    private static function resumeScan() {
        $mode = VMPFence_Config::get(self::CONFIG_LAST_ATTEMPT_MODE);
        if (!self::isValidScanType($mode))
            $mode = false;
            
        // Trigger scan restart via AJAX handler
        self::startScan(VMPFence_Config::get(self::CONFIG_LAST_ATTEMPT_WAS_FORK, false), $mode, true);
    }

    /**
     * Check if scan type is valid
     */
    private static function isValidScanType($mode) {
        $valid_types = array('limited', 'standard', 'high_sensitivity', 'custom');
        return in_array($mode, $valid_types);
    }

    /**
     * Start scan - Reuses existing scheduler logic with minimal changes
     */
    private static function startScan($fork, $mode, $isResume = false) {
        try {
            // Log the scan resume attempt
            // error_log("VMPFence: Scan resumed - Fork: " . ($fork ? 'yes' : 'no') . ", Mode: {$mode}, Resume: " . ($isResume ? 'yes' : 'no'));
            
            // Validate scan mode
            if (!self::isValidScanType($mode)) {
                // error_log("VMPFence: Invalid scan mode '{$mode}' for resume, using 'standard' instead");
                $mode = 'standard';
            }
            
            // Set monitor flags for tracking
            VMPFence_Config::set('scan_initiated_by_monitor', true);
            VMPFence_Config::set('scan_is_resume', $isResume);
            
            // Temporarily override scan type and force scans enabled for resume
            $original_scan_type = VMPFence_Config::get('scanType', 'standard');
            $original_scans_enabled = VMPFence_Config::get('scheduledScansEnabled', true);
            
            if ($mode && $mode !== 'false') {
                VMPFence_Config::set('scanType', $mode);
            }
            VMPFence_Config::set('scheduledScansEnabled', true); // Force enable for resume
            
            // Use existing scheduler to execute the scan (minimal code reuse)
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-scan-scheduler.php';
            $scheduler = VMPFence_Scan_Scheduler::shared();
            
            // Execute scan using scheduler's proven logic
            $success = $scheduler->executeScheduledScan();
            
            if ($success) {
                // error_log("VMPFence: Scan resume completed successfully");
                self::logLastSuccess();
                self::resetResumeAttemptCounter();
                return true;
            } else {
                // error_log("VMPFence: Scan resume failed");
                return false;
            }
            
        } catch (Exception $e) {
            // error_log("VMPFence: Exception during scan resume: " . $e->getMessage());
            return false;
            
        } finally {
            // Always clean up flags and restore original values
            VMPFence_Config::set('scan_initiated_by_monitor', false);
            VMPFence_Config::set('scan_is_resume', false);
            if (isset($original_scan_type)) {
                VMPFence_Config::set('scanType', $original_scan_type);
            }
            if (isset($original_scans_enabled)) {
                VMPFence_Config::set('scheduledScansEnabled', $original_scans_enabled);
            }
        }
    }

    /**
     * Log timestamp for monitoring
     */
    private static function logTimestamp($key) {
        VMPFence_Config::set($key, time());
    }

    /**
     * Log last attempt
     */
    public static function logLastAttempt($fork) {
        self::logTimestamp(self::CONFIG_LAST_ATTEMPT);
        VMPFence_Config::set(self::CONFIG_LAST_ATTEMPT_WAS_FORK, $fork);
    }

    /**
     * Log last success
     */
    public static function logLastSuccess() {
        self::logTimestamp(self::CONFIG_LAST_SUCCESS);
    }

    /**
     * Monitor scheduled scans
     */
    public static function monitorScheduledScans() {
        // Load the scheduler class
        require_once VMPFENCE_PLUGIN_DIR . 'includes/class-scan-scheduler.php';
        
        $scheduler = VMPFence_Scan_Scheduler::shared();
        $next_scan = $scheduler->nextScheduledScanTime();
        $current_time = time();
        
        self::checkForStuckScheduledScans($scheduler);
        self::monitorScanPerformance($scheduler);
        self::checkScheduleHealth($scheduler);
        self::checkRestoreOriginalScanType();
        
        // Original basic monitoring logic
        if ($next_scan && $next_scan <= $current_time) {
            $time_overdue = $current_time - $next_scan;
            
            // If scan is more than 5 minutes overdue, consider it missed
            if ($time_overdue > 300) {
                self::handleScheduledScanFailure($next_scan, $time_overdue);
            }
        }
        
        // Log monitoring activity
        VMPFence_Config::set('lastScheduledScanMonitor', $current_time);
    }

    /**
     * Handle failed scheduled scans
     */
    public static function handleScheduledScanFailure($missed_scan_time, $time_overdue) {
        $failure_count = VMPFence_Config::get('scheduledScanFailureCount', 0);
        $failure_count++;
        
        // Update failure tracking
        VMPFence_Config::set('scheduledScanFailureCount', $failure_count);
        VMPFence_Config::set('lastScheduledScanFailure', time());
        
        // If multiple failures, try to reschedule
        if ($failure_count >= 3) {
            require_once VMPFENCE_PLUGIN_DIR . 'includes/class-scan-scheduler.php';
            $scheduler = VMPFence_Scan_Scheduler::shared();
            
            // Attempt to reschedule all scans
            if ($scheduler->scheduleAllScans()) {
                VMPFence_Config::set('scheduledScanFailureCount', 0); // Reset counter on successful reschedule
            }
        }
    }

    /**
     * Register cron interval
     */
    public static function registerCronInterval($schedules) {
        if (!array_key_exists(self::CRON_INTERVAL_NAME, $schedules)) {
            $schedules[self::CRON_INTERVAL_NAME] = array(
                'interval' => self::CRON_INTERVAL_AMOUNT,
                'display' => 'VMPFence Scan Monitor'
            );
        }
        return $schedules;
    }

    /**
     * Register WordPress actions
     */
    public static function registerActions() {
        add_filter('cron_schedules', array(self::class, 'registerCronInterval'));
        add_action(self::CRON_HOOK, array(self::class, 'monitorScan'));
    }

    /**
     * Check for stuck or failed scheduled scans
     *
     * @param VMPFence_Scan_Scheduler $scheduler
     */
    private static function checkForStuckScheduledScans($scheduler) {
        $lastScanTime = VMPFence_Config::get('lastScheduledScanTime', 0);
        $nextScanTime = $scheduler->nextScheduledScanTime();
        $now = time();
        
        // If there's a next scan time but it's overdue by more than 2 hours
        if ($nextScanTime && $nextScanTime < ($now - 7200)) {
            // Log the issue
            // error_log('VMPFence Monitor: Scheduled scan appears to be stuck or missed - Next scan was due: ' . 
                    //  gmdate('Y-m-d H:i:s', $nextScanTime));
            
            // Attempt to reschedule
            self::handleStuckScheduledScan($scheduler);
        }
        
        // Check for scans that haven't run in too long
        if ($lastScanTime > 0) {
            $daysSinceLastScan = ($now - $lastScanTime) / (24 * 3600);
            
            if ($daysSinceLastScan > 10) {
                // error_log('VMPFence Monitor: No scheduled scans have run in ' . round($daysSinceLastScan, 1) . ' days');
                
                // Try to restart scheduling
                $scheduler->scheduleAllScans();
            }
        }
    }

    /**
     * Handle stuck scheduled scan
     *
     * @param VMPFence_Scan_Scheduler $scheduler
     */
    private static function handleStuckScheduledScan($scheduler) {
        // Clear old schedules
        $scheduler->unscheduleAllScans();
        
        // Regenerate schedule
        $success = $scheduler->scheduleAllScans();
        
        // if ($success) {
        //     error_log('VMPFence Monitor: Successfully rescheduled stuck scans');
        // } else {
        //     error_log('VMPFence Monitor: Failed to reschedule stuck scans');
        // }
    }

    /**
     * Monitor scan performance and alert on issues
     *
     * @param VMPFence_Scan_Scheduler $scheduler
     */
    private static function monitorScanPerformance($scheduler) {
        $performance = $scheduler->getScanPerformanceMetrics();
        
        // Alert if average scan time is too high (over 20 minutes)
        // if ($performance['average_duration'] > 1200 && $performance['total_scans_analyzed'] >= 3) {
        //     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using error_log for monitoring alerts
        //     error_log('VMPFence Monitor: Scheduled scans are running slowly - Average duration: ' . 
        //              round($performance['average_duration'] / 60, 1) . ' minutes');
        // }
        
        // Alert if max scan time is extremely high (over 1 hour)
        // if ($performance['max_duration'] > 3600) {
        //     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using error_log for monitoring alerts
        //     error_log('VMPFence Monitor: Detected very long scan duration: ' . 
        //              round($performance['max_duration'] / 60, 1) . ' minutes');
        // }
    }

    /**
     * Check overall schedule health
     *
     * @param VMPFence_Scan_Scheduler $scheduler
     */
    private static function checkScheduleHealth($scheduler) {
        $health = $scheduler->getScheduledScanHealth();
        
        // if ($health['overall_status'] === 'critical') {
        //     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using error_log for monitoring alerts
        //     error_log('VMPFence Monitor: Critical scheduled scan health issues detected: ' . 
        //              implode(', ', $health['issues']));
        // } elseif ($health['overall_status'] === 'warning' && !empty($health['issues'])) {
        //     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using error_log for monitoring alerts
        //     error_log('VMPFence Monitor: Scheduled scan warnings: ' . 
        //              implode(', ', $health['issues']));
        // }
    }

    /**
     * Check if we should restore original scan type after temporary limited mode
     */
    public static function checkRestoreOriginalScanType() {
        if (!VMPFence_Config::get('temporaryLimitedModeActive', false)) {
            return;
        }
        
        // Check if limited scans have been successful
        $recentEvents = VMPFence_Config::get('scheduledScanEvents', array());
        $recentLimitedScans = array_filter(array_slice($recentEvents, 0, 5), function($event) {
            return $event['event'] === 'completed' && 
                   isset($event['metadata']['scan_type']) && 
                   $event['metadata']['scan_type'] === 'limited';
        });
        
        // If we have 2+ successful limited scans, try to restore original type
        if (count($recentLimitedScans) >= 2) {
            $originalType = VMPFence_Config::get('originalScanTypeBeforeFailures', 'standard');
            
            VMPFence_Config::set('scanType', $originalType);
            VMPFence_Config::set('temporaryLimitedModeActive', false);
            VMPFence_Config::set('originalScanTypeBeforeFailures', '');
        }
    }

    /**
     * Public interface for starting scan recovery (used by external components)
     */
    public static function startScanRecovery($scan_type = 'standard', $fork = false) {
        // error_log("VMPFence Monitor: External scan recovery request - Type: $scan_type, Fork: " . ($fork ? 'true' : 'false'));
        
        // Apply scan type configuration if available
        if (class_exists('VMPFence_Scan_Config')) {
            $config = new VMPFence_Scan_Config();
            if (method_exists($config, 'applyScanTypePreset')) {
                $config->applyScanTypePreset($scan_type);
            }
        }
        
        // Handle fork parameter for background execution
        if ($fork && function_exists('wp_schedule_single_event')) {
            wp_schedule_single_event(time(), 'vmpfence_monitor_fork_scan', array($scan_type));
            // error_log("VMPFence Monitor: Scan scheduled for background execution");
            return true;
        }
        
        // Set up WordPress context for scan execution
        if (!defined('WP_ADMIN')) {
            define('WP_ADMIN', true);
        }
        
        // Use internal startScan method with proper parameters
        return self::startScan($fork, $scan_type, false);
    }

    /**
     * Handle plugin deactivation
     */
    public static function handleDeactivation() {
        self::endMonitoring();
    }
}