<?php
/**
 * Custom Pattern AJAX Handlers
 * 
 * Handles AJAX requests for custom pattern blocking:
 * - Add pattern
 * - Get patterns
 * - Delete pattern
 * - Toggle pattern (enable/disable)
 * 
 * @package VMPFence
 * @subpackage Admin\Ajax
 * @since 2.2.0
 * @since 2.2.5 -- Added Enhanced Audit Log integration for custom pattern actions
 * @since 2.2.6 -- Updated Custom Pattern Block Message
 */

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

// Add Custom Pattern (supports both single pattern and multi-pattern with group_id)
add_action('wp_ajax_vmpfence_add_custom_pattern', 'vmpfence_add_custom_pattern_ajax');
function vmpfence_add_custom_pattern_ajax() {
    // Security checks
    check_ajax_referer('vmpfence_nonce', 'nonce');
    
    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => 'Insufficient permissions']);
    }
    
    global $wpdb;
    $table = $wpdb->prefix . 'vmpfence_custom_patterns';
    
    // Check if table exists before operations
    if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) {
        wp_send_json_error(['message' => 'Patterns table not found. Please reactivate the plugin.']);
    }
    
    // Check if this is a multi-pattern submission (array of patterns with group_id)
    $patterns = isset($_POST['patterns']) && is_array($_POST['patterns']) ? $_POST['patterns'] : null;
    $group_id = isset($_POST['group_id']) ? sanitize_text_field($_POST['group_id']) : null;
    $reason = sanitize_text_field($_POST['reason'] ?? '');
    
    // MULTI-PATTERN SUBMISSION (new multi-field form)
    if ($patterns !== null) {
        // Validate reason
        if (empty($reason)) {
            wp_send_json_error(['message' => 'Reason is required']);
        }
        
        // Validate group_id
        if (empty($group_id)) {
            wp_send_json_error(['message' => 'Group ID is required for grouped patterns']);
        }
        
        // Validate pattern count (min 1, max 4 per group)
        if (count($patterns) === 0) {
            wp_send_json_error(['message' => 'At least one pattern is required']);
        }
        
        if (count($patterns) > 4) {
            wp_send_json_error(['message' => 'Maximum 4 patterns per group']);
        }
        
        $inserted_count = 0;
        $created_at = time();
        $created_by = get_current_user_id();
        
        // Insert each pattern
        foreach ($patterns as $pattern) {
            $pattern_type = sanitize_text_field($pattern['type'] ?? '');
            $pattern_value = trim($pattern['value'] ?? '');
            
            // Validate pattern type
            $valid_types = array('hostname', 'user_agent', 'referrer', 'ip_range');
            if (!in_array($pattern_type, $valid_types)) {
                continue; // Skip invalid type
            }
            
            // Validate pattern value
            if (empty($pattern_value)) {
                continue; // Skip empty value
            }
            
            // Pattern length check
            if (strlen($pattern_value) > 500) {
                continue; // Skip too long
            }
            
            // Check for duplicate
            $duplicate = $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM $table WHERE pattern_type = %s AND pattern_value = %s",
                $pattern_type,
                $pattern_value
            ));
            
            if ($duplicate) {
                continue; // Skip duplicate
            }
            
            // Wildcard "*" detection
            if (trim($pattern_value) === '*') {
                wp_send_json_error([
                    'message' => '⚠️ CRITICAL: Pattern "*" would block ALL traffic! Please be more specific.'
                ]);
            }
            
            // CIDR validation for ip_range
            if ($pattern_type === 'ip_range') {
                $validation_result = vmpfence_validate_cidr($pattern_value);
                if ($validation_result === 'same_ip') {
                    wp_send_json_error([
                        'message' => "Invalid IP range: Start and end IPs are identical ({$pattern_value}). Please use the 'IP Address' tab for blocking a single IP instead."
                    ]);
                }
                if (!$validation_result) {
                    wp_send_json_error([
                        'message' => "Invalid IP/CIDR format: {$pattern_value}"
                    ]);
                }
            }
            
            // Insert pattern
            $result = $wpdb->insert(
                $table,
                array(
                    'pattern_type' => $pattern_type,
                    'pattern_value' => $pattern_value,
                    'is_regex' => 0, // Multi-field form doesn't use regex
                    'reason' => $reason,
                    'created_at' => $created_at,
                    'created_by' => $created_by,
                    'is_active' => 1,
                    'source' => 'local',
                    'match_count' => 0,
                    'group_id' => $group_id
                ),
                array('%s', '%s', '%d', '%s', '%d', '%d', '%d', '%s', '%d', '%s')
            );
            
            if ($result) {
                $inserted_count++;
            }
        }
        
        // Clear cache
        if (class_exists('VMPFence_Pattern_Matcher')) {
            VMPFence_Pattern_Matcher::clear_cache();
        }
        
        if ($inserted_count > 0) {
            // Build rules summary for audit log
            $rules_summary = array();
            foreach ($patterns as $p) {
                $ptype = sanitize_text_field($p['type'] ?? '');
                $pval = trim($p['value'] ?? '');
                if (!empty($ptype) && !empty($pval)) {
                    $rules_summary[] = array('type' => $ptype, 'value' => $pval);
                }
            }

            // Fire audit log hook
            do_action('vmpfence_custom_pattern_added', $group_id, $inserted_count, $rules_summary);

            wp_send_json_success([
                'message' => $inserted_count . ' pattern' . ($inserted_count > 1 ? 's' : '') . ' blocked successfully',
                'count' => $inserted_count,
                'group_id' => $group_id
            ]);
        } else {
            wp_send_json_error(['message' => 'No valid patterns to add']);
        }
        
        return;
    }
    
    // SINGLE PATTERN SUBMISSION (legacy form - backward compatibility)
    $pattern_type = sanitize_text_field($_POST['pattern_type'] ?? '');
    $pattern_value = trim($_POST['pattern_value'] ?? '');
    $is_regex = isset($_POST['is_regex']) ? (int)$_POST['is_regex'] : 0;
    $reason = sanitize_text_field($_POST['reason'] ?? '');
    
    // Validate pattern type
    $valid_types = array('hostname', 'user_agent', 'referrer', 'ip_range');
    if (!in_array($pattern_type, $valid_types)) {
        wp_send_json_error(['message' => 'Invalid pattern type']);
    }
    
    // Validate pattern value
    if (empty($pattern_value)) {
        wp_send_json_error(['message' => 'Pattern value is required']);
    }
    
    // Pattern length limit (500 chars)
    if (strlen($pattern_value) > 500) {
        wp_send_json_error(['message' => 'Pattern too long (max 500 characters)']);
    }
    
    // Check pattern count limit (500 max)
    global $wpdb;
    $table = $wpdb->prefix . 'vmpfence_custom_patterns';
    $count = $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE is_active = 1");
    if ($count >= 500) {
        wp_send_json_error(['message' => 'Pattern limit reached (max 500 active patterns)']);
    }
    
    // Check for duplicate patterns
    $duplicate = $wpdb->get_var($wpdb->prepare(
        "SELECT id FROM $table WHERE pattern_type = %s AND pattern_value = %s AND is_regex = %d",
        $pattern_type,
        $pattern_value,
        $is_regex
    ));
    
    if ($duplicate) {
        wp_send_json_error(['message' => 'This pattern already exists']);
    }
    
    // Wildcard "*" detection (blocks everything)
    if (!$is_regex && trim($pattern_value) === '*') {
        wp_send_json_error([
            'message' => '⚠️ CRITICAL: Pattern "*" would block ALL traffic including yourself! Please be more specific.'
        ]);
    }
    
    // Regex validation
    if ($is_regex) {
        // Check for catastrophic backtracking patterns
        $dangerous_patterns = array(
            '/(\w+\s?)+/',  // Repeated word boundaries
            '/(\d+)+/',      // Repeated digits
            '/(a+)+/',       // Nested quantifiers
            '/(.+)+/'        // Nested wildcards
        );
        
        foreach ($dangerous_patterns as $dangerous) {
            if (stripos($pattern_value, str_replace('/', '', $dangerous)) !== false) {
                wp_send_json_error([
                    'message' => '⚠️ SECURITY: This regex pattern may cause catastrophic backtracking. Please simplify.'
                ]);
            }
        }
        
        // Validate regex syntax
        set_error_handler(function() {});
        $valid = @preg_match($pattern_value, '') !== false;
        restore_error_handler();
        
        if (!$valid) {
            wp_send_json_error(['message' => 'Invalid regex syntax']);
        }
        
        // Test regex performance (100ms timeout)
        $start = microtime(true);
        @preg_match($pattern_value, str_repeat('a', 1000));
        $duration = (microtime(true) - $start) * 1000;
        
        if ($duration > 100) {
            wp_send_json_error([
                'message' => '⚠️ PERFORMANCE: This regex is too slow (' . round($duration) . 'ms). Please optimize.'
            ]);
        }
    }
    
    // Validate CIDR for ip_range
    if ($pattern_type === 'ip_range') {
        if (!vmpfence_validate_cidr($pattern_value)) {
            wp_send_json_error(['message' => 'Invalid IP or CIDR notation']);
        }
    }
    
    // Known-good-bot warning
    if ($pattern_type === 'user_agent') {
        $known_bots = array('googlebot', 'bingbot', 'slurp', 'duckduckbot', 'baiduspider', 'yandexbot');
        $pattern_lower = strtolower($pattern_value);
        
        foreach ($known_bots as $bot) {
            if (stripos($pattern_lower, $bot) !== false) {
                wp_send_json_error([
                    'message' => '⚠️ WARNING: This pattern may block ' . ucfirst($bot) . ' (search engine). Continue at your own risk.',
                    'warning_only' => true
                ]);
            }
        }
    }
    
    // Insert pattern
    $result = $wpdb->insert(
        $table,
        array(
            'pattern_type' => $pattern_type,
            'pattern_value' => $pattern_value,
            'is_regex' => $is_regex,
            'reason' => $reason,
            'created_at' => time(),
            'created_by' => get_current_user_id(),
            'is_active' => 1,
            'source' => 'local',
            'match_count' => 0
        ),
        array('%s', '%s', '%d', '%s', '%d', '%d', '%d', '%s', '%d')
    );
    
    if ($result === false) {
        wp_send_json_error(['message' => 'Failed to add pattern: ' . $wpdb->last_error]);
    }
    
    // Clear cache
    if (class_exists('VMPFence_Pattern_Matcher')) {
        VMPFence_Pattern_Matcher::clear_cache();
    }

    // Fire audit log hook with single rule details
    $rules_summary = array(array('type' => $pattern_type, 'value' => $pattern_value));
    do_action('vmpfence_custom_pattern_added', null, 1, $rules_summary);

    wp_send_json_success([
        'message' => 'Pattern added successfully',
        'pattern_id' => $wpdb->insert_id
    ]);
}

