<?php
if (!defined('ABSPATH')) {
    exit;
}

function foxblock_load_scanner()
{
    static $loaded = false;
    if ($loaded)
        return;
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/Whitelist.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/Deobfuscator.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/Analyzer.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/JS_Analyzer.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/SVG_Analyzer.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/HTML_Analyzer.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/FileScanner.php';
    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/Reporter.php';
    $loaded = true;
}

function foxblock_get_current_ip()
{
    $ip = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP']
        : (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]
            : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''));
    return sanitize_text_field(trim($ip));
}

add_action('admin_menu', 'foxblock_admin_menu');
function foxblock_admin_menu()
{
    add_menu_page('Foxblock Security', 'Foxblock', 'manage_options', 'foxblock-settings', 'foxblock_settings_page', 'dashicons-shield', 80);
}

add_action('admin_enqueue_scripts', 'foxblock_admin_scripts');
function foxblock_admin_scripts($hook)
{
    if ($hook !== 'toplevel_page_foxblock-settings')
        return;
        
    // Ngăn chặn WordPress Heartbeat kiểm tra session (wp-auth-check) khi đang restore DB
    wp_deregister_script('heartbeat');

    wp_enqueue_style('foxblock-admin-css', FOXBLOCK_PLUGIN_URL . 'assets/css/admin.css', array(), FOXBLOCK_VERSION);
    wp_enqueue_script('foxblock-admin-js', FOXBLOCK_PLUGIN_URL . 'assets/js/admin.js', array('jquery'), FOXBLOCK_VERSION, true);
    wp_localize_script('foxblock-admin-js', 'foxblock_ajax', array(
        'ajax_url' => admin_url('admin-ajax.php'),
        'plugin_url' => FOXBLOCK_PLUGIN_URL,
        'nonce' => wp_create_nonce('foxblock_nonce'),
        'my_ip' => foxblock_get_current_ip(),
        'max_upload_size' => wp_max_upload_size(),
    ));
    wp_enqueue_script('foxblock-backup-js', FOXBLOCK_PLUGIN_URL . 'assets/js/backup.js', array('jquery', 'foxblock-admin-js'), FOXBLOCK_VERSION, true);
}

function foxblock_guess_device($user_agent)
{
    if (preg_match('/mobile/i', $user_agent))
        return 'Mobile';
    if (preg_match('/tablet|ipad/i', $user_agent))
        return 'Tablet';
    if (preg_match('/bot|crawl|slurp|spider|mediapartners|agent/i', $user_agent))
        return 'Bot';
    return 'Desktop';
}

function foxblock_get_country_code($ip)
{
    $cache_key = 'foxblock_cc_' . md5($ip);
    $cached = get_transient($cache_key);
    return ($cached !== false) ? $cached : '';
}

function foxblock_settings_page()
{
    global $wpdb;
    $table_name = $wpdb->prefix . 'foxblock_log';
    $logs = $wpdb->get_results("SELECT * FROM $table_name ORDER BY time DESC LIMIT 300");
    $blocked_bots = get_option('foxblock_blocked_bots', array());
    $block_foreign = get_option('foxblock_block_all_non_google', 0);
    $common_bots = foxblock_get_known_bots();
    $custom_bots = array_values(array_diff($blocked_bots, $common_bots));
    $blocked_ips = get_option('foxblock_blocked_ips', array());
    $whitelist_ips = get_option('foxblock_whitelist_ips', array());
    require_once FOXBLOCK_PLUGIN_DIR . 'admin/views/dashboard.php';
}

add_action('wp_ajax_foxblock_get_logs', 'foxblock_get_logs_callback');
function foxblock_get_logs_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    global $wpdb;
    $logs = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}foxblock_log ORDER BY time DESC LIMIT 300");
    ob_start();
    if (empty($logs)) {
        echo '<tr><td colspan="6" style="text-align:center;padding:30px;color:#666;">No logs yet.</td></tr>';
    } else {
        foreach ($logs as $log) {
            $rc = $log->is_bot ? 'foxblock-bot-row' : 'foxblock-user-row';
            $dv = foxblock_guess_device($log->user_agent);
            echo '<tr class="' . esc_attr($rc) . '">';
            echo '<td class="foxblock-time">' . esc_html($log->time) . '</td>';
            echo '<td class="foxblock-flag" data-ip="' . esc_attr($log->ip) . '" style="font-size:20px;text-align:center;line-height:1;"></td>';
            echo '<td class="foxblock-ip">' . esc_html($log->ip) . '</td>';
            echo '<td class="foxblock-url"><code>' . esc_html($log->url) . '</code></td>';
            echo '<td class="foxblock-ua"><span class="foxblock-device-badge">' . esc_html($dv) . '</span> <span class="foxblock-ua-text">' . esc_html($log->user_agent) . '</span></td>';
            echo '<td class="foxblock-action" style="text-align:center;"></td>';
            echo '</tr>';
        }
    }
    wp_send_json_success(array('html' => ob_get_clean()));
}

add_action('wp_ajax_foxblock_clear_logs', 'foxblock_clear_logs_callback');
function foxblock_clear_logs_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    global $wpdb;
    $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}foxblock_log");
    wp_send_json_success('Done');
}

add_action('wp_ajax_foxblock_save_settings', 'foxblock_save_settings_callback');
function foxblock_save_settings_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $bots = isset($_POST['bots']) ? array_map('sanitize_text_field', wp_unslash($_POST['bots'])) : array();
    $bots = array_values(array_unique(array_filter(array_map('trim', $bots))));
    update_option('foxblock_blocked_bots', $bots);
    update_option('foxblock_block_all_non_google', isset($_POST['block_foreign']) ? intval($_POST['block_foreign']) : 0);
    wp_send_json_success('Saved');
}

add_action('wp_ajax_foxblock_save_ips', 'foxblock_save_ips_callback');
function foxblock_save_ips_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $ip_text = isset($_POST['blocked_ips']) ? sanitize_textarea_field(wp_unslash($_POST['blocked_ips'])) : '';
    $clean_ips = array_values(array_filter(array_map('trim', explode("\n", $ip_text))));
    update_option('foxblock_blocked_ips', $clean_ips);
    wp_send_json_success('Saved');
}

add_action('wp_ajax_foxblock_save_whitelist', 'foxblock_save_whitelist_callback');
function foxblock_save_whitelist_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $ip_text = isset($_POST['whitelist_ips']) ? sanitize_textarea_field(wp_unslash($_POST['whitelist_ips'])) : '';
    $clean_ips = array_values(array_filter(array_map('trim', explode("\n", $ip_text))));
    update_option('foxblock_whitelist_ips', $clean_ips);
    wp_send_json_success('Đã lưu danh sách IP an toàn. Có ' . count($clean_ips) . ' IP được bảo vệ.');
}

function foxblock_get_all_scan_files()
{
    $base_dir = wp_normalize_path(ABSPATH);
    $excludes = array(
        $base_dir . 'wp-content/uploads/',
        $base_dir . 'wp-content/cache/',
        $base_dir . 'wp-content/backups/',
        $base_dir . 'wp-content/updraft/',
    );
    $files = array();
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($base_dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::LEAVES_ONLY
    );
    foreach ($iterator as $file) {
        if (!$file->isFile())
            continue;
        $abs = wp_normalize_path($file->getPathname());
        foreach ($excludes as $ex) {
            if (strpos($abs, $ex) === 0)
                continue 2;
        }
        $ext = strtolower(pathinfo($abs, PATHINFO_EXTENSION));
        if (!in_array($ext, array('php', 'js', 'json', 'htaccess', 'sh')))
            continue;
        $rel = ltrim(str_replace($base_dir, '', $abs), '/');
        $files[$rel] = $abs;
    }
    return $files;
}

add_action('wp_ajax_foxblock_scan_core_files', 'foxblock_scan_core_files_callback');
function foxblock_scan_core_files_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $baseline = get_option('foxblock_file_baseline', false);
    $current_files = foxblock_get_all_scan_files();
    if ($baseline === false || empty($baseline)) {
        $new_baseline = array();
        foreach ($current_files as $rel => $abs) {
            $new_baseline[$rel] = md5_file($abs);
        }
        update_option('foxblock_file_baseline', $new_baseline);
        update_option('foxblock_file_baseline_time', current_time('mysql'));
        wp_send_json_success(array(
            'is_init' => true,
            'html' => '<div class="foxblock-fim-clean-msg"><span class="dashicons dashicons-shield"></span> Baseline initialized with ' . count($new_baseline) . ' files.</div>',
            'base_time' => current_time('mysql'),
            'total' => 0,
        ));
        return;
    }
    $results = array('modified' => array(), 'deleted' => array(), 'added' => array());
    foreach ($baseline as $rel_path => $expected_hash) {
        $abs_path = wp_normalize_path(ABSPATH . $rel_path);
        if (!file_exists($abs_path)) {
            $results['deleted'][] = array('path' => $rel_path, 'time' => '-');
        } elseif (md5_file($abs_path) !== $expected_hash) {
            $results['modified'][] = array('path' => $rel_path, 'time' => date('Y-m-d H:i:s', filemtime($abs_path)));
        }
    }
    foreach ($current_files as $rel => $abs) {
        if (!isset($baseline[$rel])) {
            $results['added'][] = array('path' => $rel, 'time' => date('Y-m-d H:i:s', filemtime($abs)));
        }
    }
    $total = count($results['modified']) + count($results['deleted']) + count($results['added']);
    $baseline_time = get_option('foxblock_file_baseline_time', 'Unknown');
    ob_start();
    require FOXBLOCK_PLUGIN_DIR . 'admin/views/fim-result.php';
    wp_send_json_success(array(
        'is_init' => false,
        'html' => ob_get_clean(),
        'base_time' => $baseline_time,
        'total' => $total,
    ));
}

