<?php
/**
 * Sentinel Memory Monitor
 * 
 * Comprehensive memory monitoring system that provides:
 * - Early warning alerts (before problems occur)
 * - Memory usage tracking and trends
 * - Automatic optimization suggestions
 * - Performance insights
 * - Memory leak detection
 * - User-friendly notifications
 */

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

class Sentinel_Memory_Monitor {
    
    private $settings;
    private $memory_history = array();
    private $memory_hogs = array();
    
    public function __construct() {
        $this->settings = get_option('sentinel_log_management', array());
        $this->init_hooks();
    }
    
    private function init_hooks() {
        if (empty($this->settings['memory_monitoring_enabled'])) {
            return;
        }
        
        // Monitor memory throughout the request lifecycle
        add_action('init', array($this, 'start_memory_monitoring'), 1);
        add_action('wp_loaded', array($this, 'check_memory_milestone'), 10);
        add_action('template_redirect', array($this, 'check_memory_milestone'), 10);
        add_action('shutdown', array($this, 'end_memory_monitoring'), 999);
        
        // Monitor specific high-impact operations
        add_action('plugins_loaded', array($this, 'monitor_plugins_memory'));
        add_action('wp_head', array($this, 'monitor_theme_memory'));
        add_action('wp_enqueue_scripts', array($this, 'monitor_scripts_memory'));
        
        // Admin dashboard integration
        add_action('admin_notices', array($this, 'show_memory_warnings'));
        add_action('wp_ajax_sentinel_memory_insights', array($this, 'ajax_memory_insights'));
    }
    
    /**
     * Start monitoring memory usage for this request
     */
    public function start_memory_monitoring() {
        $this->memory_history['start'] = array(
            'usage' => memory_get_usage(true),
            'peak' => memory_get_peak_usage(true),
            'time' => microtime(true),
            'hook' => 'init'
        );
        
        // Store baseline memory usage
        $this->track_memory_usage('request_start');
    }
    
    /**
     * Check memory at various milestones
     */
    public function check_memory_milestone() {
        $current_hook = current_filter();
        $this->track_memory_usage($current_hook);
        
        // Check if we're approaching dangerous levels
        $this->check_memory_thresholds();
    }
    
    /**
     * End memory monitoring and analyze the request
     */
    public function end_memory_monitoring() {
        $this->memory_history['end'] = array(
            'usage' => memory_get_usage(true),
            'peak' => memory_get_peak_usage(true),
            'time' => microtime(true),
            'hook' => 'shutdown'
        );
        
        // Analyze memory usage patterns
        $this->analyze_memory_usage();
        
        // Check for memory leaks
        $this->detect_memory_leaks();
    }
    
    /**
     * Track memory usage at specific points
     */
    private function track_memory_usage($checkpoint) {
        $current_usage = memory_get_usage(true);
        $current_peak = memory_get_peak_usage(true);
        
        $this->memory_history[$checkpoint] = array(
            'usage' => $current_usage,
            'peak' => $current_peak,
            'time' => microtime(true),
            'hook' => $checkpoint
        );
        
        // Calculate memory increase since last checkpoint
        $previous_checkpoints = array_keys($this->memory_history);
        if (count($previous_checkpoints) > 1) {
            $last_checkpoint = $previous_checkpoints[count($previous_checkpoints) - 2];
            $memory_increase = $current_usage - $this->memory_history[$last_checkpoint]['usage'];
            
            if ($memory_increase > 0) {
                $this->memory_history[$checkpoint]['increase'] = $memory_increase;
                $this->memory_history[$checkpoint]['increase_formatted'] = $this->format_bytes($memory_increase);
            }
        }
    }
    
    /**
     * Check memory thresholds and trigger appropriate actions
     */
    private function check_memory_thresholds() {
        $memory_limit = $this->get_memory_limit_bytes();
        $current_usage = memory_get_usage(true);
        $usage_percent = ($current_usage / $memory_limit) * 100;
        
        $threshold = intval($this->settings['memory_limit_threshold'] ?? 80);
        
        // Early warning at 60% of threshold
        if ($usage_percent >= ($threshold * 0.6) && $usage_percent < ($threshold * 0.8)) {
            $this->log_memory_warning('early_warning', $usage_percent, $current_usage, $memory_limit);
        }
        
        // Warning at 80% of threshold
        elseif ($usage_percent >= ($threshold * 0.8) && $usage_percent < $threshold) {
            $this->log_memory_warning('warning', $usage_percent, $current_usage, $memory_limit);
        }
        
        // Critical at threshold
        elseif ($usage_percent >= $threshold) {
            $this->log_memory_warning('critical', $usage_percent, $current_usage, $memory_limit);
            
            // Trigger automatic optimization if enabled
            $this->trigger_memory_optimization();
        }
    }
    
