<?php
/**
 * Critical Security Fixes and Plugin Compatibility
 * Implementation of audit recommendations
 */

// Prevent direct access
if (!defined('WPINC')) {
    die('Direct access not permitted.');
}

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

class MCB_Security_Fixes {
    
    /**
     * Initialize security fixes and compatibility patches
     */
    public static function init() {
        // Fix 1: SSRF DNS Rebinding Protection
        add_filter('mcb_validate_api_url', array(__CLASS__, 'validate_api_url_dns'), 10, 2);
        
        // Fix 2: Wordfence Compatibility
        add_action('init', array(__CLASS__, 'wordfence_compatibility'));
        
        // Fix 3: Error Suppression in Production
        add_action('init', array(__CLASS__, 'suppress_errors_production'));
        
        // Fix 4: Token Autoloading Fix
        add_filter('mcb_update_option', array(__CLASS__, 'disable_autoload'), 10, 3);
        
        // Fix 5: User Agent Hashing
        add_filter('mcb_log_user_agent', array(__CLASS__, 'hash_user_agent'));
        
        // Fix 6: Plugin Conflict Detection
        add_action('admin_notices', array(__CLASS__, 'compatibility_notices'));
        
        // Fix 7: Performance Optimization
        add_action('init', array(__CLASS__, 'optimize_loading'));
    }
    
    /**
     * CRITICAL FIX: SSRF DNS Rebinding Protection
     * Prevents accessing internal network resources
     */
    public static function validate_api_url_dns($valid, $url) {
        $parsed = parse_url($url);
        if (!$parsed || !isset($parsed['host'])) {
            return false;
        }
        
        $host = $parsed['host'];
        
        // Whitelist of allowed API hosts
        $allowed_hosts = array(
            'api.github.com',
            'gitlab.com',
            'api.bitbucket.org',
            'codeberg.org'
        );
        
        if (!in_array($host, $allowed_hosts, true)) {
            return false;
        }
        
        // Get IP address of the host
        $ip = gethostbyname($host);
        
        // If resolution failed, block
        if ($ip === $host) {
            error_log('GCV Security: DNS resolution failed for ' . $host);
            return false;
        }
        
        // Check for private and reserved IP ranges
        if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            error_log('GCV Security: Blocked private/reserved IP ' . $ip . ' for host ' . $host);
            return false;
        }
        
        // Additional check for IPv6
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            // Check for IPv6 private ranges
            $private_ranges = array(
                'fc00::/7',   // Unique local addresses
                'fe80::/10',  // Link-local addresses
                '::1/128',    // Loopback
                '::/128',     // Unspecified
                '::ffff:0:0/96' // IPv4-mapped addresses
            );
            