add_action('wp_ajax_foxblock_update_baseline', 'foxblock_update_baseline_callback');
function foxblock_update_baseline_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $current_files = foxblock_get_all_scan_files();
    $new_baseline = array();
    foreach ($current_files as $rel => $abs) {
        $new_baseline[$rel] = md5_file($abs);
    }
    update_option('foxblock_file_baseline', $new_baseline);
    update_option('foxblock_file_baseline_time', current_time('mysql'));
    wp_send_json_success(array(
        'message' => 'Baseline updated successfully!',
        'base_time' => current_time('mysql'),
    ));
}

add_action('wp_ajax_foxblock_malware_init', 'foxblock_malware_init_callback');
function foxblock_malware_init_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    foxblock_load_scanner();

    // Lấy danh sách extensions từ request
    $extensions = isset($_POST['extensions']) ? (array) $_POST['extensions'] : array('php');
    $extensions = array_map('sanitize_text_field', $extensions);

    // Đảm bảo ít nhất có 1 extension
    if (empty($extensions)) {
        $extensions = array('php');
    }

    $files = Foxblock_FileScanner::get_file_list($extensions);
    $scan_id = 'fbscan_' . uniqid();
    set_transient($scan_id . '_files', $files, 600);
    set_transient($scan_id . '_findings', array(), 600);
    wp_send_json_success(array('scan_id' => $scan_id, 'total' => count($files)));
}

add_action('wp_ajax_foxblock_malware_chunk', 'foxblock_malware_chunk_callback');
function foxblock_malware_chunk_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $scan_id = isset($_POST['scan_id']) ? sanitize_text_field($_POST['scan_id']) : '';
    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
    $files = get_transient($scan_id . '_files');
    $findings = get_transient($scan_id . '_findings');
    if ($files === false)
        wp_send_json_error('Session expired. Please restart.');
    foxblock_load_scanner();
    $chunk_findings = Foxblock_FileScanner::scan_chunk($files, $offset, 50);
    $findings = array_merge($findings, $chunk_findings);
    set_transient($scan_id . '_findings', $findings, 600);
    $next = $offset + 50;
    $total = count($files);
    $slice = array_slice($files, $offset, 50);
    wp_send_json_success(array(
        'done' => $next >= $total,
        'next_offset' => $next,
        'scanned' => min($next, $total),
        'total' => $total,
        'current_file' => end($slice) ?: '',
        'findings' => count($findings),
    ));
}

add_action('wp_ajax_foxblock_malware_result', 'foxblock_malware_result_callback');
function foxblock_malware_result_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');
    $scan_id = isset($_POST['scan_id']) ? sanitize_text_field($_POST['scan_id']) : '';
    $findings = get_transient($scan_id . '_findings');
    $files = get_transient($scan_id . '_files');
    if ($findings === false)
        wp_send_json_error('Session expired.');

    // Keep transients for save functionality
    foxblock_load_scanner();
    $scanned = is_array($files) ? count($files) : 0;

    // Get last baseline scan for comparison
    global $wpdb;
    $last_baseline = $wpdb->get_row(
        "SELECT * FROM {$wpdb->prefix}foxblock_malware_scans WHERE is_baseline = 1 ORDER BY scan_time DESC LIMIT 1"
    );

    $comparison = null;
    if ($last_baseline) {
        $comparison = foxblock_compare_scan_results($last_baseline->id, $findings);
    }

    $html = Foxblock_Reporter::render($findings, $scanned, $comparison, $last_baseline);
    wp_send_json_success(array(
        'html' => $html,
        'total' => count($findings),
        'scan_id' => $scan_id,
        'has_baseline' => !empty($last_baseline),
        'findings' => $findings
    ));
}

add_action('wp_ajax_foxblock_ai_analyze_finding', 'foxblock_ai_analyze_finding_callback');
function foxblock_ai_analyze_finding_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error('Unauthorized');
    }

    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Scanner/Fixai_Scranmer.php';

    $file = isset($_POST['file']) ? sanitize_text_field(wp_unslash($_POST['file'])) : '';
    $line = isset($_POST['line']) ? intval($_POST['line']) : 0;
    
    // Attempt to read surrounding context
    $context_code = '';
    if (!empty($file) && $line > 0) {
        $abs_path = wp_normalize_path(ABSPATH . ltrim($file, '/'));
        if (file_exists($abs_path) && is_readable($abs_path)) {
            $all_lines = file($abs_path);
            if ($all_lines !== false) {
                // $line is 1-based, making $line_index 0-based
                $line_index = $line - 1;
                $start = max(0, $line_index - 15);
                $end = min(count($all_lines) - 1, $line_index + 15);
                
                $extracted = array();
                for ($i = $start; $i <= $end; $i++) {
                    $prefix = ($i === $line_index) ? '>> ' . ($i + 1) . ': ' : '   ' . ($i + 1) . ': ';
                    $extracted[] = $prefix . rtrim($all_lines[$i]);
                }
                $context_code = implode("\n", $extracted);
            }
        }
    }
    
    // Fallback if file read fails
    $code = isset($_POST['code']) ? wp_unslash($_POST['code']) : '';
    if (empty($context_code)) {
        $context_code = $code;
    }

    if (empty($context_code)) {
        wp_send_json_success('no');
    }

    $prompt = "Bạn là một chuyên gia bảo mật. Dưới đây là bối cảnh mã nguồn (xung quanh dòng {$line} của tệp {$file}) bị nghi ngờ là mã độc. Các dòng được đánh số và dòng bị nghi ngờ được đánh dấu `>>`.\n\nHãy phân tích cẩn thận xem dòng lệnh này kết hợp với bối cảnh xung quanh có thực sự là mã độc (backdoor, shell ngầm, obfuscated code sinh ra mục đích xấu) hay chỉ là code bình thường (false positive):\n\n" .
        "```php\n" . substr($context_code, 0, 5000) . "\n```\n\n" .
        "LƯU Ý QUAN TRỌNG VỀ ĐỊNH DẠNG TRẢ VỀ:\n" .
        "- Nếu đây thực sự là mã độc hoặc rủi ro cao, hãy trả về một chuỗi bắt đầu bằng 'Lỗi: ' và kèm theo giải thích ngắn gọn.\n" .
        "- Nếu đoạn mã này an toàn (hoặc rủi ro cực thấp), BẠN BẮT BUỘC PHẢI TRẢ VỀ ĐÚNG 1 TỪ DUY NHẤT LÀ 'no', KHÔNG CÓ KÝ TỰ/KÝ HIỆU HOẶC DẤU CHẤM CÂU NÀO KHÁC.\n";

    $api_helper = foxblock_api_helper::foxblock_get_instance();
    $result = $api_helper->foxblock_call_api($prompt, 0.2, 500);

    if (is_wp_error($result)) {
        wp_send_json_error($result->get_error_message());
    }

    wp_send_json_success(trim($result));
}

add_action('wp_ajax_foxblock_save_scan', 'foxblock_save_scan_callback');
function foxblock_save_scan_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $scan_id = isset($_POST['scan_id']) ? sanitize_text_field($_POST['scan_id']) : '';

    // Debug log
    error_log('Foxblock Save Scan - Scan ID: ' . $scan_id);

    $findings = get_transient($scan_id . '_findings');
    $files = get_transient($scan_id . '_files');

    error_log('Foxblock Save Scan - Findings count: ' . (is_array($findings) ? count($findings) : 'not array'));
    error_log('Foxblock Save Scan - Files count: ' . (is_array($files) ? count($files) : 'not array'));

    if ($findings === false) {
        error_log('Foxblock Save Scan - Session expired');
        wp_send_json_error('Session expired. Please run scan again.');
    }

    global $wpdb;

    // Insert scan record
    $result = $wpdb->insert(
        $wpdb->prefix . 'foxblock_malware_scans',
        array(
            'scan_time' => current_time('mysql'),
            'total_files' => is_array($files) ? count($files) : 0,
            'total_findings' => count($findings),
            'is_baseline' => 1
        ),
        array('%s', '%d', '%d', '%d')
    );

    if ($result === false) {
        error_log('Foxblock Save Scan - Failed to insert scan record: ' . $wpdb->last_error);
        wp_send_json_error('Database error: ' . $wpdb->last_error);
    }

    $new_scan_id = $wpdb->insert_id;
    error_log('Foxblock Save Scan - New scan ID: ' . $new_scan_id);

    // Insert findings
    $findings_inserted = 0;
    foreach ($findings as $finding) {
        $insert_result = $wpdb->insert(
            $wpdb->prefix . 'foxblock_malware_findings',
            array(
                'scan_id' => $new_scan_id,
                'file_path' => $finding['file'],
                'line_number' => $finding['line'],
                'code_snippet' => $finding['code'],
                'issue_type' => $finding['label'],
                'risk_level' => $finding['risk'],
                'score' => $finding['score']
            ),
            array('%d', '%s', '%d', '%s', '%s', '%s', '%d')
        );

        if ($insert_result !== false) {
            $findings_inserted++;
        }
    }

    error_log('Foxblock Save Scan - Findings inserted: ' . $findings_inserted);

    // Mark all previous scans as non-baseline
    $wpdb->query(
        $wpdb->prepare(
            "UPDATE {$wpdb->prefix}foxblock_malware_scans SET is_baseline = 0 WHERE id != %d",
            $new_scan_id
        )
    );

    // Clean up transients
    delete_transient($scan_id . '_files');
    delete_transient($scan_id . '_findings');

    error_log('Foxblock Save Scan - Success!');

    wp_send_json_success(array(
        'message' => 'Đã lưu kết quả quét thành công!',
        'scan_time' => current_time('mysql'),
        'scan_id' => $new_scan_id,
        'findings_saved' => $findings_inserted
    ));
}