    /**
     * Log memory warnings with actionable insights
     */
    private function log_memory_warning($severity, $usage_percent, $current_usage, $memory_limit) {
        $recommendations = $this->generate_memory_recommendations($usage_percent, $current_usage);
        
        $data = array(
            'severity' => $severity,
            'memory_usage' => $current_usage,
            'memory_usage_formatted' => $this->format_bytes($current_usage),
            'memory_limit' => $memory_limit,
            'memory_limit_formatted' => $this->format_bytes($memory_limit),
            'usage_percent' => round($usage_percent, 2),
            'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
            'recommendations' => $recommendations,
            'memory_hogs' => $this->identify_memory_hogs(),
            'timestamp' => current_time('mysql'),
            'can_optimize' => $this->can_auto_optimize()
        );
        
        // Log the event
        if (function_exists('sentinel_log_event')) {
            sentinel_log_event('memory_' . $severity, $data);
        }
        
        // Store for admin notices
        $this->store_memory_alert($severity, $data);
    }
    
    /**
     * Generate specific recommendations based on memory usage
     */
    private function generate_memory_recommendations($usage_percent, $current_usage) {
        $recommendations = array();
        
        if ($usage_percent > 90) {
            $recommendations[] = array(
                'type' => 'critical',
                'icon' => '🚨',
                'title' => 'Immediate Action Required',
                'description' => 'Your site is using over 90% of available memory. This will cause crashes.',
                'actions' => array(
                    'Increase PHP memory limit to at least ' . $this->format_bytes($current_usage * 2),
                    'Deactivate unnecessary plugins',
                    'Review recent changes that might be causing memory issues'
                )
            );
        } elseif ($usage_percent > 80) {
            $recommendations[] = array(
                'type' => 'warning',
                'icon' => '⚠️',
                'title' => 'Memory Usage High',
                'description' => 'Your site is approaching memory limits. Consider optimization.',
                'actions' => array(
                    'Increase PHP memory limit to ' . $this->format_bytes($current_usage * 1.5),
                    'Review and optimize plugins',
                    'Consider upgrading hosting plan'
                )
            );
        } elseif ($usage_percent > 60) {
            $recommendations[] = array(
                'type' => 'info',
                'icon' => '💡',
                'title' => 'Optimization Opportunity',
                'description' => 'Memory usage is increasing. Now is a good time to optimize.',
                'actions' => array(
                    'Review plugin performance',
                    'Optimize database queries',
                    'Consider caching solutions'
                )
            );
        }
        
        // Add specific recommendations based on memory hogs
        $memory_hogs = $this->identify_memory_hogs();
        if (!empty($memory_hogs)) {
            $recommendations[] = array(
                'type' => 'optimization',
                'icon' => '🔧',
                'title' => 'Memory Hogs Detected',
                'description' => 'We found operations consuming significant memory.',
                'actions' => array_map(function($hog) {
                    return "Optimize {$hog['operation']} (using {$hog['memory_formatted']})";
                }, array_slice($memory_hogs, 0, 3))
            );
        }
        
        return $recommendations;
    }
    
    /**
     * Identify operations that consume the most memory
     */
    private function identify_memory_hogs() {
        $hogs = array();
        
        foreach ($this->memory_history as $checkpoint => $data) {
            if (isset($data['increase']) && $data['increase'] > 1048576) { // > 1MB
                $hogs[] = array(
                    'operation' => $checkpoint,
                    'memory' => $data['increase'],
                    'memory_formatted' => $this->format_bytes($data['increase']),
                    'hook' => $data['hook']
                );
            }
        }
        
        // Sort by memory usage
        usort($hogs, function($a, $b) {
            return $b['memory'] - $a['memory'];
        });
        
        return $hogs;
    }
    
