<?php
/**
 * WPSec Logger Class
 * 
 * Provides a centralized, file-based logging system for the WPFort plugin
 * with support for different log levels, log rotation, and admin interface.
 */

// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

/**
 * WPSec Logger Class
 */
class WPSEC_Logger {
    /**
     * Log levels
     */
    const DEBUG = 'debug';   // Detailed debug information
    const INFO = 'info';     // Interesting events
    const NOTICE = 'notice'; // Normal but significant events
    const WARNING = 'warning'; // Warning events
    const ERROR = 'error';   // Error events
    const CRITICAL = 'critical'; // Critical events
    
    /**
     * Whether debug mode is enabled
     * 
     * @var bool
     */
    private static $debug_mode = false;
    
    /**
     * Log file path
     * 
     * @var string
     */
    private static $log_file = '';
    
    /**
     * Max log file size in bytes (default: 10MB)
     * 
     * @var int
     */
    private static $max_size = 10485760; // 10MB
    
    /**
     * Maximum number of log files to keep
     * 
     * @var int
     */
    private static $max_files = 5;
    
    /**
     * Log format
     * 
     * @var string
     */
    private static $log_format = '[%s] %s: %s';

    /**
     * Initialize the logger
     */
    public static function init() {
        // Set debug mode based on constant or filter
        self::$debug_mode = apply_filters('wpsec_debug_mode', defined('WPSEC_DEBUG') && WPSEC_DEBUG);
        
        // Set log file path
        $upload_dir = wp_upload_dir();
        $logs_dir = trailingslashit(dirname(dirname(__FILE__))) . 'logs';
        
        // Check if the logs directory exists, create it if not
        if (!file_exists($logs_dir)) {
            wp_mkdir_p($logs_dir);
            
            // Create .htaccess to prevent direct access
            $htaccess_file = $logs_dir . '/.htaccess';
            if (!file_exists($htaccess_file)) {
                file_put_contents($htaccess_file, 'deny from all');
            }
            
            // Create index.php to prevent directory listing
            $index_file = $logs_dir . '/index.php';
            if (!file_exists($index_file)) {
                file_put_contents($index_file, '<?php // Silence is golden');
            }
        }
        
        self::$log_file = $logs_dir . '/wpfort-debug.log';
    }
    
    /**
     * Log a message
     * 
     * @param string $message The log message
     * @param string $level The log level (debug, info, warning, error, critical)
     * @param array $context Additional context data (optional)
     */
    public static function log($message, $level = self::INFO, $context = array()) {
        // Skip if not in debug mode and level is debug
        if ($level === self::DEBUG && !self::$debug_mode) {
            return;
        }
        
        // Ensure the logger is initialized
        if (empty(self::$log_file)) {
            self::init();
        }
        
        // Format the log entry
        $timestamp = current_time('Y-m-d H:i:s');
        
        // Add emoji based on level for better visual scanning
        $prefix = '';
        switch ($level) {
            case self::DEBUG:
                $prefix = '🐞 ';
                break;
            case self::INFO:
                $prefix = '🔍 ';
                break;
            case self::NOTICE:
                $prefix = '📝 ';
                break;
            case self::WARNING:
                $prefix = '⚠️ ';
                break;
            case self::ERROR:
                $prefix = '❌ ';
                break;
            case self::CRITICAL:
                $prefix = '🚨 ';
                break;
        }
        
        // Format context if provided
        $context_string = '';
        if (!empty($context)) {
            $context_string = ' ' . json_encode($context);
        }
        
        // Final log entry
        $entry = sprintf(
            self::$log_format, 
            $timestamp, 
            strtoupper($level), 
            $prefix . $message . $context_string
        ) . PHP_EOL;
        
        // Check if we need to rotate the log
        self::maybe_rotate_logs();
        
        // Write directly to log file to avoid recursion
        file_put_contents(self::$log_file, $entry, FILE_APPEND);
    }
    
    /**
     * Rotate logs if needed
     */
    private static function maybe_rotate_logs() {
        if (!file_exists(self::$log_file)) {
            return;
        }
        
        $size = filesize(self::$log_file);
        
        if ($size < self::$max_size) {
            return;
        }
        
        // Rotate logs
        for ($i = self::$max_files - 1; $i >= 0; $i--) {
            $old_file = self::$log_file . ($i > 0 ? '.' . $i : '');
            $new_file = self::$log_file . '.' . ($i + 1);
            
            if (file_exists($old_file)) {
                if ($i == self::$max_files - 1) {
                    WPSEC_Filesystem_Helper::delete_file($old_file);
                } else {
                    // Use WordPress Filesystem API for rename operation
                    global $wp_filesystem;
                    if (!WPSEC_Filesystem_Helper::initialize_filesystem()) {
                        // If filesystem initialization fails, use WP move function as fallback
                        if (function_exists('wp_move_file')) {
                            wp_move_file($old_file, $new_file);
                        }
                    } else {
                        // Convert paths for WP_Filesystem
                        $old_file_fs = str_replace('\\', '/', $old_file);
                        $new_file_fs = str_replace('\\', '/', $new_file);
                        
                        // Move file using WP_Filesystem
                        $wp_filesystem->move($old_file_fs, $new_file_fs, true);
                    }
                }
            }
        }
        
        // Create a fresh log file
        @file_put_contents(self::$log_file, '');
    }
    