add_action('wp_ajax_foxblock_clear_scan_data', 'foxblock_clear_scan_data_callback');
function foxblock_clear_scan_data_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    global $wpdb;

    // Count records before deletion
    $scans_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}foxblock_malware_scans");
    $findings_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}foxblock_malware_findings");

    // Delete all findings first (foreign key relationship)
    $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}foxblock_malware_findings");

    // Delete all scans
    $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}foxblock_malware_scans");

    error_log('Foxblock Clear Scan Data - Deleted ' . $scans_count . ' scans and ' . $findings_count . ' findings');

    wp_send_json_success(array(
        'message' => 'Đã xóa toàn bộ dữ liệu quét!',
        'scans_deleted' => $scans_count,
        'findings_deleted' => $findings_count
    ));
}

add_action('wp_ajax_foxblock_export_txt', 'foxblock_export_txt_callback');
function foxblock_export_txt_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $scan_id = isset($_POST['scan_id']) ? sanitize_text_field($_POST['scan_id']) : '';

    $findings = get_transient($scan_id . '_findings');
    $files = get_transient($scan_id . '_files');

    if ($findings === false) {
        wp_send_json_error('Session expired. Please run scan again.');
    }

    foxblock_load_scanner();

    $scanned = is_array($files) ? count($files) : 0;
    $text_content = Foxblock_Reporter::generate_text_report($findings, $scanned);

    // Generate filename with timestamp
    $filename = 'foxblock-malware-report-' . date('Y-m-d-His') . '.txt';

    wp_send_json_success(array(
        'content' => $text_content,
        'filename' => $filename
    ));
}

function foxblock_compare_scan_results($baseline_scan_id, $current_findings)
{
    global $wpdb;

    // Get baseline findings
    $baseline_findings = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}foxblock_malware_findings WHERE scan_id = %d",
            $baseline_scan_id
        ),
        ARRAY_A
    );

    // Create lookup arrays
    $baseline_map = array();
    foreach ($baseline_findings as $bf) {
        $key = $bf['file_path'] . ':' . $bf['line_number'] . ':' . $bf['issue_type'];
        $baseline_map[$key] = $bf;
    }

    $current_map = array();
    foreach ($current_findings as $cf) {
        $key = $cf['file'] . ':' . $cf['line'] . ':' . $cf['label'];
        $current_map[$key] = $cf;
    }

    // Find differences
    $new_issues = array();
    $removed_issues = array();
    $unchanged_issues = array();

    // Find new issues
    foreach ($current_map as $key => $finding) {
        if (!isset($baseline_map[$key])) {
            $new_issues[] = $finding;
        } else {
            $unchanged_issues[] = $finding;
        }
    }

    // Find removed issues
    foreach ($baseline_map as $key => $finding) {
        if (!isset($current_map[$key])) {
            $removed_issues[] = array(
                'file' => $finding['file_path'],
                'line' => $finding['line_number'],
                'code' => $finding['code_snippet'],
                'label' => $finding['issue_type'],
                'risk' => $finding['risk_level'],
                'score' => $finding['score']
            );
        }
    }

    return array(
        'new' => $new_issues,
        'removed' => $removed_issues,
        'unchanged' => $unchanged_issues,
        'has_changes' => !empty($new_issues) || !empty($removed_issues)
    );
}

// AJAX: Batch lookup country codes from JS (cache results in transient)
add_action('wp_ajax_foxblock_lookup_flags', 'foxblock_lookup_flags_callback');
function foxblock_lookup_flags_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $ips = isset($_POST['ips']) ? (array) $_POST['ips'] : array();
    $ips = array_slice(array_map('sanitize_text_field', $ips), 0, 100);

    $result = array();
    $to_fetch = array();

    foreach ($ips as $ip) {
        $cached = get_transient('foxblock_cc_' . md5($ip));
        if ($cached !== false) {
            $result[$ip] = $cached;
        } else {
            $to_fetch[] = $ip;
        }
    }

    if (!empty($to_fetch)) {
        $body = array_map(function ($ip) {
            return array('query' => $ip, 'fields' => 'countryCode,query');
        }, $to_fetch);

        $response = wp_remote_post('http://ip-api.com/batch?fields=countryCode,query', array(
            'timeout' => 10,
            'headers' => array('Content-Type' => 'application/json'),
            'body' => wp_json_encode($body),
        ));

        if (!is_wp_error($response)) {
            $data = json_decode(wp_remote_retrieve_body($response), true);
            if (is_array($data)) {
                foreach ($data as $item) {
                    $ip = isset($item['query']) ? $item['query'] : '';
                    $code = isset($item['countryCode']) ? sanitize_text_field($item['countryCode']) : '';
                    if ($ip) {
                        $result[$ip] = $code;
                        set_transient('foxblock_cc_' . md5($ip), $code, 30 * DAY_IN_SECONDS);
                    }
                }
            }
        }
    }

    wp_send_json_success($result);
}

// AJAX: Quick block IP from log table
add_action('wp_ajax_foxblock_block_ip_quick', 'foxblock_block_ip_quick_callback');
function foxblock_block_ip_quick_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $ip = isset($_POST['ip']) ? sanitize_text_field(trim($_POST['ip'])) : '';
    if (empty($ip))
        wp_send_json_error('Invalid IP');

    $blocked_ips = get_option('foxblock_blocked_ips', array());
    if (!in_array($ip, $blocked_ips, true)) {
        $blocked_ips[] = $ip;
        update_option('foxblock_blocked_ips', $blocked_ips);
    }

    wp_send_json_success('Blocked');
}

// ─── Auto Block Settings AJAX Handlers ────────────────────────────────────────

// AJAX: Save blocked countries
add_action('wp_ajax_foxblock_save_countries', 'foxblock_save_countries_callback');
function foxblock_save_countries_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $countries = isset($_POST['countries']) ? array_map('sanitize_text_field', wp_unslash($_POST['countries'])) : array();
    $countries = array_values(array_unique(array_filter($countries)));

    update_option('foxblock_blocked_countries', $countries);
    wp_send_json_success('Đã lưu cài đặt chặn theo quốc gia. Có ' . count($countries) . ' quốc gia được chặn.');
}

// AJAX: Save blocked behaviors
add_action('wp_ajax_foxblock_save_behaviors', 'foxblock_save_behaviors_callback');
function foxblock_save_behaviors_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $behaviors = isset($_POST['behaviors']) ? array_map('sanitize_text_field', wp_unslash($_POST['behaviors'])) : array();
    $behaviors = array_values(array_unique(array_filter($behaviors)));

    update_option('foxblock_blocked_behaviors', $behaviors);
    wp_send_json_success('Đã lưu cài đặt chặn theo hành vi. Có ' . count($behaviors) . ' hành vi được giám sát.');
}

// AJAX: Save blocked endpoints
add_action('wp_ajax_foxblock_save_endpoints', 'foxblock_save_endpoints_callback');
function foxblock_save_endpoints_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $endpoints = isset($_POST['endpoints']) ? array_map('sanitize_text_field', wp_unslash($_POST['endpoints'])) : array();
    $endpoints = array_values(array_unique(array_filter($endpoints)));

    update_option('foxblock_blocked_endpoints', $endpoints);
    wp_send_json_success('Đã lưu cài đặt chặn endpoint. Có ' . count($endpoints) . ' endpoint/pattern được bảo vệ.');
}

// AJAX: Save Telegram settings
add_action('wp_ajax_foxblock_save_telegram', 'foxblock_save_telegram_callback');
function foxblock_save_telegram_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $enabled = isset($_POST['telegram_enabled']) ? 1 : 0;
    $token = isset($_POST['telegram_token']) ? sanitize_text_field($_POST['telegram_token']) : '';
    $chatid = isset($_POST['telegram_chatid']) ? sanitize_text_field($_POST['telegram_chatid']) : '';

    update_option('foxblock_telegram_enabled', $enabled);
    update_option('foxblock_telegram_token', $token);
    update_option('foxblock_telegram_chatid', $chatid);

    wp_send_json_success('Đã lưu cài đặt Telegram. Thông báo đã được ' . ($enabled ? 'BẬT' : 'TẮT'));
}

// AJAX: Save Advanced Settings (Hide Login)
add_action('wp_ajax_foxblock_save_advanced', 'foxblock_save_advanced_callback');
function foxblock_save_advanced_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $enable = isset($_POST['enable_wp_login_hide']) ? 1 : 0;
    $slug = isset($_POST['login_slug']) ? sanitize_title(trim($_POST['login_slug'], '/')) : '';

    if (in_array($slug, array('wp-login', 'wp-admin', 'login', 'admin', 'dashboard'))) {
        $slug = ''; // Fallback to safe default
    }

    update_option('foxblock_enable_wp_login_hide', $enable);
    delete_option('foxblock_disable_wp_login_hide'); // dọn dẹp option cũ
    update_option('foxblock_login_slug', $slug);

    // Auto sync options internally 
    flush_rewrite_rules(); // Make the new slug take effect immediately

    wp_send_json_success('Đã lưu Cài đặt Nâng cao thành công!');
}