    /**
     * Show memory warnings in admin dashboard
     */
    public function show_memory_warnings() {
        if (!current_user_can('manage_options')) {
            return;
        }
        
        $alerts = get_transient('sentinel_memory_alerts');
        if (empty($alerts)) {
            return;
        }
        
        foreach ($alerts as $alert) {
            $class = 'notice-' . ($alert['severity'] === 'critical' ? 'error' : 'warning');
            echo '<div class="notice ' . $class . ' is-dismissible sentinel-memory-alert">';
            echo '<div style="display: flex; align-items: center; gap: 10px;">';
            echo '<div style="font-size: 24px;">🧠</div>';
            echo '<div>';
            echo '<h4 style="margin: 0;">Memory Usage Alert</h4>';
            echo '<p><strong>' . round($alert['usage_percent'], 1) . '%</strong> of memory limit used ';
            echo '(' . $alert['memory_usage_formatted'] . ' / ' . $alert['memory_limit_formatted'] . ')</p>';
            
            if (!empty($alert['recommendations'])) {
                echo '<div style="margin-top: 10px;">';
                echo '<strong>Recommendations:</strong>';
                foreach ($alert['recommendations'] as $rec) {
                    echo '<div style="margin: 5px 0; padding: 8px; background: #f0f0f0; border-radius: 4px;">';
                    echo '<div style="font-weight: bold;">' . $rec['icon'] . ' ' . $rec['title'] . '</div>';
                    echo '<div style="font-size: 12px; color: #666;">' . $rec['description'] . '</div>';
                    if (!empty($rec['actions'])) {
                        echo '<ul style="margin: 5px 0; padding-left: 20px;">';
                        foreach ($rec['actions'] as $action) {
                            echo '<li style="font-size: 12px;">' . $action . '</li>';
                        }
                        echo '</ul>';
                    }
                    echo '</div>';
                }
                echo '</div>';
            }
            
            echo '<p style="margin-top: 10px;"><a href="' . admin_url('admin.php?page=sentinel-settings&tab=performance') . '" class="button">Memory Settings</a></p>';
            echo '</div>';
            echo '</div>';
            echo '</div>';
        }
        
        // Clear alerts after showing
        delete_transient('sentinel_memory_alerts');
    }
    
    /**
     * Store memory alert for admin display
     */
    private function store_memory_alert($severity, $data) {
        $alerts = get_transient('sentinel_memory_alerts') ?: array();
        $alerts[] = array(
            'severity' => $severity,
            'usage_percent' => $data['usage_percent'],
            'memory_usage_formatted' => $data['memory_usage_formatted'],
            'memory_limit_formatted' => $data['memory_limit_formatted'],
            'recommendations' => $data['recommendations'],
            'timestamp' => $data['timestamp']
        );
        
        // Keep only last 5 alerts
        $alerts = array_slice($alerts, -5);
        
        set_transient('sentinel_memory_alerts', $alerts, 3600); // 1 hour
    }
    
    /**
     * Analyze memory usage patterns
     */
    private function analyze_memory_usage() {
        if (empty($this->memory_history)) {
            return;
        }
        
        $start = $this->memory_history['start'] ?? null;
        $end = $this->memory_history['end'] ?? null;
        
        if (!$start || !$end) {
            return;
        }
        
        $total_increase = $end['usage'] - $start['usage'];
        $peak_usage = $end['peak'];
        $duration = $end['time'] - $start['time'];
        
        // Store analysis results
        $analysis = array(
            'total_memory_increase' => $total_increase,
            'peak_memory_usage' => $peak_usage,
            'request_duration' => $duration,
            'memory_efficiency' => $total_increase > 0 ? ($peak_usage / $total_increase) : 0,
            'checkpoints' => count($this->memory_history),
            'memory_hogs' => $this->identify_memory_hogs()
        );
        
        // Store for trending analysis
        $this->store_memory_analysis($analysis);
    }
    
    /**
     * Store memory analysis for trending
     */
    private function store_memory_analysis($analysis) {
        $history = get_option('sentinel_memory_analysis_history', array());
        
        $history[] = array(
            'timestamp' => current_time('mysql'),
            'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown',
            'analysis' => $analysis
        );
        
        // Keep only last 100 entries
        $history = array_slice($history, -100);
        
        update_option('sentinel_memory_analysis_history', $history, false);
    }
    