// Get Custom Patterns (for table)
add_action('wp_ajax_vmpfence_get_custom_patterns', 'vmpfence_get_custom_patterns_ajax');
function vmpfence_get_custom_patterns_ajax() {
    check_ajax_referer('vmpfence_nonce', 'nonce');
    
    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => 'Insufficient permissions']);
    }
    
    global $wpdb;
    $table = $wpdb->prefix . 'vmpfence_custom_patterns';
    
    // Check if table exists
    if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) {
        wp_send_json_success(['patterns' => array()]); // Return empty array if table doesn't exist
    }
    
    $patterns = $wpdb->get_results(
        "SELECT id, pattern_type, pattern_value, is_regex, reason, created_at, is_active, match_count, last_matched, group_id 
         FROM $table 
         ORDER BY created_at DESC",
        ARRAY_A
    );
    
    wp_send_json_success(['patterns' => $patterns ? $patterns : array()]);
}

// Delete Custom Pattern (handles grouped patterns - deletes entire group)
add_action('wp_ajax_vmpfence_delete_custom_pattern', 'vmpfence_delete_custom_pattern_ajax');
function vmpfence_delete_custom_pattern_ajax() {
    check_ajax_referer('vmpfence_nonce', 'nonce');
    
    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => 'Insufficient permissions']);
    }
    
    $pattern_id = isset($_POST['pattern_id']) ? (int)$_POST['pattern_id'] : 0;
    
    if ($pattern_id <= 0) {
        wp_send_json_error(['message' => 'Invalid pattern ID']);
    }
    
    global $wpdb;
    $table = $wpdb->prefix . 'vmpfence_custom_patterns';
    
    // Check if table exists
    if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) {
        wp_send_json_error(['message' => 'Patterns table not found. Please reactivate the plugin.']);
    }
    
    // Check if pattern exists and get its group_id
    $pattern = $wpdb->get_row($wpdb->prepare(
        "SELECT id, group_id FROM $table WHERE id = %d",
        $pattern_id
    ));
    
    if (!$pattern) {
        wp_send_json_error(['message' => 'Pattern not found']);
    }
    
    // If pattern has group_id, delete ALL patterns in that group
    if (!empty($pattern->group_id)) {
        // Get patterns in group before deletion (for audit log)
        $group_patterns = $wpdb->get_results($wpdb->prepare(
            "SELECT pattern_type, pattern_value FROM $table WHERE group_id = %s",
            $pattern->group_id
        ), ARRAY_A);
        $group_count = count($group_patterns);

        // Build rules summary for audit log
        $rules_summary = array();
        foreach ($group_patterns as $gp) {
            $rules_summary[] = array('type' => $gp['pattern_type'], 'value' => $gp['pattern_value']);
        }
        
        // Delete entire group
        $result = $wpdb->delete($table, array('group_id' => $pattern->group_id), array('%s'));
        
        if ($result === false) {
            wp_send_json_error(['message' => 'Database error during group deletion']);
        }
        
        // Clear cache
        if (class_exists('VMPFence_Pattern_Matcher')) {
            VMPFence_Pattern_Matcher::clear_cache();
        }

        // Fire audit log hook
        do_action('vmpfence_custom_pattern_deleted', $pattern->group_id, $group_count, $rules_summary);

        wp_send_json_success([
            'message' => $group_count . ' grouped pattern' . ($group_count > 1 ? 's' : '') . ' deleted successfully',
            'deleted_count' => $group_count,
            'is_group' => true
        ]);
    } else {
        // Get pattern details before deletion (for audit log)
        $single_pattern = $wpdb->get_row($wpdb->prepare(
            "SELECT pattern_type, pattern_value FROM $table WHERE id = %d",
            $pattern_id
        ), ARRAY_A);
        $rules_summary = array();
        if ($single_pattern) {
            $rules_summary[] = array('type' => $single_pattern['pattern_type'], 'value' => $single_pattern['pattern_value']);
        }

        // Single pattern - delete only this one
        $result = $wpdb->delete($table, array('id' => $pattern_id), array('%d'));
        
        if ($result === false) {
            wp_send_json_error(['message' => 'Database error during deletion']);
        }
        
        // Clear cache
        if (class_exists('VMPFence_Pattern_Matcher')) {
            VMPFence_Pattern_Matcher::clear_cache();
        }

        // Fire audit log hook
        do_action('vmpfence_custom_pattern_deleted', $pattern_id, 1, $rules_summary);

        wp_send_json_success([
            'message' => 'Pattern deleted successfully',
            'deleted_count' => 1,
            'is_group' => false
        ]);
    }
}