// AJAX: Save AI Settings
add_action('wp_ajax_foxblock_save_ai_settings', 'foxblock_save_ai_settings_callback');
function foxblock_save_ai_settings_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $enabled = isset($_POST['foxblock_ai_enabled']) ? 1 : 0;
    $endpoint = isset($_POST['foxblock_ai_endpoint']) ? sanitize_text_field($_POST['foxblock_ai_endpoint']) : '';
    $model = isset($_POST['foxblock_ai_model']) ? sanitize_text_field($_POST['foxblock_ai_model']) : '';
    $apikey = isset($_POST['foxblock_ai_apikey']) ? sanitize_text_field($_POST['foxblock_ai_apikey']) : '';

    update_option('foxblock_ai_enabled', $enabled);
    update_option('foxblock_ai_endpoint', $endpoint);
    update_option('foxblock_ai_model', $model);
    update_option('foxblock_ai_apikey', $apikey);

    wp_send_json_success('Đã lưu cấu hình phân tích AI.');
}

// AJAX: Save Disabled Security Features
add_action('wp_ajax_foxblock_save_disabled_features', 'foxblock_save_disabled_features_callback');
function foxblock_save_disabled_features_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $allowed_keys = array(
        'disable_rest_api',
        'disable_xmlrpc',
        'disable_wp_embed',
        'disable_rss_feed',
        'disable_author_enum',
        'disable_wp_version',
        'disable_file_edit',
        'disable_pingback',
        'disable_self_pingback',
        'disable_readme',
        'disable_script_concat',
        'disable_application_passwords',
    );

    $features = isset($_POST['disabled_features']) ? (array) $_POST['disabled_features'] : array();
    $features = array_values(array_intersect(array_map('sanitize_text_field', $features), $allowed_keys));

    update_option('foxblock_disabled_features', $features);

    $count = count($features);
    wp_send_json_success('Đã lưu cài đặt bảo mật. ' . ($count > 0 ? $count . ' tính năng dễ bị tấn công đã được vô hiệu hoá.' : 'Không có tính năng nào bị vô hiệu hoá.'));
}

// AJAX: Save Media Filter Settings
add_action('wp_ajax_foxblock_save_media_filter', 'foxblock_save_media_filter_callback');
function foxblock_save_media_filter_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $raw = isset($_POST['media_filter']) ? (array) $_POST['media_filter'] : array();

    $opts = array(
        'enabled' => !empty($raw['enabled']) ? 1 : 0,
        'block_fake_image' => !empty($raw['block_fake_image']) ? 1 : 0,
        'block_svg_malware' => !empty($raw['block_svg_malware']) ? 1 : 0,
        'block_malware_archive' => !empty($raw['block_malware_archive']) ? 1 : 0,
        'block_code_payload' => !empty($raw['block_code_payload']) ? 1 : 0,
    );

    update_option('foxblock_media_filter', $opts);

    $active = array_filter($opts);
    $msg = $opts['enabled']
        ? 'Bộ lọc Media đã được BẬT với ' . (count($active) - 1) . ' bộ lọc hoạt động.'
        : 'Bộ lọc Media đã được TẮT.';

    wp_send_json_success($msg);
}

// AJAX: Test Telegram connection
add_action('wp_ajax_foxblock_test_telegram', 'foxblock_test_telegram_callback');
function foxblock_test_telegram_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $token = isset($_POST['token']) ? sanitize_text_field($_POST['token']) : '';
    $chatid = isset($_POST['chatid']) ? sanitize_text_field($_POST['chatid']) : '';

    if (empty($token) || empty($chatid)) {
        wp_send_json_error('Bot Token và Chat ID không được để trống');
    }

    $message = "🔔 <b>Foxblock Security - Test Message</b>\n\n";
    $message .= "✅ Kết nối Telegram thành công!\n";
    $message .= "📅 Thời gian: " . current_time('Y-m-d H:i:s') . "\n";
    $message .= "🌐 Website: " . get_bloginfo('name') . "\n";
    $message .= "🔗 URL: " . home_url() . "\n\n";
    $message .= "Bạn sẽ nhận được thông báo mỗi khi có IP bị chặn.";

    $result = foxblock_send_telegram_message($token, $chatid, $message);

    if ($result['success']) {
        wp_send_json_success('Gửi tin nhắn thành công! Kiểm tra Telegram của bạn.');
    } else {
        wp_send_json_error($result['error']);
    }
}