            foreach ($private_ranges as $range) {
                if (self::ip_in_range($ip, $range)) {
                    error_log('GCV Security: Blocked private IPv6 ' . $ip);
                    return false;
                }
            }
        }
        
        return true;
    }
    
    /**
     * Wordfence Compatibility Layer
     */
    public static function wordfence_compatibility() {
        if (!defined('WORDFENCE_VERSION')) {
            return;
        }
        
        // Add API URLs to Wordfence whitelist
        if (class_exists('wfConfig')) {
            $whitelisted = wfConfig::get('whitelisted');
            $whitelist_urls = array(
                'api.github.com',
                'gitlab.com',
                'api.bitbucket.org', 
                'codeberg.org'
            );
            
            foreach ($whitelist_urls as $url) {
                if (strpos($whitelisted, $url) === false) {
                    $whitelisted .= "\n" . $url;
                }
            }
            
            wfConfig::set('whitelisted', $whitelisted);
        }
        
        // Disable plugin rate limiting if Wordfence is active
        if (has_filter('mcb_enable_rate_limiting')) {
            add_filter('mcb_enable_rate_limiting', '__return_false');
        }
        
        // Add Wordfence compatibility headers
        add_action('mcb_before_api_request', function() {
            if (function_exists('wfUtils::doNotCache')) {
                wfUtils::doNotCache();
            }
        });
    }
    
    /**
     * Suppress errors in production environments
     */
    public static function suppress_errors_production() {
        // Only in production (non-debug mode)
        if (defined('WP_DEBUG') && WP_DEBUG) {
            return;
        }
        
        // Suppress PHP errors in AJAX handlers
        if (wp_doing_ajax()) {
            @error_reporting(0);
            @ini_set('display_errors', '0');
            @ini_set('display_startup_errors', '0');
            
            // Set custom error handler
            set_error_handler(function($errno, $errstr, $errfile, $errline) {
                // Log to error log but don't display
                if (WP_DEBUG_LOG) {
                    error_log(sprintf(
                        'GCV Error: %s in %s on line %d',
                        $errstr,
                        $errfile,
                        $errline
                    ));
                }
                return true; // Prevent default error handler
            });
        }
    }
    
    /**
     * Fix token autoloading issue
     */
    public static function disable_autoload($value, $option, $autoload) {
        // Disable autoload for token options
        if (strpos($option, 'mcb_') === 0 && strpos($option, '_token') !== false) {
            return 'no';
        }
        return $autoload;
    }
    
    /**
     * Hash user agents for privacy
     */
    public static function hash_user_agent($user_agent) {
        if (empty($user_agent)) {
            return 'unknown';
        }
        
        // Use a consistent salt for hashing
        $salt = wp_salt('auth');
        
        // Create a hash that's consistent but not reversible
        return substr(hash('sha256', $salt . $user_agent), 0, 16);
    }
    
    /**
     * Show compatibility notices
     */
    public static function compatibility_notices() {
        $notices = array();
        
        // Wordfence compatibility notice
        if (defined('WORDFENCE_VERSION')) {
            $notices[] = array(
                'type' => 'warning',
                'message' => __('Git Code Viewer: Wordfence detected. Please ensure the following URLs are whitelisted in Wordfence settings: api.github.com, gitlab.com, api.bitbucket.org, codeberg.org', 'maple-code-blocks'),
                'dismissible' => true
            );
        }
        
        // WooCommerce compatibility (informational)
        if (class_exists('WooCommerce')) {
            // No issues, just log for debugging
            error_log('GCV: WooCommerce detected - compatibility mode active');
        }
        
        // LearnDash compatibility (informational)
        if (defined('LEARNDASH_VERSION')) {
            // No issues, just log for debugging
            error_log('GCV: LearnDash detected - compatibility mode active');
        }
        
        // Memory limit warning
        $memory_limit = ini_get('memory_limit');
        if ($memory_limit && self::convert_to_bytes($memory_limit) < 67108864) { // 64MB
            $notices[] = array(
                'type' => 'warning',
                'message' => __('Git Code Viewer: Low memory limit detected. Consider increasing memory_limit to at least 64MB for optimal performance.', 'maple-code-blocks'),
                'dismissible' => true
            );
        }
        
        // Display notices
        foreach ($notices as $notice) {
            $dismissible = $notice['dismissible'] ? 'is-dismissible' : '';
            printf(
                '<div class="notice notice-%s %s"><p>%s</p></div>',
                esc_attr($notice['type']),
                esc_attr($dismissible),
                esc_html($notice['message'])
            );
        }
    }
    
    /**
     * Optimize plugin loading
     */
    public static function optimize_loading() {
        // Hook into admin_init for screen-specific optimizations
        if (is_admin()) {
            add_action('admin_init', function() {
                // Check screen only after it's available
                add_action('current_screen', function($screen) {
                    if ($screen && $screen->id !== 'settings_page_maple-code-blocks') {
                        remove_action('admin_enqueue_scripts', array('Maple_Code_Blocks', 'enqueue_scripts'));
                    }
                });
            });
        }
        
        // Optimize transient cleanup
        if (!wp_next_scheduled('mcb_cleanup_transients')) {
            wp_schedule_event(time(), 'daily', 'mcb_cleanup_transients');
        }
        
        add_action('mcb_cleanup_transients', function() {
            global $wpdb;
            
            // Use WordPress function instead of direct query
            if (function_exists('delete_expired_transients')) {
                delete_expired_transients();
            } else {
                // Fallback for older WordPress versions
                $wpdb->query(
                    "DELETE FROM {$wpdb->options} 
                    WHERE option_name LIKE '_transient_timeout_mcb_%' 
                    AND option_value < UNIX_TIMESTAMP()"
                );
            }
        });
    }
    
    /**
     * Helper: Check if IP is in range
     */
    private static function ip_in_range($ip, $range) {
        if (strpos($range, '/') === false) {
            $range .= '/32';
        }
        
        list($subnet, $bits) = explode('/', $range);
        
        // IPv4
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && 
            filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $ip = ip2long($ip);
            $subnet = ip2long($subnet);
            $mask = -1 << (32 - $bits);
            $subnet &= $mask;
            return ($ip & $mask) == $subnet;
        }
        
        // IPv6
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && 
            filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $ip_bin = inet_pton($ip);
            $subnet_bin = inet_pton($subnet);
            $bytes = $bits / 8;
            $remainder = $bits % 8;
            
            if ($bytes > 0) {
                if (substr($ip_bin, 0, $bytes) !== substr($subnet_bin, 0, $bytes)) {
                    return false;
                }
            }
            
            if ($remainder > 0 && $bytes < 16) {
                $mask = 0xFF << (8 - $remainder);
                $ip_byte = ord($ip_bin[$bytes]);
                $subnet_byte = ord($subnet_bin[$bytes]);
                if (($ip_byte & $mask) !== ($subnet_byte & $mask)) {
                    return false;
                }
            }
            
            return true;
        }
        
        return false;
    }
    
    /**
     * Helper: Convert memory string to bytes
     */
    private static function convert_to_bytes($val) {
        $val = trim($val);
        $last = strtolower($val[strlen($val)-1]);
        $val = (int)$val;
        
        switch($last) {
            case 'g':
                $val *= 1024;
            case 'm':
                $val *= 1024;
            case 'k':
                $val *= 1024;
        }
        
        return $val;
    }
}

// Initialize security fixes
add_action('plugins_loaded', array('MCB_Security_Fixes', 'init'), 5);
add_action('rest_api_init', function() {
    register_rest_route('maple-code-blocks/v1', '/health', array(
        'methods' => 'GET',
        'callback' => function() {
            $health = array(
                'status' => 'healthy',
                'version' => '2.0.0',
                'php_version' => PHP_VERSION,
                'wordpress_version' => get_bloginfo('version'),
                'memory_usage' => memory_get_usage(true),
                'memory_limit' => ini_get('memory_limit'),
                'wordfence_active' => defined('WORDFENCE_VERSION'),
                'woocommerce_active' => class_exists('WooCommerce'),
                'learndash_active' => defined('LEARNDASH_VERSION'),
                'cache_size' => count(get_transient('mcb_security_logs') ?: array()),
                'rate_limit_active' => !defined('WORDFENCE_VERSION')
            );
            
            return new WP_REST_Response($health, 200);
        },
        'permission_callback' => function() {
            return current_user_can('manage_options');
        }
    ));
});