// Toggle Custom Pattern (enable/disable)
add_action('wp_ajax_vmpfence_toggle_custom_pattern', 'vmpfence_toggle_custom_pattern_ajax');
function vmpfence_toggle_custom_pattern_ajax() {
    check_ajax_referer('vmpfence_nonce', 'nonce');
    
    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => 'Insufficient permissions']);
    }
    
    $pattern_id = isset($_POST['pattern_id']) ? (int)$_POST['pattern_id'] : 0;
    
    if ($pattern_id <= 0) {
        wp_send_json_error(['message' => 'Invalid pattern ID']);
    }
    
    global $wpdb;
    $table = $wpdb->prefix . 'vmpfence_custom_patterns';
    
    // Check if table exists
    if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) {
        wp_send_json_error(['message' => 'Patterns table not found. Please reactivate the plugin.']);
    }
    
    // Get current state
    $current_state = $wpdb->get_var($wpdb->prepare(
        "SELECT is_active FROM $table WHERE id = %d",
        $pattern_id
    ));
    
    if ($current_state === null) {
        wp_send_json_error(['message' => 'Pattern not found']);
    }
    
    // Toggle state
    $new_state = $current_state ? 0 : 1;
    
    $result = $wpdb->update(
        $table,
        array('is_active' => $new_state),
        array('id' => $pattern_id),
        array('%d'),
        array('%d')
    );
    
    if ($result === false) {
        wp_send_json_error(['message' => 'Failed to toggle pattern']);
    }
    
    // Clear cache
    if (class_exists('VMPFence_Pattern_Matcher')) {
        VMPFence_Pattern_Matcher::clear_cache();
    }

    // Fire audit log hook
    do_action('vmpfence_custom_pattern_toggled', $pattern_id, $new_state);

    wp_send_json_success([
        'message' => $new_state ? 'Pattern enabled' : 'Pattern disabled',
        'is_active' => $new_state
    ]);
}