// Helper function to send Telegram message
function foxblock_send_telegram_message($token, $chatid, $message)
{
    $url = 'https://api.telegram.org/bot' . $token . '/sendMessage';

    $response = wp_remote_post($url, array(
        'timeout' => 10,
        'body' => array(
            'chat_id' => $chatid,
            'text' => $message,
            'parse_mode' => 'HTML'
        )
    ));

    if (is_wp_error($response)) {
        return array(
            'success' => false,
            'error' => 'Lỗi kết nối: ' . $response->get_error_message()
        );
    }

    $body = wp_remote_retrieve_body($response);
    $data = json_decode($body, true);

    if (isset($data['ok']) && $data['ok'] === true) {
        return array('success' => true);
    } else {
        $error_msg = isset($data['description']) ? $data['description'] : 'Unknown error';
        return array(
            'success' => false,
            'error' => 'Telegram API Error: ' . $error_msg
        );
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// AJAX: Save Master 2FA Settings
// ─────────────────────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_save_2fa_settings', 'foxblock_save_2fa_settings_callback');
function foxblock_save_2fa_settings_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $enabled = isset($_POST['foxblock_2fa_master_enabled']) ? intval($_POST['foxblock_2fa_master_enabled']) : 0;
    update_option('foxblock_2fa_master_enabled', $enabled);

    wp_send_json_success('Đã ' . ($enabled ? 'BẬT' : 'TẮT') . ' cấu hình 2FA toàn trang thành công.');
}

// ─────────────────────────────────────────────────────────────────────────────
// AJAX: Security Audit — Comprehensive security scan & scoring
// ─────────────────────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_security_audit', 'foxblock_security_audit_callback');
function foxblock_security_audit_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error('Unauthorized');

    $checks = array();
    $score = 0;
    $max = 0;

    // Helper to push a check
    $add = function ($cat, $id, $title, $status, $desc, $points, $fix = '') use (&$checks, &$score, &$max) {
        if ($status === 'pass')
            $score += $points;
        if ($status !== 'info')
            $max += $points;
        $checks[] = compact('cat', 'id', 'title', 'status', 'desc', 'points', 'fix');
    };

    // ── 1. WordPress Core ────────────────────────────────────────────────────
    $cat = 'WordPress Core';

    // WP Debug off
    $debug_on = defined('WP_DEBUG') && WP_DEBUG;
    $add(
        $cat,
        'wp_debug',
        'WP_DEBUG tắt trên production',
        $debug_on ? 'fail' : 'pass',
        $debug_on ? 'WP_DEBUG đang BẬT — thông tin lỗi nhạy cảm có thể lộ ra ngoài.' : 'WP_DEBUG đang TẮT — an toàn.',
        6,
        admin_url('admin.php?page=foxblock')
    );

    // Debug log off
    $debug_log = defined('WP_DEBUG_LOG') && WP_DEBUG_LOG;
    $add(
        $cat,
        'wp_debug_log',
        'WP_DEBUG_LOG tắt',
        $debug_log ? 'warn' : 'pass',
        $debug_log ? 'Debug log đang ghi file — tệp debug.log có thể bị truy cập công khai.' : 'Debug log không được bật.',
        4
    );

    // WordPress version up to date
    $wp_ver = get_bloginfo('version');
    $core_updates = get_site_transient('update_core');
    $needs_update = false;
    if (isset($core_updates->updates) && is_array($core_updates->updates)) {
        foreach ($core_updates->updates as $u) {
            if ($u->response === 'upgrade') {
                $needs_update = true;
                break;
            }
        }
    }
    $add(
        $cat,
        'wp_version',
        'WordPress cập nhật mới nhất',
        $needs_update ? 'fail' : 'pass',
        $needs_update ? "WordPress {$wp_ver} có bản cập nhật bảo mật — cần nâng cấp ngay." : "WordPress {$wp_ver} đang là phiên bản mới nhất.",
        8
    );

    // File editing disabled
    $file_edit_off = defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT;
    $add(
        $cat,
        'file_edit',
        'Editor code trong Admin bị tắt',
        $file_edit_off ? 'pass' : 'warn',
        $file_edit_off ? 'DISALLOW_FILE_EDIT = true — kẻ tấn công không thể sửa plugin/theme qua admin.' : 'Editor file đang BẬT — nên tắt trong wp-config.php hoặc qua Foxblock.',
        5,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // SSL
    $is_ssl = is_ssl();
    $add(
        $cat,
        'ssl',
        'HTTPS / SSL đang hoạt động',
        $is_ssl ? 'pass' : 'fail',
        $is_ssl ? 'Trang web đang dùng HTTPS — kết nối được mã hóa.' : 'Trang web chưa dùng HTTPS — dữ liệu đăng nhập có thể bị đánh cắp.',
        8
    );

    // Default "admin" user removed
    $admin_exists = username_exists('admin');
    $add(
        $cat,
        'admin_user',
        'Không tồn tại user "admin" mặc định',
        $admin_exists ? 'fail' : 'pass',
        $admin_exists ? 'User "admin" vẫn tồn tại — đây là mục tiêu brute force phổ biến nhất.' : 'Không có user tên "admin" — tốt.',
        7,
        admin_url('users.php')
    );

    // DB prefix not wp_
    global $wpdb;
    $db_prefix_safe = ($wpdb->prefix !== 'wp_');
    $add(
        $cat,
        'db_prefix',
        'Database prefix không phải "wp_"',
        $db_prefix_safe ? 'pass' : 'warn',
        $db_prefix_safe ? "DB prefix là \"{$wpdb->prefix}\" — không phải mặc định, tốt hơn." : 'DB prefix vẫn là "wp_" — dễ bị tấn công SQL Injection có mục tiêu.',
        4
    );

    // ── 2. Foxblock Protection ───────────────────────────────────────────────
    $cat = 'Foxblock Protection';

    // Bot blocking active
    $blocked_bots = get_option('foxblock_blocked_bots', array());
    $bots_active = !empty($blocked_bots) && is_array($blocked_bots) && count($blocked_bots) > 0;
    $add(
        $cat,
        'bot_blocking',
        'Foxblock chặn bot đang hoạt động',
        $bots_active ? 'pass' : 'fail',
        $bots_active ? count($blocked_bots) . ' bot AI/crawler đang bị chặn.' : 'Danh sách chặn bot rỗng — bật ngay trong Foxblock.',
        6,
        admin_url('admin.php?page=foxblock#settings')
    );

    // IP blocklist
    $blocked_ips = get_option('foxblock_blocked_ips', array());
    $add(
        $cat,
        'ip_blocklist',
        'Danh sách IP bị chặn đang hoạt động',
        !empty($blocked_ips) ? 'pass' : 'info',
        !empty($blocked_ips) ? count($blocked_ips) . ' IP đang bị chặn.' : 'Chưa có IP nào trong danh sách chặn (bình thường nếu website mới).',
        2,
        admin_url('admin.php?page=foxblock#ipblock')
    );

    // Behavior blocking
    $blocked_behaviors = get_option('foxblock_blocked_behaviors', array());
    $behaviors_on = !empty($blocked_behaviors) && is_array($blocked_behaviors);
    $add(
        $cat,
        'behavior_block',
        'Chặn theo hành vi bất thường',
        $behaviors_on ? 'pass' : 'warn',
        $behaviors_on ? 'Chặn hành vi đang BẬT với ' . count($blocked_behaviors) . ' quy tắc.' : 'Chặn theo hành vi chưa được cấu hình.',
        5,
        admin_url('admin.php?page=foxblock#autoblock')
    );

    // Endpoint blocking
    $blocked_endpoints = get_option('foxblock_blocked_endpoints', array());
    $endpoints_on = !empty($blocked_endpoints);
    $add(
        $cat,
        'endpoint_block',
        'Chặn endpoint nguy hiểm (xmlrpc, path traversal…)',
        $endpoints_on ? 'pass' : 'warn',
        $endpoints_on ? 'Đang chặn ' . count($blocked_endpoints) . ' loại endpoint nguy hiểm.' : 'Chưa bật chặn endpoint — dễ bị khai thác.',
        5,
        admin_url('admin.php?page=foxblock#autoblock')
    );

    // Media filter
    $media_opts = get_option('foxblock_media_filter', array());
    $media_on = !empty($media_opts['enabled']);
    $add(
        $cat,
        'media_filter',
        'Bộ lọc Media (chặn upload độc hại)',
        $media_on ? 'pass' : 'warn',
        $media_on ? 'Bộ lọc upload đang BẬT — file độc hại bị chặn khi upload.' : 'Bộ lọc upload chưa bật — có thể upload shell qua media.',
        6,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // ── 3. Access Control & Privacy ──────────────────────────────────────────
    $cat = 'Kiểm soát Truy cập';

    $disabled_features = get_option('foxblock_disabled_features', array());

    // REST API restricted
    $rest_off = in_array('disable_rest_api', $disabled_features);
    $add(
        $cat,
        'rest_api',
        'REST API hạn chế cho khách',
        $rest_off ? 'pass' : 'warn',
        $rest_off ? 'REST API bị vô hiệu hoá cho người chưa đăng nhập.' : 'REST API mở công khai — có thể bị dùng để liệt kê users/nội dung.',
        5,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // XML-RPC
    $xmlrpc_off = in_array('disable_xmlrpc', $disabled_features);
    $add(
        $cat,
        'xmlrpc',
        'XML-RPC bị tắt',
        $xmlrpc_off ? 'pass' : 'fail',
        $xmlrpc_off ? 'XML-RPC đã bị vô hiệu hoá hoàn toàn.' : 'XML-RPC vẫn mở — thường xuyên bị tấn công brute force!',
        7,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // User enumeration
    $user_enum_off = in_array('disable_author_enum', $disabled_features);
    $add(
        $cat,
        'user_enum',
        'Chặn dò tìm username (?author=)',
        $user_enum_off ? 'pass' : 'warn',
        $user_enum_off ? 'Author enumeration (?author=N) đã bị chặn.' : '?author=N có thể bị dùng để lộ tên user.',
        5,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // WP version hidden
    $ver_hidden = in_array('disable_wp_version', $disabled_features);
    $add(
        $cat,
        'wp_version_hidden',
        'Ẩn phiên bản WordPress',
        $ver_hidden ? 'pass' : 'warn',
        $ver_hidden ? 'Phiên bản WP đã bị ẩn khỏi mã HTML và RSS.' : 'Phiên bản WordPress hiển thị công khai — kẻ tấn công biết nên dùng exploit nào.',
        4,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // Pingbacks
    $ping_off = in_array('disable_pingback', $disabled_features);
    $add(
        $cat,
        'pingback',
        'Pingback / Trackback tắt',
        $ping_off ? 'pass' : 'info',
        $ping_off ? 'Pingback và trackback đã tắt.' : 'Pingback đang bật — có thể dùng để DDoS khuếch đại.',
        3,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // Application Passwords
    $app_pw_off = in_array('disable_application_passwords', $disabled_features);
    $add(
        $cat,
        'app_passwords',
        'Application Passwords tắt',
        $app_pw_off ? 'pass' : 'warn',
        $app_pw_off ? 'Application Passwords đã tắt.' : 'Application Passwords đang bật — có thể bị dùng để bypass 2FA.',
        4,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // Readme.html blocked
    $readme_off = in_array('disable_readme', $disabled_features);
    $add(
        $cat,
        'readme',
        'readme.html / license.txt bị chặn',
        $readme_off ? 'pass' : 'warn',
        $readme_off ? 'readme.html và license.txt đã bị chặn truy cập.' : 'readme.html vẫn truy cập được — lộ phiên bản WP.',
        3,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // ── 4. Login Security ────────────────────────────────────────────────────
    $cat = 'Bảo mật Đăng nhập';

    // Login URL hidden (custom login)
    $custom_login = get_option('foxblock_login_slug', '');
    $login_hidden = !empty($custom_login);
    $add(
        $cat,
        'login_url',
        'URL đăng nhập được ẩn (khác /wp-login.php)',
        $login_hidden ? 'pass' : 'warn',
        $login_hidden ? "URL đăng nhập tuỳ chỉnh: /{$custom_login}" : '/wp-login.php vẫn là URL mặc định — bot brute force rất dễ tìm.',
        6,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // Country blocking
    $blocked_countries = get_option('foxblock_blocked_countries', array());
    $countries_on = !empty($blocked_countries);
    $add(
        $cat,
        'country_block',
        'Chặn theo quốc gia',
        $countries_on ? 'pass' : 'info',
        $countries_on ? 'Đang chặn traffic từ ' . count($blocked_countries) . ' quốc gia.' : 'Chưa bật chặn theo quốc gia (tuỳ chọn, không bắt buộc).',
        2,
        admin_url('admin.php?page=foxblock#autoblock')
    );

    // 2FA - TOTP
    $two_fa_enabled = get_option('foxblock_2fa_master_enabled', 0);
    $add(
        $cat,
        '2fa_totp',
        'Xác thực 2 lớp (2FA — TOTP)',
        $two_fa_enabled ? 'pass' : 'warn',
        $two_fa_enabled ? 'Xác thực 2 lớp (2FA) đã được BẬT — bảo vệ tài khoản khỏi nguy cơ lộ mật khẩu.' : 'Xác thực 2 lớp chưa được bật — nên bật để tăng cường bảo mật đăng nhập.',
        6,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // ── 5. Server & PHP ──────────────────────────────────────────────────────
    $cat = 'Server & PHP';

    // PHP version
    $php_ver = phpversion();
    $php_major = (int) PHP_MAJOR_VERSION;
    $php_minor = (int) PHP_MINOR_VERSION;
    $php_ok = ($php_major > 8) || ($php_major === 8 && $php_minor >= 1);
    $add(
        $cat,
        'php_version',
        'Phiên bản PHP >= 8.1',
        $php_ok ? 'pass' : ($php_major >= 7 ? 'warn' : 'fail'),
        $php_ok ? "PHP {$php_ver} — phiên bản an toàn và được hỗ trợ bảo mật." : "PHP {$php_ver} — đã hết hỗ trợ bảo mật, nên nâng cấp lên PHP 8.1+.",
        6
    );

    // display_errors off
    $display_errors = ini_get('display_errors');
    $add(
        $cat,
        'display_errors',
        'display_errors tắt',
        (!$display_errors || $display_errors === 'Off' || $display_errors === '0') ? 'pass' : 'fail',
        (!$display_errors || $display_errors === 'Off' || $display_errors === '0') ? 'display_errors tắt trên server.' : 'display_errors đang BẬT — thông tin lỗi PHP có thể lộ ra ngoài!',
        5
    );

    // WP Embed off
    $embed_off = in_array('disable_wp_embed', $disabled_features);
    $add(
        $cat,
        'wp_embed',
        'WP Embed / oEmbed giới hạn',
        $embed_off ? 'pass' : 'info',
        $embed_off ? 'oEmbed đã bị tắt — giảm attack surface.' : 'oEmbed đang hoạt động (tuỳ chọn tắt để giảm attack surface).',
        2,
        admin_url('admin.php?page=foxblock#advanced')
    );

    // ── 6. HTTP Security Headers ──────────────────────────────────────────────
    $cat = 'HTTP Security Headers';

    // Fetch homepage headers via a self-request (non-blocking, short timeout)
    $response_headers = array();
    $home_url = trailingslashit(home_url());
    $http_resp = wp_remote_get($home_url, array(
        'timeout' => 8,
        'sslverify' => false,
        'user-agent' => 'FoxblockAudit/1.0',
    ));

    if (!is_wp_error($http_resp)) {
        $raw_headers = wp_remote_retrieve_headers($http_resp);
        // Normalize header names to lowercase
        foreach ($raw_headers as $name => $value) {
            $response_headers[strtolower($name)] = is_array($value) ? implode(', ', $value) : $value;
        }
    } else {
        // Fallback: try apache_response_headers() (works when running in Apache)
        if (function_exists('apache_response_headers')) {
            foreach (apache_response_headers() as $name => $value) {
                $response_headers[strtolower($name)] = $value;
            }
        }
    }

    // Helper: check if header present and non-empty
    $has_header = function ($name) use ($response_headers) {
        $key = strtolower($name);
        return !empty($response_headers[$key]);
    };
    $header_val = function ($name) use ($response_headers) {
        return $response_headers[strtolower($name)] ?? '';
    };

    // a) X-Frame-Options
    $xfo = $has_header('x-frame-options');
    $add(
        $cat,
        'header_xfo',
        'X-Frame-Options (Chống Clickjacking)',
        $xfo ? 'pass' : 'fail',
        $xfo
        ? 'X-Frame-Options: ' . $header_val('x-frame-options') . ' — ngăn iframe ăn cắp click.'
        : 'Header X-Frame-Options chưa được set — website có thể bị nhúng vào iframe (Clickjacking).',
        5
    );

    // b) X-Content-Type-Options
    $xcto = $has_header('x-content-type-options');
    $add(
        $cat,
        'header_xcto',
        'X-Content-Type-Options: nosniff (Chống MIME Sniffing)',
        $xcto ? 'pass' : 'fail',
        $xcto
        ? 'X-Content-Type-Options: nosniff — trình duyệt không đoán MIME type.'
        : 'Header X-Content-Type-Options chưa được set — trình duyệt có thể hiểu nhầm loại file (MIME sniffing).',
        4
    );

    // c) HSTS
    $hsts = $has_header('strict-transport-security');
    $add(
        $cat,
        'header_hsts',
        'Strict-Transport-Security / HSTS (Bắt buộc HTTPS)',
        $hsts ? 'pass' : ($is_ssl ? 'warn' : 'info'),
        $hsts
        ? 'HSTS: ' . $header_val('strict-transport-security') . ' — trình duyệt chỉ kết nối HTTPS.'
        : ($is_ssl
            ? 'Website dùng HTTPS nhưng chưa bật HSTS — nên thêm header này qua server hoặc plugin.'
            : 'HSTS chỉ có tác dụng sau khi bật HTTPS.'),
        5
    );

    // d) Content-Security-Policy
    $csp = $has_header('content-security-policy') || $has_header('content-security-policy-report-only');
    $csp_val = $header_val('content-security-policy') ?: $header_val('content-security-policy-report-only');
    $add(
        $cat,
        'header_csp',
        'Content-Security-Policy (CSP)',
        $csp ? 'pass' : 'warn',
        $csp
        ? 'CSP đã được thiết lập — kiểm soát nguồn tải script/style.'
        : 'Content-Security-Policy chưa được cấu hình — nguy cơ XSS cao hơn.',
        5
    );

    // e) Referrer-Policy
    $rp = $has_header('referrer-policy');
    $add(
        $cat,
        'header_rp',
        'Referrer-Policy (Kiểm soát Referrer)',
        $rp ? 'pass' : 'warn',
        $rp
        ? 'Referrer-Policy: ' . $header_val('referrer-policy') . '.'
        : 'Referrer-Policy chưa được set — URL trang (có thể chứa tham số nhạy cảm) sẽ bị gửi cho bên thứ ba.',
        3
    );

    // f) Permissions-Policy (Feature-Policy cũ)
    $pp = $has_header('permissions-policy') || $has_header('feature-policy');
    $add(
        $cat,
        'header_pp',
        'Permissions-Policy (Giới hạn API trình duyệt)',
        $pp ? 'pass' : 'info',
        $pp
        ? 'Permissions-Policy đã thiết lập — hạn chế camera, micro, geolocation…'
        : 'Permissions-Policy chưa được set (không bắt buộc, nhưng tốt cho bảo mật nâng cao).',
        2
    );

    // g) X-XSS-Protection (legacy nhưng vẫn hữu ích cho IE/Edge cũ)
    $xxss = $has_header('x-xss-protection');
    $add(
        $cat,
        'header_xxss',
        'X-XSS-Protection (Bảo vệ XSS trình duyệt cũ)',
        $xxss ? 'pass' : 'info',
        $xxss
        ? 'X-XSS-Protection: ' . $header_val('x-xss-protection') . '.'
        : 'X-XSS-Protection chưa được set (không quan trọng với trình duyệt hiện đại, nhưng tốt cho IE/Edge cũ).',
        2
    );

    // ── Tính điểm ────────────────────────────────────────────────────────────
    $final_score = $max > 0 ? round(($score / $max) * 100) : 0;

    $pass = array_filter($checks, fn($c) => $c['status'] === 'pass');
    $fail = array_filter($checks, fn($c) => $c['status'] === 'fail');
    $warn = array_filter($checks, fn($c) => $c['status'] === 'warn');
    $info = array_filter($checks, fn($c) => $c['status'] === 'info');

    // Group by category for JS rendering
    $by_cat = array();
    foreach ($checks as $c) {
        $by_cat[$c['cat']][] = $c;
    }

    wp_send_json_success(array(
        'score' => $final_score,
        'pass' => count($pass),
        'fail' => count($fail),
        'warn' => count($warn),
        'info' => count($info),
        'total' => count($checks),
        'groups' => $by_cat,
        'php_ver' => phpversion(),
        'wp_ver' => get_bloginfo('version'),
    ));
}

// ══════════════════════════════════════════════════════════════════════════════
// BACKUP & RESTORE SYSTEM
// ══════════════════════════════════════════════════════════════════════════════

function foxblock_load_backup_classes()
{
    static $loaded = false;
    if ($loaded)
        return;
    $dir = FOXBLOCK_PLUGIN_DIR . 'includes/Backup/';
    require_once $dir . 'StateManager.php';
    require_once $dir . 'SecurityHelper.php';
    require_once $dir . 'DatabaseExporter.php';
    require_once $dir . 'FileHandler.php';
    require_once $dir . 'BackupManager.php';
    require_once $dir . 'RestoreManager.php';
    require_once $dir . 'Google/class-google-oauth-handler.php';
    require_once $dir . 'Google/class-google-drive-service.php';
    $loaded = true;
}

// ── Backup Step ───────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_step', 'foxblock_backup_step_callback');
function foxblock_backup_step_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();

    // Nếu có tham số 'new' = 1, xóa state cũ và bắt đầu mới
    if (!empty($_POST['new'])) {
        Foxblock_BackupManager::cancel();
    }

    // Tăng thời gian thực thi
    @set_time_limit(30);

    $result = Foxblock_BackupManager::run_step();

    if (!empty($result['error'])) {
        Foxblock_Backup_StateManager::clear_backup_state();
        wp_send_json_error(['message' => $result['error']]);
    }

    wp_send_json_success($result);
}

// ── Cancel Backup ─────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_cancel', 'foxblock_backup_cancel_callback');
function foxblock_backup_cancel_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);
    foxblock_load_backup_classes();
    Foxblock_BackupManager::cancel();
    wp_send_json_success(['message' => 'Đã hủy backup.']);
}

// ── Backup List ───────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_list', 'foxblock_backup_list_callback');
function foxblock_backup_list_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);
    foxblock_load_backup_classes();
    $backups = Foxblock_Backup_FileHandler::list_backups();
    wp_send_json_success(['backups' => $backups]);
}

// ── Download GDrive to Local ──────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_download_gdrive', 'foxblock_backup_download_gdrive_callback');
function foxblock_backup_download_gdrive_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => 'Unauthorized']);
    }

    $filename = isset($_POST['filename']) ? sanitize_file_name($_POST['filename']) : '';
    $gdrive_id = isset($_POST['gdrive_id']) ? sanitize_text_field($_POST['gdrive_id']) : '';

    if (empty($filename) || empty($gdrive_id)) {
        wp_send_json_error(['message' => 'Dữ liệu không hợp lệ']);
    }

    foxblock_load_backup_classes();
    $backup_dir = Foxblock_Backup_SecurityHelper::get_backup_dir();
    $dest_path = $backup_dir . '/' . $filename;

    if (file_exists($dest_path)) {
        wp_send_json_success(['message' => 'File đã tồn tại ở local.']);
    }

    require_once FOXBLOCK_PLUGIN_DIR . 'includes/Backup/Google/class-google-drive-service.php';
    $service = new Foxblock_Backup_Google_Drive_Service();
    
    if (!$service->is_ready()) {
        wp_send_json_error(['message' => 'Google Drive chưa kết nối']);
    }

    $result = $service->download_file($gdrive_id, $dest_path);

    if (is_wp_error($result)) {
        wp_send_json_error(['message' => $result->get_error_message()]);
    }

    wp_send_json_success(['message' => 'Tải về local thành công']);
}

// ── Delete Backup ─────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_delete', 'foxblock_backup_delete_callback');
function foxblock_backup_delete_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();
    $filename = isset($_POST['filename']) ? sanitize_file_name($_POST['filename']) : '';
    $gdrive_id = isset($_POST['gdrive_id']) ? sanitize_text_field($_POST['gdrive_id']) : '';

    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    if (empty($filename) || !in_array($ext, ['foxblock', 'txt'])) {
        wp_send_json_error(['message' => 'Tên file không hợp lệ']);
    }

    $backup_dir = Foxblock_Backup_SecurityHelper::get_backup_dir();
    $file_path = $backup_dir . '/' . $filename;

    $deleted_local = false;
    if (file_exists($file_path)) {
        if (realpath($file_path) === realpath($backup_dir) . DIRECTORY_SEPARATOR . $filename) {
            @unlink($file_path);
            $deleted_local = true;
        }
    }

    $deleted_gdrive = false;
    if (!empty($gdrive_id)) {
        $service = new Foxblock_Backup_Google_Drive_Service();
        if ($service->is_ready()) {
            if ($service->delete_file($gdrive_id)) {
                $deleted_gdrive = true;
            }
        }
    }

    if (!$deleted_local && !$deleted_gdrive) {
        wp_send_json_error(['message' => 'Không tìm thấy file cục bộ lẫn trên Google Drive, hoặc không thể xóa']);
    }

    wp_send_json_success(['message' => 'Đã xóa backup: ' . $filename . ($deleted_gdrive ? ' (và trên GDrive)' : '')]);
}

// ── Download Token ────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_download_token', 'foxblock_backup_download_token_callback');
function foxblock_backup_download_token_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();
    $filename = isset($_POST['file']) ? sanitize_file_name($_POST['file']) : '';
    if (empty($filename) || pathinfo($filename, PATHINFO_EXTENSION) !== 'foxblock') {
        wp_send_json_error(['message' => 'File không hợp lệ']);
    }

    $backup_dir = Foxblock_Backup_SecurityHelper::get_backup_dir();
    if (!file_exists($backup_dir . '/' . $filename)) {
        wp_send_json_error(['message' => 'File không tồn tại']);
    }

    $token = Foxblock_Backup_SecurityHelper::create_download_token($filename);
    $url = admin_url('admin-ajax.php?action=foxblock_backup_download&token=' . $token . '&nonce=' . wp_create_nonce('foxblock_nonce'));
    wp_send_json_success(['url' => $url]);
}