    /**
     * Detect potential memory leaks
     */
    private function detect_memory_leaks() {
        $history = get_option('sentinel_memory_analysis_history', array());
        
        if (count($history) < 10) {
            return; // Need more data
        }
        
        // Check for consistently increasing memory usage
        $recent_requests = array_slice($history, -10);
        $memory_increases = array();
        
        foreach ($recent_requests as $request) {
            $memory_increases[] = $request['analysis']['total_memory_increase'];
        }
        
        $average_increase = array_sum($memory_increases) / count($memory_increases);
        
        // If average increase is consistently high, might be a leak
        if ($average_increase > 5242880) { // 5MB average increase
            $this->log_memory_leak_warning($average_increase, $memory_increases);
        }
    }
    
    /**
     * Log memory leak warning
     */
    private function log_memory_leak_warning($average_increase, $increases) {
        // Disabled: This event is not registered and creates noise
        // TODO: Register this event properly if memory leak detection is needed
        return;
        
        /*
        $data = array(
            'average_increase' => $average_increase,
            'average_increase_formatted' => $this->format_bytes($average_increase),
            'recent_increases' => $increases,
            'severity' => 'warning',
            'recommendations' => array(
                array(
                    'type' => 'investigation',
                    'icon' => '🔍',
                    'title' => 'Potential Memory Leak Detected',
                    'description' => 'Memory usage is consistently increasing across requests.',
                    'actions' => array(
                        'Review recently activated plugins',
                        'Check for infinite loops in custom code',
                        'Monitor database query performance',
                        'Consider enabling query debugging'
                    )
                )
            )
        );
        
        if (function_exists('sentinel_log_event')) {
            sentinel_log_event('memory_leak_warning', $data);
        }
        */
    }
    
    /**
     * Trigger automatic memory optimization
     */
    private function trigger_memory_optimization() {
        // Clear object cache if available
        if (function_exists('wp_cache_flush')) {
            wp_cache_flush();
        }
        
        // Clear transients
        $this->clear_expired_transients();
        
        // Limit query results if possible
        if (!defined('WP_MEMORY_LIMIT_EXCEEDED')) {
            define('WP_MEMORY_LIMIT_EXCEEDED', true);
        }
        
        // Log optimization attempt
        if (function_exists('sentinel_log_event')) {
            sentinel_log_event('memory_auto_optimization', array(
                'actions_taken' => array(
                    'cleared_object_cache',
                    'cleared_expired_transients',
                    'set_memory_limit_flag'
                ),
                'timestamp' => current_time('mysql')
            ));
        }
    }
    