// Helper: Validate CIDR notation and IP ranges
function vmpfence_validate_cidr($cidr) {
    // Remove spaces for consistent parsing
    $cidr = str_replace(' ', '', $cidr);
    
    // Handle dash-separated range (e.g., "192.168.0.120-192.168.0.169")
    // Bug #26 & #28 fix: Only treat as range if both parts are valid IPs
    if (strpos($cidr, '-') !== false) {
        list($start_ip, $end_ip) = explode('-', $cidr, 2);
        
        // Bug #25 fix: Validate both parts are non-empty
        if (empty($start_ip) || empty($end_ip)) {
            return false;
        }
        
        // Bug #28 fix: Validate both parts are actual IPs before treating as range
        // This prevents hostnames like "192.my-server.com" from being parsed as ranges
        if (filter_var($start_ip, FILTER_VALIDATE_IP) === false || 
            filter_var($end_ip, FILTER_VALIDATE_IP) === false) {
            // Bug #31 fix: Malformed IP range detected - return false immediately
            // This provides clearer error messaging for invalid range attempts
            return false;
        } else {
            // Both parts are valid IPs - proceed with range validation
            
            // Ensure both are same IP version
            $start_is_ipv6 = filter_var($start_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
            $end_is_ipv6 = filter_var($end_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
            
            if ($start_is_ipv6 !== $end_is_ipv6) {
                return false; // Mixed IP versions not allowed
            }
            
            // Bug #27 fix: Reject identical start and end IPs (should use single IP format instead)
            if ($start_ip === $end_ip) {
                return 'same_ip'; // Special return value to trigger specific error message
            }
            
            // Validate range order (start must be < end)
            $start_long = $start_is_ipv6 ? inet_pton($start_ip) : ip2long($start_ip);
            $end_long = $end_is_ipv6 ? inet_pton($end_ip) : ip2long($end_ip);
            
            if ($start_is_ipv6) {
                $comparison = strcmp($start_long, $end_long);
                if ($comparison === 0) {
                    return 'same_ip'; // Identical IPs
                }
                return $comparison < 0;
            } else {
                if ($start_long === false || $end_long === false) {
                    return false;
                }
                if ($start_long === $end_long) {
                    return 'same_ip'; // Identical IPs
                }
                return $start_long < $end_long;
            }
        }
    }
    
    // Handle single IP
    if (strpos($cidr, '/') === false) {
        return filter_var($cidr, FILTER_VALIDATE_IP) !== false;
    }
    
    // Handle CIDR
    list($ip, $mask) = explode('/', $cidr);
    
    // Validate IP
    if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
        return false;
    }
    
    // Validate mask (0-32 for IPv4, 0-128 for IPv6)
    $max_mask = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 128 : 32;
    return is_numeric($mask) && $mask >= 0 && $mask <= $max_mask;
}