// ── Download File (GET request) ───────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_download', 'foxblock_backup_download_callback');
function foxblock_backup_download_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_die('Unauthorized', 403);

    foxblock_load_backup_classes();
    $token = isset($_GET['token']) ? sanitize_text_field($_GET['token']) : '';
    $filename = Foxblock_Backup_SecurityHelper::verify_download_token($token);

    if (!$filename)
        wp_die('Token không hợp lệ hoặc đã hết hạn.', 403);

    $backup_dir = Foxblock_Backup_SecurityHelper::get_backup_dir();
    $file_path = $backup_dir . '/' . $filename;

    if (!file_exists($file_path))
        wp_die('File không tồn tại.', 404);

    // Stream download
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Content-Length: ' . filesize($file_path));
    header('Cache-Control: no-cache');
    ob_end_clean();

    $fh = fopen($file_path, 'rb');
    while (!feof($fh)) {
        echo fread($fh, 65536);
        flush();
    }
    fclose($fh);
    exit;
}

// ── Save Schedule ─────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_backup_save_schedule', 'foxblock_backup_save_schedule_callback');
function foxblock_backup_save_schedule_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);

    $schedule = isset($_POST['schedule']) ? sanitize_text_field($_POST['schedule']) : 'disabled';
    $retention = isset($_POST['retention']) ? intval($_POST['retention']) : 5;
    $time_str = isset($_POST['time']) ? sanitize_text_field($_POST['time']) : '02:00';
    
    // Nơi lưu trữ (local hoặc gdrive)
    $storage = isset($_POST['storage']) ? sanitize_text_field($_POST['storage']) : 'local';
    // Thư mục Drive
    $gdrive_folder = isset($_POST['gdrive_folder']) ? sanitize_text_field($_POST['gdrive_folder']) : 'Foxblock Backups';

    $allowed = ['disabled', 'daily', 'weekly', 'monthly'];
    if (!in_array($schedule, $allowed))
        $schedule = 'disabled';
    if ($retention < 1)
        $retention = 1;
    if ($retention > 30)
        $retention = 30;

    // Validate time format (HH:MM)
    if (!preg_match('/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/', $time_str)) {
        $time_str = '02:00';
    }

    update_option('foxblock_backup_schedule', $schedule);
    update_option('foxblock_backup_retention', $retention);
    update_option('foxblock_backup_time', $time_str);
    update_option('foxblock_backup_storage', $storage);
    update_option('foxblock_gdrive_folder', $gdrive_folder);

    // Cập nhật cron schedule
    wp_clear_scheduled_hook('foxblock_auto_backup');
    if ($schedule !== 'disabled') {
        $interval_map = ['daily' => 'daily', 'weekly' => 'weekly', 'monthly' => 'monthly'];
        $wp_interval = $interval_map[$schedule] ?? 'daily';
        
        // Tự động tính thời gian Cron chạy dựa vào đúng WP Timezone thay vì PHP Timezone
        try {
            $wp_tz = wp_timezone();
            $date = new DateTime('now', $wp_tz);
            $parts = explode(':', $time_str);
            $date->setTime((int)$parts[0], (int)$parts[1], 0);

            if ($date->getTimestamp() <= time()) {
                $date->modify('+1 day');
            }
            $utc_timestamp = $date->getTimestamp();
        } catch (Exception $e) {
            $utc_timestamp = time() + 3600; 
        }
        
        wp_schedule_event($utc_timestamp, $wp_interval, 'foxblock_auto_backup');
    }

    wp_send_json_success(['message' => 'Đã lưu lịch backup' . ($schedule !== 'disabled' ? ' (' . $schedule . ')' : ' — đã tắt') . '.']);
}