    /**
     * Clear expired transients to free memory
     */
    private function clear_expired_transients() {
        global $wpdb;
        
        $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%' AND option_value < " . time());
        $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' AND option_name NOT LIKE '_transient_timeout_%' AND option_name NOT IN (SELECT REPLACE(option_name, '_transient_timeout_', '_transient_') FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%')");
    }
    
    /**
     * Check if auto-optimization is possible
     */
    private function can_auto_optimize() {
        return function_exists('wp_cache_flush') || wp_using_ext_object_cache();
    }
    
    /**
     * Get memory limit in bytes
     */
    private function get_memory_limit_bytes() {
        $memory_limit = ini_get('memory_limit');
        
        if (function_exists('wp_convert_hr_to_bytes')) {
            return wp_convert_hr_to_bytes($memory_limit);
        }
        
        // Fallback conversion
        $limit = trim($memory_limit);
        $last = strtolower($limit[strlen($limit) - 1]);
        $limit = (int) $limit;
        
        switch ($last) {
            case 'g':
                $limit *= 1024;
            case 'm':
                $limit *= 1024;
            case 'k':
                $limit *= 1024;
        }
        
        return $limit;
    }
    
    /**
     * Format bytes for display
     */
    private function format_bytes($bytes) {
        if ($bytes >= 1073741824) {
            return round($bytes / 1073741824, 2) . ' GB';
        } elseif ($bytes >= 1048576) {
            return round($bytes / 1048576, 2) . ' MB';
        } elseif ($bytes >= 1024) {
            return round($bytes / 1024, 2) . ' KB';
        } else {
            return $bytes . ' bytes';
        }
    }
    
    /**
     * Monitor memory usage during plugins loading
     */
    public function monitor_plugins_memory() {
        $this->track_memory_usage('plugins_loaded');
    }
    
    /**
     * Monitor memory usage during theme loading
     */
    public function monitor_theme_memory() {
        $this->track_memory_usage('theme_loaded');
    }
    
    /**
     * Monitor memory usage during scripts enqueuing
     */
    public function monitor_scripts_memory() {
        $this->track_memory_usage('scripts_enqueued');
    }
    
    /**
     * AJAX handler for memory insights
     */
    public function ajax_memory_insights() {
        if (!current_user_can('manage_options')) {
            wp_die('Unauthorized');
        }
        
        $insights = $this->get_memory_insights();
        wp_send_json_success($insights);
    }
    
    /**
     * Get memory insights for dashboard
     */
    public function get_memory_insights() {
        $history = get_option('sentinel_memory_analysis_history', array());
        
        if (empty($history)) {
            return array(
                'message' => 'No memory data available yet. Check back after some site activity.',
                'recommendations' => array()
            );
        }
        
        $recent = array_slice($history, -20);
        $total_requests = count($recent);
        $high_memory_requests = 0;
        $memory_hogs = array();
        
        foreach ($recent as $request) {
            $analysis = $request['analysis'];
            if ($analysis['peak_memory_usage'] > $this->get_memory_limit_bytes() * 0.8) {
                $high_memory_requests++;
            }
            
            if (!empty($analysis['memory_hogs'])) {
                $memory_hogs = array_merge($memory_hogs, $analysis['memory_hogs']);
            }
        }
        
        return array(
            'total_requests_analyzed' => $total_requests,
            'high_memory_requests' => $high_memory_requests,
            'high_memory_percentage' => round(($high_memory_requests / $total_requests) * 100, 1),
            'top_memory_hogs' => array_slice($memory_hogs, 0, 5),
            'recommendations' => $this->generate_performance_recommendations($history)
        );
    }
    
    /**
     * Generate performance recommendations based on historical data
     */
    private function generate_performance_recommendations($history) {
        $recommendations = array();
        
        // Analyze trends
        $recent = array_slice($history, -10);
        $memory_increases = array();
        
        foreach ($recent as $request) {
            $memory_increases[] = $request['analysis']['total_memory_increase'];
        }
        
        $average_increase = array_sum($memory_increases) / count($memory_increases);
        
        if ($average_increase > 2097152) { // 2MB average
            $recommendations[] = array(
                'type' => 'optimization',
                'title' => 'High Memory Usage Detected',
                'description' => 'Your site is using more memory than typical. Consider optimization.',
                'priority' => 'high',
                'actions' => array(
                    'Review recently installed plugins',
                    'Optimize database queries',
                    'Consider increasing PHP memory limit',
                    'Enable object caching'
                )
            );
        }
        
        return $recommendations;
    }
    
    /**
     * Get memory statistics for dashboard
     */
    public static function get_memory_stats() {
        $current_usage = memory_get_usage(true);
        $peak_usage = memory_get_peak_usage(true);
        $limit = wp_convert_hr_to_bytes(ini_get('memory_limit'));
        
        return array(
            'current_usage' => $current_usage,
            'current_usage_formatted' => self::format_bytes_static($current_usage),
            'peak_usage' => $peak_usage,
            'peak_usage_formatted' => self::format_bytes_static($peak_usage),
            'limit' => $limit,
            'limit_formatted' => self::format_bytes_static($limit),
            'usage_percent' => round(($current_usage / $limit) * 100, 1),
            'peak_percent' => round(($peak_usage / $limit) * 100, 1)
        );
    }
    
    /**
     * Static version of format_bytes for use in other contexts
     */
    private static function format_bytes_static($bytes) {
        if ($bytes >= 1073741824) {
            return round($bytes / 1073741824, 2) . ' GB';
        } elseif ($bytes >= 1048576) {
            return round($bytes / 1048576, 2) . ' MB';
        } elseif ($bytes >= 1024) {
            return round($bytes / 1024, 2) . ' KB';
        } else {
            return $bytes . ' bytes';
        }
    }
}

// Initialize the memory monitor
if (!function_exists('sentinel_init_memory_monitor')) {
    function sentinel_init_memory_monitor() {
        if (class_exists('Sentinel_Memory_Monitor')) {
            new Sentinel_Memory_Monitor();
        }
    }
    add_action('init', 'sentinel_init_memory_monitor', 1);
} 