    /**
     * Debug level log
     * 
     * @param string $message The log message
     * @param array $context Additional context data (optional)
     */
    public static function debug($message, $context = array()) {
        self::log($message, self::DEBUG, $context);
    }
    
    /**
     * Info level log
     * 
     * @param string $message The log message
     * @param array $context Additional context data (optional)
     */
    public static function info($message, $context = array()) {
        self::log($message, self::INFO, $context);
    }
    
    /**
     * Notice level log
     * 
     * @param string $message The log message
     * @param array $context Additional context data (optional)
     */
    public static function notice($message, $context = array()) {
        self::log($message, self::NOTICE, $context);
    }
    
    /**
     * Warning level log
     * 
     * @param string $message The log message
     * @param array $context Additional context data (optional)
     */
    public static function warning($message, $context = array()) {
        self::log($message, self::WARNING, $context);
    }
    
    /**
     * Error level log
     * 
     * @param string $message The log message
     * @param array $context Additional context data (optional)
     */
    public static function error($message, $context = array()) {
        self::log($message, self::ERROR, $context);
    }
    
    /**
     * Critical level log
     * 
     * @param string $message The log message
     * @param array $context Additional context data (optional)
     */
    public static function critical($message, $context = array()) {
        self::log($message, self::CRITICAL, $context);
    }
    
    /**
     * Get the log file path
     * 
     * @return string Log file path
     */
    public static function get_log_file() {
        if (empty(self::$log_file)) {
            self::init();
        }
        
        return self::$log_file;
    }
    
    /**
     * Get all log files
     * 
     * @return array Log files with their sizes and modified times
     */
    public static function get_log_files() {
        if (empty(self::$log_file)) {
            self::init();
        }
        
        $log_dir = dirname(self::$log_file);
        $main_log = basename(self::$log_file);
        $files = array();
        
        if (file_exists(self::$log_file)) {
            $files[$main_log] = array(
                'path' => self::$log_file,
                'size' => size_format(filesize(self::$log_file)),
                'modified' => gmdate('Y-m-d H:i:s', filemtime(self::$log_file))
            );
        }
        
        // Get rotated log files
        for ($i = 1; $i <= self::$max_files; $i++) {
            $rotated_file = self::$log_file . '.' . $i;
            if (file_exists($rotated_file)) {
                $filename = basename($rotated_file);
                $files[$filename] = array(
                    'path' => $rotated_file,
                    'size' => size_format(filesize($rotated_file)),
                    'modified' => gmdate('Y-m-d H:i:s', filemtime($rotated_file))
                );
            }
        }
        
        return $files;
    }
    
    /**
     * Get log file content
     * 
     * @param string $file The log file to get content from (default is current log)
     * @param int $lines Number of lines to get (0 for all)
     * @return string Log file content
     */
    public static function get_log_content($file = '', $lines = 0) {
        if (empty($file)) {
            $file = self::get_log_file();
        }
        
        if (!file_exists($file)) {
            return 'Log file does not exist.';
        }
        
        if ($lines > 0) {
            // Get only specified number of lines (most recent first)
            $content = '';
            $file_lines = array_reverse(file($file));
            
            for ($i = 0; $i < min($lines, count($file_lines)); $i++) {
                $content = $file_lines[$i] . $content;
            }
            
            return $content;
        }
        
        return file_get_contents($file);
    }
    
    /**
     * Clear log file
     * 
     * @param string $file The log file to clear (default is current log)
     * @return bool Success
     */
    public static function clear_log($file = '') {
        if (empty($file)) {
            $file = self::get_log_file();
        }
        
        if (!file_exists($file)) {
            return false;
        }
        
        return (bool) file_put_contents($file, '');
    }
}

// Initialize the logger
WPSEC_Logger::init();

/**
 * Global logging function
 * 
 * @param string $message The message to log
 * @param string $level The log level
 * @param array $context Additional context
 */
function wpsec_debug_log($message, $level = 'info', $context = array(), $backup_level = 'info') {
    // Handle the old usage pattern from the backup manager:
    // wpsec_debug_log($log_message, 3, $this->log_file, 'info');
    if (is_numeric($level) && is_string($context) && !empty($context)) {
        // This is the old pattern with a file path
        $file_path = $context;
        $actual_level = $backup_level; // Use the 4th parameter as the actual log level
        
        // Create directory if it doesn't exist
        $dir = dirname($file_path);
        if (!file_exists($dir)) {
            wp_mkdir_p($dir);
        }
        
        // Write directly to the specified file to avoid recursive loops
        error_log($message, 3, $file_path);
        
        // Also log to the main plugin log with context about where the message was written
        WPSEC_Logger::log("Backup log entry written to {$file_path}", $actual_level, array('original_message' => $message));
        
        return;
    }
    
    // Standard logging pattern
    switch ($level) {
        case 'debug':
            WPSEC_Logger::debug($message, $context);
            break;
        case 'notice':
            WPSEC_Logger::notice($message, $context);
            break;
        case 'warning':
            WPSEC_Logger::warning($message, $context);
            break;
        case 'error':
            WPSEC_Logger::error($message, $context);
            break;
        case 'critical':
            WPSEC_Logger::critical($message, $context);
            break;
        case 'info':
        default:
            WPSEC_Logger::info($message, $context);
            break;
    }
}