// ── Auto Backup Cron Hook & Schedules ──────────────────────────────────────────
add_filter('cron_schedules', 'foxblock_custom_cron_schedules');
function foxblock_custom_cron_schedules($schedules) {
    if (!isset($schedules['weekly'])) {
        $schedules['weekly'] = [
            'interval' => 604800, // 7 days
            'display'  => 'Hàng tuần'
        ];
    }
    if (!isset($schedules['monthly'])) {
        $schedules['monthly'] = [
            'interval' => 2592000, // 30 days
            'display'  => 'Hàng tháng'
        ];
    }
    return $schedules;
}

add_action('foxblock_auto_backup', 'foxblock_run_auto_backup');
function foxblock_run_auto_backup()
{
    foxblock_load_backup_classes();

    $state = Foxblock_Backup_StateManager::get_backup_state();

    // Nếu chưa có state, hoặc state là backup manual, hoặc chạy xong rồi -> Khởi tạo mới
    if (!$state || $state['step'] === 'done' || empty($state['meta']['trigger']) || $state['meta']['trigger'] !== 'auto') {
        Foxblock_BackupManager::cancel();
        Foxblock_Backup_StateManager::init_backup_state(['trigger' => 'auto']);
    }

    // Chạy loop tối đa 20 giây để không bị PHP timeout (giả sử timeout thường là 30-120s)
    $start_time = time();
    $max_runtime = 20;

    while ((time() - $start_time) < $max_runtime) {
        $state = Foxblock_Backup_StateManager::get_backup_state();
        if (!$state || $state['step'] === 'done') {
            break;
        }
        
        $result = Foxblock_BackupManager::run_step();
        
        if (!empty($result['done']) && $result['done'] === true) {
            break;
        }
        if (!empty($result['error'])) {
            Foxblock_Backup_StateManager::clear_backup_state();
            break;
        }
    }

    // Kiểm tra lại state sau vòng lặp
    $state = Foxblock_Backup_StateManager::get_backup_state();
    if ($state && $state['step'] !== 'done') {
        // Chưa xong, lên lịch chèn ngay một event 1 lần vào thời điểm hiện tại để cron tiếp tục chạy
        if (!wp_next_scheduled('foxblock_auto_backup_worker')) {
            wp_schedule_single_event(time(), 'foxblock_auto_backup_worker');
        }
    }
}
add_action('foxblock_auto_backup_worker', 'foxblock_run_auto_backup');

// ── Restore Upload ────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_restore_upload_chunk', 'foxblock_restore_upload_chunk_callback');
function foxblock_restore_upload_chunk_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();

    if (empty($_FILES['foxblock_file'])) {
        wp_send_json_error(['message' => 'Không tìm thấy file upload']);
    }

    $file = $_FILES['foxblock_file'];
    $upload_id = isset($_POST['upload_id']) ? sanitize_text_field($_POST['upload_id']) : '';
    $chunk_index = isset($_POST['chunk_index']) ? intval($_POST['chunk_index']) : 0;
    $total_chunks = isset($_POST['total_chunks']) ? intval($_POST['total_chunks']) : 1;
    $is_last = isset($_POST['is_last']) && $_POST['is_last'] == 1;
    $filename = isset($_POST['filename']) ? sanitize_file_name($_POST['filename']) : 'backup.foxblock';

    if (empty($upload_id)) {
        wp_send_json_error(['message' => 'Thiếu Upload ID']);
    }

    $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    if ($ext !== 'foxblock') {
        wp_send_json_error(['message' => 'File phải có định dạng .foxblock']);
    }

    $temp_dir = wp_upload_dir()['basedir'] . '/foxblock_temp';
    wp_mkdir_p($temp_dir);
    $dest_path = $temp_dir . '/restore_' . $upload_id . '_' . $filename;

    // Stream append chunk to the destination file
    $in = @fopen($file['tmp_name'], 'rb');
    $out = @fopen($dest_path, 'ab');
    $success = false;
    if ($in && $out) {
        while (!feof($in)) {
            fwrite($out, fread($in, 1048576)); // 1MB writing chunks
        }
        fclose($in);
        fclose($out);
        $success = true;
    }

    if (!$success) {
        wp_send_json_error(['message' => 'Không thể lưu phần dữ liệu tải lên']);
    }

    if ($is_last) {
        // Validate the entire assembled file
        $validation = Foxblock_Backup_SecurityHelper::validate_upload(
            $dest_path,
            $filename,
            filesize($dest_path)
        );

        if (!$validation['valid']) {
            @unlink($dest_path);
            wp_send_json_error(['message' => $validation['error']]);
        }

        // Khởi tạo restore state
        @set_time_limit(300); // 5 minutes max for extraction/init just in case
        $result = Foxblock_RestoreManager::init($dest_path);

        if (!empty($result['error'])) {
            @unlink($dest_path);
            wp_send_json_error(['message' => $result['error']]);
        }

        // Lấy token để trả về JS
        $state = Foxblock_Backup_StateManager::get_restore_state();
        $result['restore_token'] = $state['restore_token'] ?? '';

        wp_send_json_success($result);
    } else {
        wp_send_json_success(['message' => 'Chunk uploaded']);
    }
}

// ── Restore Init From Server ──────────────────────────────────────────────────
add_action('wp_ajax_foxblock_restore_init_from_server', 'foxblock_restore_init_from_server_callback');
function foxblock_restore_init_from_server_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options'))
        wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();

    $filename = isset($_POST['filename']) ? sanitize_file_name($_POST['filename']) : '';
    if (empty($filename) || pathinfo($filename, PATHINFO_EXTENSION) !== 'foxblock') {
        wp_send_json_error(['message' => 'Tên file không hợp lệ']);
    }

    $backup_dir = Foxblock_Backup_SecurityHelper::get_backup_dir();
    $archive_path = $backup_dir . '/' . $filename;

    if (!file_exists($archive_path)) {
        wp_send_json_error(['message' => 'File backup không tồn tại']);
    }

    @set_time_limit(30);
    $result = Foxblock_RestoreManager::init($archive_path);

    if (!empty($result['error'])) {
        wp_send_json_error(['message' => $result['error']]);
    }

    $state = Foxblock_Backup_StateManager::get_restore_state();
    $result['restore_token'] = $state['restore_token'] ?? '';

    wp_send_json_success($result);
}

// ── Restore Step ──────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_restore_step', 'foxblock_restore_step_callback');
add_action('wp_ajax_nopriv_foxblock_restore_step', 'foxblock_restore_step_callback');
function foxblock_restore_step_callback()
{
    foxblock_load_backup_classes();
    $state = Foxblock_Backup_StateManager::get_restore_state();
    $token = isset($_POST['restore_token']) ? sanitize_text_field($_POST['restore_token']) : '';

    $is_authorized = false;
    if ($state && !empty($state['restore_token']) && hash_equals($state['restore_token'], $token)) {
        $is_authorized = true;
    } else {
        if (check_ajax_referer('foxblock_nonce', 'nonce', false) && current_user_can('manage_options')) {
            $is_authorized = true;
        }
    }

    if (!$is_authorized) {
        wp_send_json_error(['message' => 'Unauthorized'], 403);
    }

    foxblock_load_backup_classes();
    @set_time_limit(30);

    $result = Foxblock_RestoreManager::run_step();

    if (!empty($result['error'])) {
        Foxblock_Backup_StateManager::clear_restore_state();
        wp_send_json_error(['message' => $result['error']]);
    }

    wp_send_json_success($result);
}

// ── Cancel Restore ────────────────────────────────────────────────────────────
add_action('wp_ajax_foxblock_restore_cancel', 'foxblock_restore_cancel_callback');
add_action('wp_ajax_nopriv_foxblock_restore_cancel', 'foxblock_restore_cancel_callback');
function foxblock_restore_cancel_callback()
{
    foxblock_load_backup_classes();
    $state = Foxblock_Backup_StateManager::get_restore_state();
    $token = isset($_POST['restore_token']) ? sanitize_text_field($_POST['restore_token']) : '';

    $is_authorized = false;
    if ($state && !empty($state['restore_token']) && hash_equals($state['restore_token'], $token)) {
        $is_authorized = true;
    } else {
        if (check_ajax_referer('foxblock_nonce', 'nonce', false) && current_user_can('manage_options')) {
            $is_authorized = true;
        }
    }

    if (!$is_authorized) {
        wp_send_json_error(['message' => 'Unauthorized'], 403);
    }
    
    Foxblock_RestoreManager::cancel();
    wp_send_json_success(['message' => 'Đã hủy khôi phục.']);
}

// ── Google Drive API AJAX ───────────────────────────────────────────────────

add_action('wp_ajax_foxblock_gdrive_auth_url', 'foxblock_gdrive_auth_url_callback');
function foxblock_gdrive_auth_url_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    if (!empty($_POST['client_id']) && !empty($_POST['client_secret'])) {
        update_option('foxblock_gdrive_client_id', sanitize_text_field($_POST['client_id']));
        update_option('foxblock_gdrive_client_secret', sanitize_text_field($_POST['client_secret']));
    }

    foxblock_load_backup_classes();
    $handler = new Foxblock_Backup_Google_OAuth_Handler();
    $url = $handler->get_auth_url();
    if ($url) {
        wp_send_json_success(['url' => $url]);
    } else {
        wp_send_json_error(['message' => 'Lỗi tạo URL ủy quyền. Vui lòng kiểm tra lại Client ID và Secret (foxblock_gdrive_client_id).']);
    }
}

add_action('wp_ajax_foxblock_gdrive_oauth_callback', 'foxblock_gdrive_oauth_callback_callback');
function foxblock_gdrive_oauth_callback_callback()
{
    if (!current_user_can('manage_options')) wp_die('Unauthorized', 403);
    
    $code = isset($_GET['code']) ? sanitize_text_field($_GET['code']) : '';
    $redirect = admin_url('admin.php?page=foxblock-settings&tab=backup');
    
    if (empty($code)) {
        wp_redirect($redirect . '&gdrive_error=missing_code');
        exit;
    }

    foxblock_load_backup_classes();
    $handler = new Foxblock_Backup_Google_OAuth_Handler();
    $result = $handler->handle_callback($code);
    
    if (is_wp_error($result)) {
        wp_redirect($redirect . '&gdrive_error=' . urlencode($result->get_error_message()));
    } else {
        wp_redirect($redirect . '&gdrive_success=1');
    }
    exit;
}

add_action('wp_ajax_foxblock_gdrive_disconnect', 'foxblock_gdrive_disconnect_callback');
function foxblock_gdrive_disconnect_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();
    $handler = new Foxblock_Backup_Google_OAuth_Handler();
    $handler->disconnect();
    
    // Fallback to local storage if disconnecting
    update_option('foxblock_backup_storage', 'local');
    
    wp_send_json_success(['message' => 'Đã ngắt kết nối Google Drive.']);
}

add_action('wp_ajax_foxblock_gdrive_test_upload', 'foxblock_gdrive_test_upload_callback');
function foxblock_gdrive_test_upload_callback()
{
    check_ajax_referer('foxblock_nonce', 'nonce');
    if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    foxblock_load_backup_classes();
    
    $service = new Foxblock_Backup_Google_Drive_Service();
    if (!$service->is_ready()) {
        wp_send_json_error(['message' => 'Google Drive chưa được liên kết hoặc cấu hình sai.']);
    }
    
    // Create a dummy text file
    $test_file = wp_upload_dir()['basedir'] . '/foxblock_temp/test_upload.txt';
    wp_mkdir_p(dirname($test_file));
    file_put_contents($test_file, "Đây là file test đẩy lên từ Foxblock Backup.\nCreated: " . current_time('mysql'));
    
    $folder = get_option('foxblock_gdrive_folder', 'Foxblock Backups');
    $result = $service->upload_file($test_file, $folder);
    
    @unlink($test_file);
    
    if (is_wp_error($result)) {
        wp_send_json_error(['message' => $result->get_error_message()]);
    } else {
        wp_send_json_success(['message' => 'Tải lên thành công! ID: ' . $result]);
    }
}
