<?php
/**
 * EONSR AEO Agent - AJAX Handlers & Logic Processing
 *
 * Handles all asynchronous requests including article generation,
 * job polling, image generation, and bulk operations.
 *
 * @package EONSR_AEO_Agent
 */

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

/* ==========================================================================
   SECTION 1: ARTICLE GENERATION (SINGLE)
   ========================================================================== */

/**
 * AJAX Handler: Generate Article (Queue System).
 * Initiates the job with the Node.js backend.
 */
add_action('wp_ajax_eonsr_aeo_agent_generate_ajax', function () {
    // 1. Security & Permission Check
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied'], 403);
    }
    check_ajax_referer('eonsr_aeo_agent_generate_ajax', 'nonce');

    // 2. Lock Check: Ensure user doesn't have parallel jobs
    $active_job = eonsr_aeo_agent_user_has_active_job();
    if ($active_job) {
        wp_send_json_error([
            'message' => 'Bạn đang có một bài viết đang được tạo ở tab khác. Vui lòng đợi bài viết đó hoàn thành.',
            'code'    => 'LOCKED_SINGLE_TAB',
            'job_id'  => $active_job['job_id']
        ], 429);
    }

    // 3. Custom Logo Logic
    $use_custom_logo = isset($_POST['use_custom_logo']) && sanitize_text_field(wp_unslash($_POST['use_custom_logo'])) === 'on';
    $features = get_option('eonsr_aeo_agent_license_features', []);
    
    // Force disable if feature not allowed in license
    // if (empty($features['allowCustomLogo'])) {
    //     $use_custom_logo = false;
    // }

    // 4. License Validation (With Cache Logic)
    $validation_result = eonsr_aeo_agent_validate_license_key();

    // If cache is stale or invalid, force a re-check
    if (empty($validation_result['success']) && empty($validation_result['valid'])) {
        $validation_result = eonsr_aeo_agent_validate_license_key(true);
    }

    $is_valid = !empty($validation_result['success']) || !empty($validation_result['valid']);

    if (!$is_valid) {
        $msg = $validation_result['message'] ?? 'Lỗi xác thực: Không nhận được phản hồi hợp lệ từ Server.';
        wp_send_json_error(['message' => $msg], 403);
    }

    // 5. Prepare Payload
    $params = [
        'model_name'   => sanitize_text_field(wp_unslash($_POST['model_name'] ?? 'gemini-2.5-pro')),
        'primary_term' => sanitize_text_field(wp_unslash($_POST['primary_term'] ?? '')),
        'audience'     => sanitize_text_field(wp_unslash($_POST['audience'] ?? 'a general audience')),
        'country'      => sanitize_text_field(wp_unslash($_POST['country'] ?? 'global')),
        'language'     => sanitize_text_field(wp_unslash($_POST['language'] ?? 'English')),
        'min_words'    => absint($_POST['min_words'] ?? 1600),
        'output_style' => sanitize_text_field(wp_unslash($_POST['output_style'] ?? 'Mac dinh')),
        'gen_keywords' => !empty($_POST['gen_keywords']),
        'gen_youtube'  => !empty($_POST['gen_youtube']),
    ];

    $license_key  = get_option('eonsr_aeo_agent_license_key');
    $node_api_url = EONSR_AEO_AGENT_API_URL . '/api/v1/article/generate';

    // 6. Call Backend API
    $response = wp_remote_post($node_api_url, [
        'body'    => json_encode([
            'licenseKey' => $license_key, 
            'domain'     => site_url(), 
            'params'     => $params
        ]),
        'headers' => ['Content-Type' => 'application/json'], 
        'timeout' => 30
    ]);

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

    $result = json_decode(wp_remote_retrieve_body($response), true);

    if (empty($result['success'])) {
        wp_send_json_error(['message' => $result['message'] ?? 'Error']);
    }

    // 7. Update Usage Stats
    if (isset($result['currentUsage'])) {
        $details = get_option('eonsr_aeo_agent_license_details', []);
        $details['usage'] = $result['currentUsage'];
        update_option('eonsr_aeo_agent_license_details', $details);
        $result['usage'] = $result['currentUsage'];
    }

    // 8. Handle Job Initiation
    if (!empty($result['jobId'])) {
        // Save Language context
        set_transient('eonsr_aeo_job_lang_' . $result['jobId'], $params['language'], 24 * HOUR_IN_SECONDS);

        // Save Start Time for Timeout logic (10 mins)
        set_transient('eonsr_aeo_job_start_' . $result['jobId'], time(), 20 * MINUTE_IN_SECONDS);

        // Lock the user
        eonsr_aeo_agent_lock_user_job($result['jobId'], $params['primary_term']);
    }

    wp_send_json_success($result);
});

/* ==========================================================================
   SECTION 2: POLLING & JOB STATUS (CORE LOGIC)
   ========================================================================== */

/**
 * AJAX Handler: Poll Job Status.
 * Implements Atomic Locking and Direct DB Checking to prevent race conditions.
 */
add_action('wp_ajax_eonsr_aeo_agent_poll_job_status', function() {
    // 1. Security Check
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied'], 403);
    }
    check_ajax_referer('eonsr_aeo_agent_poll_job_ajax', 'nonce');

    $job_id = sanitize_text_field(wp_unslash($_POST['job_id'] ?? ''));

    // 2. Timeout Check (10 Minutes)
    $start_time = get_transient('eonsr_aeo_job_start_' . $job_id);
    if ($start_time) {
        $elapsed = time() - $start_time;
        if ($elapsed > 600) {
            // Cleanup on timeout
            eonsr_aeo_agent_unlock_user_job($job_id);
            delete_transient('eonsr_aeo_job_start_' . $job_id);
            delete_transient('eonsr_aeo_job_lang_' . $job_id);

            wp_send_json_error([
                'message' => 'Hết thời gian chờ (Timeout 10 phút). Hệ thống đã dừng theo dõi bài viết này.',
                'code'    => 'JOB_TIMEOUT'
            ]);
            return;
        }
    }

    global $wpdb;

    // -------------------------------------------------------------------------
    // STEP 1: DIRECT DB CHECK (Bypass WP Cache)
    // -------------------------------------------------------------------------
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Critical atomic check to prevent race condition in job processing
    $existing_id = $wpdb->get_var($wpdb->prepare(
        "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_eonsr_aeo_job_id' AND meta_value = %s LIMIT 1",
        $job_id
    ));

    if ($existing_id) {
        // Job already processed, unlock and return data
        eonsr_aeo_agent_unlock_user_job($job_id);
        wp_send_json_success(eonsr_aeo_agent_get_post_preview_data($existing_id));
        return;
    }

    // -------------------------------------------------------------------------
    // STEP 2: CALL NODE SERVER
    // -------------------------------------------------------------------------
    $response = wp_remote_get(EONSR_AEO_AGENT_API_URL . '/api/v1/article/job/' . $job_id, ['timeout' => 30]);
    
    if (is_wp_error($response)) {
        wp_send_json_error(['message' => $response->get_error_message()]);
    }

    $result = json_decode(wp_remote_retrieve_body($response), true);
    
    if (empty($result['success'])) {
        wp_send_json_error(['message' => $result['message'] ?? 'Error']);
    }

    $job = $result['job'];

    // Handle Failed
    if ($job['status'] === 'failed') {
        eonsr_aeo_agent_unlock_user_job($job_id); 
    }

    // Handle Completed
    if ($job['status'] === 'completed') {

        // ---------------------------------------------------------------------
        // STEP 3: ATOMIC LOCKING (Critical Section)
        // ---------------------------------------------------------------------
        $lock_key = 'eonsr_aeo_processing_lock_' . $job_id;

        // If locked, another request is processing this job
        if (get_transient($lock_key)) {
            wp_send_json_success(['status' => 'processing', 'message' => 'Waiting for lock release...']);
            return;
        }

        // Set Lock (45s)
        set_transient($lock_key, true, 45);

        try {
            // -----------------------------------------------------------------
            // STEP 4: DOUBLE CHECK DB (Inside Lock)
            // -----------------------------------------------------------------
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Double-check inside lock to ensure no duplicate post creation
            $check_again = $wpdb->get_var($wpdb->prepare(
                "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_eonsr_aeo_job_id' AND meta_value = %s LIMIT 1",
                $job_id
            ));

            if ($check_again) {
                delete_transient($lock_key);
                eonsr_aeo_agent_unlock_user_job($job_id);
                wp_send_json_success(eonsr_aeo_agent_get_post_preview_data($check_again));
                return;
            }

            // -----------------------------------------------------------------
            // STEP 5: PROCESS JOB & CREATE POST
            // -----------------------------------------------------------------
            eonsr_aeo_agent_unlock_user_job($job_id);

            $processed = eonsr_aeo_agent_process_completed_job($job, $job_id);
            $post_id   = $processed['post_id'];

            // Release Lock
            delete_transient($lock_key);

            $response_data = eonsr_aeo_agent_get_post_preview_data($post_id);

            // Update Usage if available
            if (isset($result['currentUsage'])) {
                $details = get_option('eonsr_aeo_agent_license_details', []);
                $details['usage'] = $result['currentUsage'];
                update_option('eonsr_aeo_agent_license_details', $details);
                
                $response_data['usage'] = $result['currentUsage'];
            }

            wp_send_json_success($response_data);

        } catch (Exception $e) {
            // Ensure lock is released on error
            delete_transient($lock_key);
            wp_send_json_error(['message' => $e->getMessage()]);
        }
        return;
    }

    // If pending/processing, return job status
    wp_send_json_success($job);
});

/* ==========================================================================
   SECTION 3: IMAGE GENERATION
   ========================================================================== */

/**
 * AJAX Handler: Generate Images.
 * Handles locking, calling backend, adding logos, and updating WP Media.
 */
add_action('wp_ajax_eonsr_aeo_agent_generate_images_ajax', function () {
    // 1. Security Check
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied'], 403);
    }
    check_ajax_referer('eonsr_aeo_agent_generate_images_ajax', 'nonce');

    // 2. Lock Check
    $active_job = eonsr_aeo_agent_user_has_active_job();
    if ($active_job) {
        wp_send_json_error([
            'message' => 'Bạn đang có một tác vụ khác đang chạy. Vui lòng đợi.',
            'code'    => 'LOCKED_SINGLE_TAB',
            'job_id'  => $active_job['job_id']
        ], 429);
    }

    // 3. Input Validation
    $post_id     = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
    $target_type = isset($_POST['target_type']) ? sanitize_text_field(wp_unslash($_POST['target_type'])) : 'all';

    $featured_prompt   = sanitize_textarea_field(wp_unslash($_POST['featured_prompt'] ?? ''));
    $in_content_prompt = sanitize_textarea_field(wp_unslash($_POST['in_content_prompt'] ?? ''));

    if (!$post_id || get_post_status($post_id) === false) {
        wp_send_json_error(['message' => 'ID bài viết không hợp lệ.']);
    }

    // Prompt Validation
    if ($target_type === 'all' && (empty($featured_prompt) || empty($in_content_prompt))) {
        wp_send_json_error(['message' => 'Thiếu câu lệnh để tạo ảnh.']);
    } elseif ($target_type === 'featured' && empty($featured_prompt)) {
        wp_send_json_error(['message' => 'Thiếu câu lệnh tạo Thumbnail.']);
    } elseif ($target_type === 'in_content' && empty($in_content_prompt)) {
        wp_send_json_error(['message' => 'Thiếu câu lệnh tạo ảnh bài viết.']);
    }

    $post_to_update = get_post($post_id);
    if (!$post_to_update) {
        wp_send_json_error(['message' => 'Không tìm thấy bài viết để cập nhật.']);
    }

    // 4. Custom Logo Option Logic
    if (isset($_POST['use_custom_logo'])) {
        $use_logo   = (sanitize_text_field(wp_unslash($_POST['use_custom_logo'])) == '1');
        $features   = get_option('eonsr_aeo_agent_license_features', []);
        $allow_logo = isset($features['allowCustomLogo']) ? $features['allowCustomLogo'] : false;
        
        // if (!$allow_logo) $use_logo = false;
        if (!$allow_logo) $use_logo = true;
        update_option('eonsr_aeo_agent_use_custom_logo', $use_logo);
    }

    // 5. Lock User
    $temp_job_id = 'img_gen_' . $post_id;
    eonsr_aeo_agent_lock_user_job($temp_job_id, 'Đang tạo hình ảnh (' . $target_type . ')...');

    // 6. Call Backend
    $license_key = get_option('eonsr_aeo_agent_license_key');
    $domain      = site_url();
    $node_api_url = EONSR_AEO_AGENT_API_URL . '/api/v1/article/generate-images';

    $response = wp_remote_post($node_api_url, [
        'body'    => json_encode([
            'licenseKey' => $license_key,
            'domain'     => $domain,
            'targetType' => $target_type,
            'prompts'    => [
                'featured'   => $featured_prompt,
                'in_content' => $in_content_prompt
            ]
        ]),
        'headers' => ['Content-Type' => 'application/json'],
        'timeout' => 120
    ]);

    if (is_wp_error($response)) {
        eonsr_aeo_agent_unlock_user_job($temp_job_id);
        wp_send_json_error(['message' => 'Lỗi kết nối backend: ' . $response->get_error_message()]);
    }

    $response_code = wp_remote_retrieve_response_code($response);
    $result        = json_decode(wp_remote_retrieve_body($response), true);

    if ($response_code !== 200 || empty($result['success'])) {
        eonsr_aeo_agent_unlock_user_job($temp_job_id);
        wp_send_json_error(['message' => $result['message'] ?? 'Lỗi không xác định từ backend']);
    }

    // 7. Update Usage
    $latest_usage = $result['usage']['current_db_usage'] ?? ($result['currentUsage'] ?? null);
    if ($latest_usage) {
        $details = get_option('eonsr_aeo_agent_license_details', []);
        $details['usage'] = $latest_usage;
        update_option('eonsr_aeo_agent_license_details', $details);
    }

    // 8. Process Images
    $data              = $result['data'];
    $featured_base64   = $data['featured_image'] ?? null;
    $in_content_base64 = $data['in_content_image'] ?? null;
    $logo_path         = EONSR_AEO_AGENT_PATH . 'assets/img/EONSR-Logo.png';

    // Get Language for Logo placement
    $current_lang = 'Vietnamese';
    $gen_params   = get_post_meta($post_id, '_eonsr_aeo_generation_params', true);
    if (is_array($gen_params) && !empty($gen_params['language'])) {
        $current_lang = $gen_params['language'];
    }

    $response_data = [
        'message' => 'Xử lý thành công!',
        'usage'   => $latest_usage
    ];

    // Process Featured Image
    if (!empty($featured_base64)) {
        $featured_with_logo = eonsr_aeo_agent_add_logo_to_image_base64($featured_base64, $logo_path, $current_lang);
        
        if (!is_wp_error($featured_with_logo)) {
            $featured_image_id = eonsr_aeo_agent_save_base64_image_to_media(
                $featured_with_logo, 
                $post_to_update->post_title . ' - Featured Image'
            );
            
            if ($featured_image_id) {
                set_post_thumbnail($post_id, $featured_image_id);
                $response_data['featuredImage'] = $featured_with_logo;
            }
        }
    }

    // Process In-Content Image
    if (!empty($in_content_base64)) {
        $in_content_with_logo = eonsr_aeo_agent_add_logo_to_image_base64($in_content_base64, $logo_path, $current_lang);
        
        if (!is_wp_error($in_content_with_logo)) {
            $in_content_image_id = eonsr_aeo_agent_save_base64_image_to_media(
                $in_content_with_logo, 
                $post_to_update->post_title . ' - In-content Image'
            );

            if ($in_content_image_id) {
                update_post_meta($post_id, '_eonsr_aeo_in_content_image_id', $in_content_image_id);
                
                // Replace/Insert logic using DOMDocument
                $content_from_db = $post_to_update->post_content;
                $placeholder_id  = 'gemini-in-content-image';

                if (strpos($content_from_db, 'id="' . $placeholder_id . '"') !== false) {
                    $image_html = wp_get_attachment_image(
                        $in_content_image_id, 
                        'large', 
                        false, 
                        ['style' => 'width:100%;height:auto;border-radius:8px;margin-bottom:0.5em; display:block;']
                    );
                    
                    $doc = new DOMDocument();
                    @$doc->loadHTML('<?xml encoding="utf-8" ?>' . $content_from_db, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
                    
                    $figure_placeholder = $doc->getElementById($placeholder_id);
                    
                    if ($figure_placeholder) {
                        // Remove existing images
                        $existing_images = $figure_placeholder->getElementsByTagName('img');
                        for ($i = $existing_images->length - 1; $i >= 0; $i--) {
                            $existing_images->item($i)->parentNode->removeChild($existing_images->item($i));
                        }
                        
                        $figcaption = $figure_placeholder->getElementsByTagName('figcaption')->item(0);
                        
                        // Create temp doc to import new image node
                        $temp_doc = new DOMDocument();
                        @$temp_doc->loadHTML('<?xml encoding="utf-8" ?>' . $image_html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
                        $temp_img = $temp_doc->getElementsByTagName('img')->item(0);
                        
                        if ($temp_img) {
                            $imported_img = $doc->importNode($temp_img, true);
                            if ($figcaption) {
                                $figure_placeholder->insertBefore($imported_img, $figcaption);
                            } else {
                                $figure_placeholder->appendChild($imported_img);
                            }
                        }
                        $content_from_db = $doc->saveHTML();
                    }
                }
                
                wp_update_post(['ID' => $post_id, 'post_content' => $content_from_db]);
                $response_data['inContentImage'] = $in_content_with_logo;
            }
        }
    }

    // 9. Update Local Logs
    global $wpdb;
    $table_name      = $wpdb->prefix . 'eonsr_aeo_agent_cost_logs';
    $generated_count = ($target_type === 'all') ? 2 : 1;
    
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Update custom cost tracking table for image generation count
    $wpdb->query($wpdb->prepare(
        "UPDATE {$wpdb->prefix}eonsr_aeo_agent_cost_logs SET images_generated = images_generated + %d WHERE post_id = %d",
        $generated_count, $post_id
    ));

    // Prepare Response Data
    $updated_post = get_post($post_id);
    $post_tags    = wp_get_post_tags($post_id);
    $updated_tags = [];
    foreach ($post_tags as $tag) {
        $updated_tags[] = $tag->name;
    }

    $response_data['updated_title']        = $updated_post->post_title;
    $response_data['updated_content_body'] = $updated_post->post_content;
    $response_data['updated_tags']         = $updated_tags;

    // Unlock User
    eonsr_aeo_agent_unlock_user_job($temp_job_id);

    wp_send_json_success($response_data);
});

/* ==========================================================================
   SECTION 4: POST & CATEGORY UPDATES
   ========================================================================== */

add_action('wp_ajax_eonsr_aeo_agent_update_content_ajax', function() {
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied.'], 403);
    }
    check_ajax_referer('eonsr_aeo_agent_update_content_ajax', 'nonce');

    $post_id     = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
    $content     = isset($_POST['content']) ? wp_kses_post(wp_unslash($_POST['content'])) : '';
    $active_tab  = isset($_POST['active_tab']) ? sanitize_text_field(wp_unslash($_POST['active_tab'])) : 'content';

    if (!$post_id || !get_post($post_id)) {
        wp_send_json_error(['message' => 'Invalid post ID.'], 403);
    }

    // Load existing content to preserve styles and structure
    $existing_post   = get_post($post_id);
    $trusted_content = $existing_post->post_content;

    $trusted_doc = new DOMDocument();
    libxml_use_internal_errors(true);
    @$trusted_doc->loadHTML('<?xml encoding="utf-8" ?>' . $trusted_content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    libxml_clear_errors();
    
    // Extract Style block
    $style_node = $trusted_doc->getElementsByTagName('style')->item(0);
    $style_html = $style_node ? $trusted_doc->saveHTML($style_node) : '';
    
    // Extract Image Node (to preserve image when updating text)
    $xpath = new DOMXPath($trusted_doc);
    $trusted_figure_node = $xpath->query("//*[@id='gemini-in-content-image']")->item(0);
    $real_image_node = null;
    if ($trusted_figure_node) {
        $real_image_node = $trusted_figure_node->getElementsByTagName('img')->item(0);
    }

    // Process New Content
    $editor_doc = new DOMDocument();
    libxml_use_internal_errors(true);
    @$editor_doc->loadHTML('<?xml encoding="utf-8" ?>' . '<div id="editor-root">' . $content . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    libxml_clear_errors();
    $editor_root = $editor_doc->getElementById('editor-root');

    // Remove temp image in review tab
    if ($active_tab === 'review' && $editor_root && $editor_root->hasChildNodes()) {
        $first_child = $editor_root->firstChild;
        if ($first_child->nodeType === XML_ELEMENT_NODE && strtolower($first_child->tagName) === 'img') {
             $editor_root->removeChild($first_child);
        }
    }
    
    // Extract Title from H1
    $new_title = $existing_post->post_title;
    $h1_nodes  = $editor_doc->getElementsByTagName('h1');
    if ($h1_nodes->length > 0) {
        $first_h1 = $h1_nodes->item(0);
        $candidate = trim($first_h1->textContent);
        if (!empty($candidate)) {
            $new_title = $candidate;
        }
        $first_h1->parentNode->removeChild($first_h1);
    }

    // Re-inject Real Image
    if ($real_image_node) {
        $editor_xpath = new DOMXPath($editor_doc);
        $editor_figure_node = $editor_xpath->query("//*[@id='gemini-in-content-image']")->item(0);

        if ($editor_figure_node) {
            $existing_images = $editor_figure_node->getElementsByTagName('img');
            while ($existing_images->length > 0) {
                $existing_images->item(0)->parentNode->removeChild($existing_images->item(0));
            }

            $imported_image_node = $editor_doc->importNode($real_image_node, true);
            $figcaption = $editor_figure_node->getElementsByTagName('figcaption')->item(0);

            if ($figcaption) {
                $editor_figure_node->insertBefore($imported_image_node, $figcaption);
            } else {
                $editor_figure_node->appendChild($imported_image_node);
            }
        }
    }

    // Save final content
    $final_body_html = '';
    if ($editor_root) {
        foreach($editor_root->childNodes as $child) {
            $final_body_html .= $editor_doc->saveHTML($child);
        }
    }
    
    $final_content = $style_html . '<div class="gemini-article-content">' . $final_body_html . '</div>';
    
    $post_data = [
        'ID'           => $post_id,
        'post_title'   => $new_title,
        'post_content' => $final_content,
    ];
    $result = wp_update_post($post_data, true);

    if (is_wp_error($result)) {
        wp_send_json_error(['message' => 'Lỗi cập nhật: ' . $result->get_error_message()]);
    }

    wp_send_json_success(['message' => 'Đã cập nhật thành công!']);
});

add_action('wp_ajax_eonsr_aeo_agent_update_categories_ajax', function() {
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied.'], 403);
    }
    check_ajax_referer('eonsr_aeo_agent_update_categories_ajax', 'nonce');

    $post_id    = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
    $categories = isset($_POST['categories']) && is_array($_POST['categories']) 
        ? array_map('absint', wp_unslash($_POST['categories'])) 
        : [];

    if (!$post_id || !get_post($post_id)) {
        wp_send_json_error(['message' => 'Invalid post ID.'], 403);
    }

    $sanitized = array_map('absint', $categories);
    $result    = wp_set_post_categories($post_id, $sanitized);

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

    wp_send_json_success(['message' => 'Categories updated successfully.']);
});

/* ==========================================================================
   SECTION 5: BULK OPERATIONS
   ========================================================================== */

/**
 * AJAX Handler: Bulk Upload CSV.
 */
/**
 * AJAX Handler: Bulk Upload CSV (REFACTORED - WP REMOTE POST).
 * Replaces direct cURL usage with WordPress HTTP API to comply with guidelines.
 */
add_action('wp_ajax_eonsr_aeo_agent_bulk_upload_csv', function() {
    // 1. Security Check
    if (!current_user_can('publish_posts')) {
        wp_send_json_error(['message' => 'Permission denied.'], 403);
    }
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce');

    // 2. Validate File
    if (!isset($_FILES['bulk_csv_file']) || 
        !isset($_FILES['bulk_csv_file']['error']) || 
        $_FILES['bulk_csv_file']['error'] !== UPLOAD_ERR_OK || 
        empty($_FILES['bulk_csv_file']['name'])) {
        wp_send_json_error(['message' => 'Missing or invalid CSV file.']);
    }

    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    $file = $_FILES['bulk_csv_file'];
    $file_info = wp_check_filetype($file['name']);
    
    // Strict file type check
    if ($file_info['ext'] !== 'xlsx') {
        wp_send_json_error(['message' => '❌ Bảo mật: Chỉ chấp nhận file định dạng XLSX.']);
    }

    // 3. Custom Logo Logic
    $use_logo = false;
    if (isset($_POST['use_custom_logo'])) {
        $raw_val = sanitize_text_field(wp_unslash($_POST['use_custom_logo']));
        $use_logo = ($raw_val === 'true' || $raw_val === 'on' || $raw_val == '1');
        
        // $features = get_option('eonsr_aeo_agent_license_features', []);
        // if (empty($features['allowCustomLogo'])) {
        //     $use_logo = false; 
        // }
        update_option('eonsr_aeo_agent_use_custom_logo', $use_logo);
    }

    // 4. Prepare Params
    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    $target_langs = isset($_POST['eonsr_aeo_target_langs']) && is_array($_POST['eonsr_aeo_target_langs']) 
    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        ? wp_unslash($_POST['eonsr_aeo_target_langs']) 
        : [];
    
    // Sanitize input arrays
    $clean_target_langs = array_map('sanitize_text_field', $target_langs);

    $shared_params = [
        'model_name'       => sanitize_text_field(wp_unslash($_POST['model_name'] ?? 'gemini-2.5-pro')),
        'primary_term'     => sanitize_text_field(wp_unslash($_POST['primary_term'] ?? '')),
        'audience'         => sanitize_text_field(wp_unslash($_POST['audience'] ?? 'a general audience')),
        'country'          => sanitize_text_field(wp_unslash($_POST['country'] ?? 'global')),
        'language'         => sanitize_text_field(wp_unslash($_POST['language'] ?? 'English')),
        'min_words'        => absint($_POST['min_words'] ?? 1600),
        'output_style'     => sanitize_text_field(wp_unslash($_POST['output_style'] ?? 'Sleek Blog')),
        'gen_keywords'     => !empty($_POST['gen_keywords']),
        'gen_youtube'      => !empty($_POST['gen_youtube']),
        'auto_translate'   => !empty($_POST['auto_translate']),
        'target_languages' => $clean_target_langs,
        'use_custom_logo'  => get_option('eonsr_aeo_agent_use_custom_logo', false) 
    ];
    // 5. Send Request using WP_REMOTE_POST (Multipart)
    $node_api_url = EONSR_AEO_AGENT_API_URL . '/api/v1/article/bulk/upload-scheduled';
    $license_key  = get_option('eonsr_aeo_agent_license_key');
    $domain       = site_url();

    // Construct Multipart Body manually because wp_remote_post file upload support varies
    $boundary = wp_generate_password(24, false);
    $payload  = '';

    // Helper to add fields
    $add_field = function($name, $value) use (&$payload, $boundary) {
        $payload .= "--" . $boundary . "\r\n";
        $payload .= "Content-Disposition: form-data; name=\"" . $name . "\"\r\n\r\n";
        $payload .= $value . "\r\n";
    };

    $add_field('licenseKey', $license_key);
    $add_field('domain', $domain);
    $add_field('sharedParams', json_encode($shared_params));

    // Add File Data
    if (file_exists($file['tmp_name'])) {
        $file_content = file_get_contents($file['tmp_name']);
        $payload .= "--" . $boundary . "\r\n";
        $payload .= "Content-Disposition: form-data; name=\"file\"; filename=\"" . $file['name'] . "\"\r\n";
        $payload .= "Content-Type: " . ($file['type'] ?: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') . "\r\n\r\n";
        $payload .= $file_content . "\r\n";
    }

    $payload .= "--" . $boundary . "--\r\n";

    $response = wp_remote_post($node_api_url, [
        'headers' => [
            'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
            'Content-Length' => strlen($payload)
        ],
        'body'    => $payload,
        'timeout' => 60
    ]);

    if (is_wp_error($response)) {
        wp_send_json_error(['message' => 'Node Error: ' . $response->get_error_message()]);
    }

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

    if (empty($result['success'])) {
        wp_send_json_error(['message' => $result['message'] ?? 'Upload failed']);
    }

    // 6. Log to DB
    global $wpdb;
    $table_bulk = $wpdb->prefix . 'eonsr_aeo_agent_bulk_logs';
    $bulk_id    = $result['bulkUploadId'] ?? ('bulk_' . time());
    
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Insert into custom bulk tracking table, no WP equivalent
    $wpdb->insert($table_bulk, [
        'bulk_id'    => sanitize_text_field($bulk_id),
        'filename'   => sanitize_file_name($file['name']),
        'total_jobs' => intval($result['totalJobs'] ?? 0),
        'status'     => 'processing',
        'created_at' => current_time('mysql')
    ]);

    wp_send_json_success([
        'message'      => $result['message'] ?? 'Upload thành công',
        'bulkUploadId' => $bulk_id,
        'filename'     => $file['name'],
        'type'         => 'scheduled', 
        'schedule'     => $result['schedule'] ?? [],
        'totalJobs'    => $result['totalJobs']
    ]);
});

add_action('wp_ajax_eonsr_aeo_agent_check_scheduled_bulk_status', function() {
    if (!current_user_can('publish_posts')) wp_send_json_error([], 403);
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce'); 

    $bulk_id = isset($_POST['bulkUploadId']) ? sanitize_text_field(wp_unslash($_POST['bulkUploadId'])) : '';
    if (empty($bulk_id)) wp_send_json_error(['message' => 'Missing ID']);

    $response = wp_remote_post(EONSR_AEO_AGENT_API_URL . '/api/v1/article/bulk/status-scheduled', [
        'body'    => json_encode(['bulkUploadId' => $bulk_id]),
        'headers' => ['Content-Type' => 'application/json'], 
        'timeout' => 30
    ]);

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

    $response_code = wp_remote_retrieve_response_code($response);
    $result        = json_decode(wp_remote_retrieve_body($response), true);

    // Auto-clean Logs if Not Found
    if ($response_code === 404 || (isset($result['success']) && !$result['success'])) {
        global $wpdb;
        $table = $wpdb->prefix . 'eonsr_aeo_agent_bulk_logs';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Delete from custom bulk tracking table
        $wpdb->delete($table, ['bulk_id' => $bulk_id]);

        wp_send_json_error([
            'message' => 'Dữ liệu không tồn tại trên server (đã bị xóa). Đang cập nhật lại lịch sử...',
            'code'    => 'NOT_FOUND_ON_SERVER' 
        ]);
        return;
    }

    // Process Data
    if (isset($result['data']) && isset($result['data']['stats'])) {
        $final_data = $result['data'];
    } else if (isset($result['stats'])) {
        $final_data = $result;
    } else {
        unset($result['success']);
        $final_data = $result;
    }

    // Intelligent Status Update
    $stats      = $final_data['stats'] ?? [];
    $scheduled  = $stats['scheduled'] ?? 0;
    $processing = $stats['processing'] ?? 0;
    $total      = $stats['total'] ?? 0;

    // Only update if no jobs are pending
    if (isset($stats['total']) && $scheduled == 0 && $processing == 0) {
        global $wpdb;
        $table = $wpdb->prefix . 'eonsr_aeo_agent_bulk_logs';
        
        $cancelled = $stats['cancelled'] ?? 0;
        $failed    = $stats['failed'] ?? 0;
        $completed = $stats['completed'] ?? 0;

        $new_status = 'completed';

        if ($cancelled > 0 && $cancelled == $total) {
            $new_status = 'cancelled';
        } else if ($failed > 0 && $failed == $total) {
            $new_status = 'failed';
        } else if ($completed == 0 && $total > 0) {
             $new_status = 'cancelled';
        }
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Update custom bulk log status
        $wpdb->update($table, ['status' => $new_status], ['bulk_id' => $bulk_id]);
    }

    wp_send_json_success($final_data);
});

/* ==========================================================================
   SECTION 6: CANCELLATION HANDLERS
   ========================================================================== */

add_action('wp_ajax_eonsr_aeo_agent_cancel_job', function() {
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce');

    $job_id = isset($_POST['jobId']) ? sanitize_text_field(wp_unslash($_POST['jobId'])) : '';
    if (empty($job_id)) wp_send_json_error(['message' => 'Missing Job ID']);

    $response = wp_remote_post(EONSR_AEO_AGENT_API_URL . '/api/v1/article/bulk/cancel-job', [
        'body'    => json_encode(['jobId' => $job_id]),
        'headers' => ['Content-Type' => 'application/json'],
        'timeout' => 15
    ]);

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

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

    if (isset($result['success']) && $result['success']) {
        wp_send_json_success(['message' => 'Đã hủy thành công']);
    } else {
        $error_msg = isset($result['message']) ? $result['message'] : 'Lỗi không xác định khi hủy job';
        wp_send_json_error(['message' => $error_msg]);
    }
});

add_action('wp_ajax_eonsr_aeo_agent_cancel_bulk', function() {
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce');
    $bulk_id = isset($_POST['bulkUploadId']) ? sanitize_text_field(wp_unslash($_POST['bulkUploadId'])) : '';
    if (empty($bulk_id)) {
        wp_send_json_error(['message' => 'Missing ID']);
    }
    
    $response = wp_remote_post(EONSR_AEO_AGENT_API_URL . '/api/v1/article/bulk/cancel-upload', [
        'body'    => json_encode(['bulkUploadId' => $bulk_id]),
        'headers' => ['Content-Type' => 'application/json']
    ]);
    
    global $wpdb;
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Update bulk status to cancelled
    $wpdb->update($wpdb->prefix . 'eonsr_aeo_agent_bulk_logs', ['status' => 'cancelled'], ['bulk_id' => $bulk_id]);

    if (!is_wp_error($response)) wp_send_json_success(['message' => 'Bulk Cancelled']);
    else wp_send_json_error(['message' => 'Failed']);
});

/* ==========================================================================
   SECTION 7: HELPER FUNCTIONS (POST PREVIEW & PROCESSING)
   ========================================================================== */

/**
 * Retrieve Post Data for Preview (Sanitized).
 */
function eonsr_aeo_agent_get_post_preview_data($post_id) {
    $post = get_post($post_id);
    
    if (!$post) {
        return ['status' => 'failed', 'message' => 'Post not found'];
    }
    
    if (empty($post->post_content)) {
        return ['status' => 'failed', 'message' => 'Post has no content'];
    }

    $content = $post->post_content;
    
    // Extract CSS
    $style_block_for_preview = '';
    if (preg_match('/<style[^>]*>(.*?)<\/style>/is', $content, $style_matches)) {
        $css_inside = $style_matches[1];
        $scoped_css = eonsr_aeo_agent_scope_css($css_inside, '#aeo-agent-preview-body');
        $style_block_for_preview = "<style type='text/css'>" . $scoped_css . "</style>";
        $content = str_replace($style_matches[0], '', $content);
    }

    // Remove H1
    if (preg_match('/(<h1[^>]*>.*?<\/h1>)/is', $content, $matches)) {
        $content = str_replace($matches[0], '', $content);
    }

    // Clean Figure/Img placeholders
    if (preg_match('/<figure[^>]*>.*?<\/figure>/is', $content, $figure_matches)) {
        $original_figure = $figure_matches[0];
        $cleaned_figure = preg_replace('/<img[^>]*>/is', '', $original_figure);
        $content = str_replace($original_figure, $cleaned_figure, $content);
    }

    // Metadata
    $meta_desc = get_post_meta($post_id, '_yoast_wpseo_metadesc', true) ?: get_post_meta($post_id, 'rank_math_description', true);
    $focus_kw  = get_post_meta($post_id, '_yoast_wpseo_focuskw', true) ?: get_post_meta($post_id, 'rank_math_focus_keyword', true);
    $tags      = wp_get_post_tags($post_id, ['fields' => 'names']);

    // Prompts
    $featured_prompt   = get_post_meta($post_id, '_eonsr_aeo_featured_image_prompt', true);
    $in_content_prompt = get_post_meta($post_id, '_eonsr_aeo_in_content_image_prompt', true);

    $title = $post->post_title;
    if (empty($title)) {
        $title = 'Untitled';
    }

    $summary_paragraph = get_post_meta($post_id, '_eonsr_aeo_summary_paragraph', true);
    $summary_bullets   = json_decode(get_post_meta($post_id, '_eonsr_aeo_summary_bullets', true), true) ?: [];

    return [
        'status'                  => 'completed',
        'post_id'                 => $post_id,
        'summaries'               => [
            'paragraph' => $summary_paragraph,
            'bullets'   => $summary_bullets
        ],
        'preview_url'             => get_preview_post_link($post_id),
        'title'                   => $title,
        'content_style'           => $style_block_for_preview,
        'content_body'            => wp_kses_post($content),
        'slug'                    => $post->post_name,
        'meta_description'        => $meta_desc,
        'tags'                    => $tags,
        'focus_keyphrase'         => $focus_kw,
        'featured_image_prompt'   => $featured_prompt,
        'in_content_image_prompt' => $in_content_prompt,
    ];
}

/**
 * Process a Completed Job: Create/Update Post, Handle Meta, Polylang.
 */
function eonsr_aeo_agent_process_completed_job($job, $job_id) {
    $data = $job['result'];
    
    $raw_content = $data['content'];
    $metadata    = $data['metadata'];
    $summaries   = $data['summaries'] ?? null;
    
    // Extract Title
    $preview_title = $metadata['title'] ?? '';
    $h1_tag_html   = '';
    
    if (preg_match('/(<h1[^>]*>.*?<\/h1>)/is', $raw_content, $matches)) {
        $h1_tag_html = $matches[0];
        if (empty($preview_title)) {
            $preview_title = trim(wp_strip_all_tags($h1_tag_html));
        }
    }

    // Process CSS
    $style_block_for_preview = '';
    $style_block_for_post    = '';
    $html_body               = $raw_content;
    
    if (preg_match('/<style[^>]*>(.*?)<\/style>/is', $raw_content, $style_matches)) {
        $css_inside = $style_matches[1];
        $scoped_css_for_preview  = eonsr_aeo_agent_scope_css($css_inside, '#aeo-agent-preview-body');
        $scoped_css_for_post     = eonsr_aeo_agent_scope_css($css_inside, '.gemini-article-content');
        $style_block_for_preview = "<style type='text/css'>" . $scoped_css_for_preview . "</style>";
        $style_block_for_post    = "<style type='text/css'>" . $scoped_css_for_post . "</style>";
        $html_body               = str_replace($style_matches[0], '', $raw_content);
    }

    $content_body_without_h1 = trim(preg_replace('//s', '', $html_body));
    if (!empty($h1_tag_html)) {
        $content_body_without_h1 = str_replace($h1_tag_html, '', $content_body_without_h1);
    }

    // Clean Preview Content
    $content_for_preview = $content_body_without_h1;
    if (preg_match('/<figure[^>]*>.*?<\/figure>/is', $content_for_preview, $figure_matches)) {
        $original_figure = $figure_matches[0];
        $cleaned_figure  = preg_replace('/<img[^>]*>/is', '', $original_figure);
        $content_for_preview = str_replace($original_figure, $cleaned_figure, $content_for_preview);
    }

    if (empty($preview_title)) {
        throw new Exception('Title is empty');
    }

    // Create WordPress Post
    $decoded_content = html_entity_decode($content_body_without_h1, ENT_QUOTES, 'UTF-8');
    $content_for_post = $style_block_for_post . '<div class="gemini-article-content">' . $decoded_content . '</div>';

    $post_id = wp_insert_post([
        'post_title'   => $preview_title,
        'post_content' => $content_for_post,
        'post_status'  => 'draft',
        'post_author'  => get_current_user_id(),
    ], true);

    if (is_wp_error($post_id)) {
        throw new Exception('WordPress error: ' . esc_html($post_id->get_error_message()));
    }

    // =========================================================================
    // POLYLANG FIX
    // =========================================================================
    
    // 1. PRIORITY 1: Retrieve language from Transient
    $source_language = get_transient('eonsr_aeo_job_lang_' . $job_id);

    // 2. PRIORITY 2: Retrieve from Job Params
    if (empty($source_language)) {
        $job_params = $job['params'] ?? [];
        $source_language = isset($job_params['language']) ? trim($job_params['language']) : '';
    }

    if (empty($source_language)) {
        $source_language = 'Vietnamese';
    }

    // 3. Map to Slug
    $target_slug = 'vi';
    $lang_map = [
        'Vietnamese' => 'vi', 'Tiếng Việt' => 'vi',
        'English'    => 'en', 'Tiếng Anh'  => 'en',
        'Russian'    => 'ru', 'Tiếng Nga'  => 'ru'
    ];

    if (isset($lang_map[$source_language])) {
        $target_slug = $lang_map[$source_language];
    } else {
        if (function_exists('pll_languages_list')) {
            $all_languages = pll_languages_list(['fields' => '']);
            foreach ($all_languages as $lang) {
                if (strcasecmp($lang->slug, $source_language) === 0 || strcasecmp($lang->name, $source_language) === 0) {
                    $target_slug = $lang->slug;
                    break;
                }
            }
        }
    }

    // 4. Set Language Term
    if (taxonomy_exists('language')) {
        delete_transient('eonsr_aeo_job_lang_' . $job_id);
        
        if (function_exists('pll_set_post_language')) {
            pll_set_post_language($post_id, $target_slug);
        }
        
        $set_result = wp_set_object_terms($post_id, $target_slug, 'language');
        if (is_wp_error($set_result)) {
        }
    }

    // =========================================================================
    // META & TAGS UPDATE
    // =========================================================================

    update_post_meta($post_id, '_eonsr_aeo_job_id', $job_id);
    if (!empty($job['params'])) {
        update_post_meta($post_id, '_eonsr_aeo_generation_params', $job['params']);
    }

    if (!empty($data['youtube_timestamps'])) {
        update_post_meta($post_id, '_eonsr_aeo_youtube_timestamps', $data['youtube_timestamps']);
    }

    if ($summaries && !empty($summaries['paragraph'])) {
        update_post_meta($post_id, '_eonsr_aeo_summary_paragraph', $summaries['paragraph']);
        $bullets_json = json_encode($summaries['bullets'] ?? [], JSON_UNESCAPED_UNICODE);
        update_post_meta($post_id, '_eonsr_aeo_summary_bullets', wp_slash($bullets_json));
        update_post_meta($post_id, '_eonsr_aeo_has_summaries', 'yes');
    } else {
        update_post_meta($post_id, '_eonsr_aeo_has_summaries', 'no');
    }

    if (!empty($metadata['meta_description'])) {
        update_post_meta($post_id, '_yoast_wpseo_metadesc', $metadata['meta_description']);
        update_post_meta($post_id, 'rank_math_description', $metadata['meta_description']);
    }
    
    if (!empty($metadata['focus_keyphrase'])) {
        update_post_meta($post_id, '_yoast_wpseo_focuskw', $metadata['focus_keyphrase']);
        update_post_meta($post_id, 'rank_math_focus_keyword', $metadata['focus_keyphrase']);
    }
    
    $final_tags = $metadata['tags'] ?? [];
    if (!empty($final_tags)) {
        wp_set_post_tags($post_id, $final_tags, false);
    }

    $image_prompts = $data['image_prompts'] ?? [];
    if (!empty($image_prompts['featured'])) {
        update_post_meta($post_id, '_eonsr_aeo_featured_image_prompt', $image_prompts['featured']);
    }
    if (!empty($image_prompts['in_content'])) {
        update_post_meta($post_id, '_eonsr_aeo_in_content_image_prompt', $image_prompts['in_content']);
    }

    // Logging Usage to DB
    global $wpdb;
    $table_name = $wpdb->prefix . 'eonsr_aeo_agent_cost_logs';
    $usage = $data['usage'] ?? [];
    
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Insert usage tracking into custom cost log table
    $wpdb->insert($table_name, [
        'post_id'               => $post_id,
        'generation_date'       => current_time('mysql'),
        'primary_term'          => $metadata['focus_keyphrase'] ?? '',
        'model_name'            => 'gemini-2.5-pro',
        'search_queries'        => isset($usage['search_calls']) ? absint($usage['search_calls']) : 0,
        'youtube_queries'       => isset($usage['youtube_calls']) ? absint($usage['youtube_calls']) : 0,
        'images_generated'      => 0,
        'gemini_usage_details'  => wp_json_encode($usage['gemini'] ?? []),
    ]);

    // Auto Translate Check
    $should_auto_translate = !empty($job['params']['auto_translate']);
    $has_node_translations = isset($job['result']['translatedPostIds']) && !empty($job['result']['translatedPostIds']);

    if ($should_auto_translate && !$has_node_translations && function_exists('eonsr_aeo_agent_handle_polylang_translation')) {
        eonsr_aeo_agent_handle_polylang_translation($post_id);
    } elseif ($has_node_translations) { 
    }
        
    return [
        'status'                  => 'completed',
        'post_id'                 => $post_id,
        'preview_url'             => get_preview_post_link($post_id),
        'title'                   => $preview_title,
        'content_style'           => $style_block_for_preview,
        'content_body'            => wp_kses_post($content_for_preview),
        'slug'                    => $metadata['slug'] ?? '',
        'meta_description'        => $metadata['meta_description'] ?? '',
        'tags'                    => array_values($final_tags),
        'focus_keyphrase'         => $metadata['focus_keyphrase'] ?? '',
        'featured_image_prompt'   => $image_prompts['featured'] ?? '',
        'in_content_image_prompt' => $image_prompts['in_content'] ?? '',
        'summaries'               => $summaries,
    ];
}

/**
 * Helper: Auto Generate and Attach Images (Used in Bulk)
 */
function eonsr_aeo_agent_generate_and_attach_images($post_id, $featured_prompt, $in_content_prompt) {
    try {
        $post = get_post($post_id);
        if (!$post) throw new Exception('Post not found');
        
        $license_key = get_option('eonsr_aeo_agent_license_key');
        $domain      = site_url();
        
        if (strpos($domain, 'localhost') !== false) {
            $domain = str_replace('localhost', '127.0.0.1', $domain);
        }

        $node_api_url = EONSR_AEO_AGENT_API_URL . '/api/v1/article/generate-images';
        
        $response = wp_remote_post($node_api_url, [
            'body'    => json_encode([
                'licenseKey' => $license_key,
                'domain'     => $domain,
                'targetType' => 'all',
                'prompts'    => [
                    'featured'   => $featured_prompt,
                    'in_content' => $in_content_prompt
                ]
            ]),
            'headers' => ['Content-Type' => 'application/json'],
            'timeout' => 120
        ]);
        
        if (is_wp_error($response)) throw new Exception('Backend connection error: ' . $response->get_error_message());
        
        $response_code = wp_remote_retrieve_response_code($response);
        $result        = json_decode(wp_remote_retrieve_body($response), true);
        
        if ($response_code !== 200 || empty($result['success'])) {
            $msg = $result['message'] ?? 'Image generation failed';
            throw new Exception("Server Error ($response_code): $msg");
        }
        
        $data              = $result['data'];
        $featured_base64   = $data['featured_image'];
        $in_content_base64 = $data['in_content_image'];

        // Get Lang for Logo
        $current_lang = 'Vietnamese'; 
        $gen_params   = get_post_meta($post_id, '_eonsr_aeo_generation_params', true);
        if (is_array($gen_params) && !empty($gen_params['language'])) {
            $current_lang = $gen_params['language'];
        }
        
        $logo_path = EONSR_AEO_AGENT_PATH . 'assets/img/EONSR-Logo.png';
        
        $featured_with_logo = eonsr_aeo_agent_add_logo_to_image_base64($featured_base64, $logo_path, $current_lang);
        if (is_wp_error($featured_with_logo)) $featured_with_logo = $featured_base64;
        
        $in_content_with_logo = eonsr_aeo_agent_add_logo_to_image_base64($in_content_base64, $logo_path, $current_lang);
        if (is_wp_error($in_content_with_logo)) $in_content_with_logo = $in_content_base64;
        
        // Save Featured
        $featured_image_id = eonsr_aeo_agent_save_base64_image_to_media(
            $featured_with_logo, 
            $post->post_title . ' - Featured Image'
        );
        if ($featured_image_id) set_post_thumbnail($post_id, $featured_image_id);
        
        // Save In-Content & Insert
        $in_content_image_id = eonsr_aeo_agent_save_base64_image_to_media(
            $in_content_with_logo, 
            $post->post_title . ' - In-content Image'
        );
        
        if ($in_content_image_id) {
            update_post_meta($post_id, '_eonsr_aeo_in_content_image_id', $in_content_image_id);
            
            $content_from_db = $post->post_content;
            $placeholder_id  = 'gemini-in-content-image';
            
            if (strpos($content_from_db, 'id="' . $placeholder_id . '"') !== false) {
                $image_html = wp_get_attachment_image(
                    $in_content_image_id, 
                    'large', 
                    false, 
                    ['style' => 'width:100%;height:auto;border-radius:8px;margin-bottom:0.5em; display:block;']
                );
                
                $doc = new DOMDocument();
                @$doc->loadHTML(mb_convert_encoding($content_from_db, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
                $figure_placeholder = $doc->getElementById($placeholder_id);
                
                if ($figure_placeholder) {
                    // Remove old images
                    $existing_images = $figure_placeholder->getElementsByTagName('img');
                    while ($existing_images->length > 0) {
                        $existing_images->item(0)->parentNode->removeChild($existing_images->item(0));
                    }
                    
                    // Add new image
                    $temp_doc = new DOMDocument();
                    @$temp_doc->loadHTML(mb_convert_encoding($image_html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
                    $temp_img = $temp_doc->getElementsByTagName('img')->item(0);

                    if ($temp_img) {
                        $imported_img = $doc->importNode($temp_img, true);
                        $figcaption   = $figure_placeholder->getElementsByTagName('figcaption')->item(0);
                        
                        if ($figcaption) {
                            $figure_placeholder->insertBefore($imported_img, $figcaption);
                        } else {
                            $figure_placeholder->appendChild($imported_img);
                        }
                    }
                    $content_from_db = $doc->saveHTML();
                }
            }
            wp_update_post(['ID' => $post_id, 'post_content' => $content_from_db]);
        }
        
        // Log
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Increment image count in custom cost tracking table
        $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->prefix}eonsr_aeo_agent_cost_logs SET images_generated = images_generated + 2 WHERE post_id = %d",
            $post_id
        ));
        
        return true;
        
    } catch (Exception $e) {
        return new WP_Error('image_generation_failed', $e->getMessage());
    }
}

/**
 * Helper: Create Post from Bulk Job
 */
function eonsr_aeo_agent_create_post_from_bulk_job($job_id, $result, $job_params = []) {
    // Ensure helper logic is loaded
    if (!function_exists('eonsr_aeo_agent_handle_post_creation_logic')) {
        $rest_file = plugin_dir_path(__FILE__) . 'includes/rest-api-sync.php';
        if (file_exists($rest_file)) require_once $rest_file;
    }

    try {
        
        $raw_content     = $result['content'];
        $metadata        = $result['metadata'];
        $source_language = isset($job_params['language']) ? trim($job_params['language']) : 'Vietnamese';
        
        $creation_data = [
            'jobId'               => $job_id,
            'content'             => $raw_content,
            'metadata'            => $metadata,
            'imagePrompts'        => $result['image_prompts'] ?? [],
            'summaries'           => $result['summaries'] ?? [],
            'usage'               => $result['usage'] ?? [],
            'params'              => $job_params,
            'scheduledTime'       => gmdate('Y-m-d H:i:s'), 
            'language'            => $source_language,
            'isTranslation'       => false,
            'sourcePostId'        => null,
            'skip_wp_translation' => false
        ];

        if (function_exists('eonsr_aeo_agent_handle_post_creation_logic')) {
            $res = eonsr_aeo_agent_handle_post_creation_logic($creation_data);
            
            if (is_wp_error($res)) {
                throw new Exception($res->get_error_message());
            }
            
            $post_id = $res['postId'];

            // Bulk Auto-Translate Trigger
            $auto_translate = !empty($job_params['auto_translate']);
            $target_langs   = !empty($job_params['target_languages']) ? $job_params['target_languages'] : [];

            if ($auto_translate && !empty($target_langs)) {
                foreach ($target_langs as $t_lang) {
                    if (function_exists('eonsr_aeo_agent_trigger_translation_job_no_polylang')) {
                        eonsr_aeo_agent_trigger_translation_job_no_polylang($post_id, $t_lang);
                    }
                }
            }

            return $post_id;

        } else {
            throw new Exception('Creation logic function not found.');
        }

    } catch (Exception $e) {
        return new WP_Error('post_creation_failed', $e->getMessage());
    }
}

/**
 * Helper: Get Bulk Post Data status.
 */
function eonsr_aeo_agent_get_bulk_post_data($post_id) {
    $post = get_post($post_id);
    if (!$post) return null;
    
    return [
        'postId'  => $post_id,
        'postUrl' => get_permalink($post_id),
        'editUrl' => get_edit_post_link($post_id, 'raw'),
        'title'   => $post->post_title,
        'status'  => $post->post_status
    ];
}

/* ==========================================================================
   SECTION 8: TRANSLATION LOGIC (SINGLE & AUTO)
   ========================================================================== */

/**
 * AJAX Handler: Auto Translate Post (Single).
 */
function eonsr_aeo_agent_auto_translate_post_ajax() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'eonsr_aeo_agent_auto_translate')) {
        wp_send_json_error(['message' => 'Invalid nonce'], 403);
        return;
    }
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied'], 403);
        return;
    }
    
    $source_post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
    if (!$source_post_id) {
        wp_send_json_error(['message' => 'Missing post_id'], 400);
        return;
    }

    // Detect Source Language
    $source_lang_code = '';
    if (function_exists('pll_get_post_language')) {
        $polylang_slug = pll_get_post_language($source_post_id);
        if ($polylang_slug) {
            $source_lang_code = $polylang_slug;
        }
    }

    if (empty($source_lang_code)) {
        $raw_source_lang = isset($_POST['source_language']) ? sanitize_text_field(wp_unslash($_POST['source_language'])) : '';
        $clean_lang = trim(strtolower($raw_source_lang));

        if (in_array($clean_lang, ['english', 'en', 'us', 'uk', 'tiếng anh', 'anh'])) {
            $source_lang_code = 'en';
        } elseif (in_array($clean_lang, ['vietnamese', 'vi', 'vn', 'tiếng việt', 'việt', 'việt nam'])) {
            $source_lang_code = 'vi';
        } else {
            $source_lang_code = 'vi'; 
        }
    }

    // Logic: If EN -> VI, If VI -> EN
    $target_language  = '';      
    $target_country   = '';       
    $target_lang_code = '';     
    
    if ($source_lang_code === 'en') {
        $target_language  = 'Vietnamese';
        $target_lang_code = 'vi';
        $target_country   = 'Vietnam';
    } else {
        $target_language  = 'English';
        $target_lang_code = 'en';
        $target_country   = 'United States';
    }
    
    // Get Params
    $original_params = get_post_meta($source_post_id, '_eonsr_aeo_generation_params', true);
    if (!$original_params || !is_array($original_params)) {
        $original_params = [
            'primary_term' => get_the_title($source_post_id),
            'model_name'   => 'gemini-2.5-pro'
        ];
    }
    
    // Config Target Params
    $target_params = $original_params;
    $target_params['language'] = $target_language; 
    $target_params['country'] = $target_country;
    $target_params['skip_image_generation'] = true; 
    $target_params['auto_translate'] = false;       

    // Reuse YouTube
    $source_post = get_post($source_post_id);
    if ($source_post) {
        if (preg_match('/youtube\.com\/embed\/([a-zA-Z0-9_-]+)/', $source_post->post_content, $yt_matches)) {
            $target_params['youtube_video_id'] = $yt_matches[1];
            $target_params['gen_youtube'] = true; 
            
            $timestamps = get_post_meta($source_post_id, '_eonsr_aeo_youtube_timestamps', true);
            if (empty($timestamps) || !is_array($timestamps)) {
                $timestamps = [];
                // Fallback scrape from HTML
                if (preg_match_all('/data-seconds="(\d+)"[^>]*>\s*⏱\s*([^<]+)\s*<\/a>\s*<span[^>]*>\s*-\s*([^<]+)\s*<\/span>/iu', $source_post->post_content, $matches, PREG_SET_ORDER)) {
                    foreach ($matches as $m) {
                        $timestamps[] = ['seconds' => (int)$m[1], 'time' => trim($m[2]), 'label' => trim($m[3])];
                    }
                }
            }
            if (!empty($timestamps)) {
                $target_params['youtube_timestamps'] = $timestamps;
            }
        }
    }
    
    // Call Node API
    $license_key = get_option('eonsr_aeo_agent_license_key');
    $domain      = get_site_url();
    $api_url = EONSR_AEO_AGENT_API_URL . '/api/v1/article/generate';
    
    $response = wp_remote_post($api_url, [
        'timeout' => 5, 
        'body'    => json_encode([
            'licenseKey' => $license_key,
            'domain'     => $domain,
            'params'     => $target_params
        ]),
        'headers' => ['Content-Type' => 'application/json']
    ]);
    
    if (is_wp_error($response)) {
        wp_send_json_error(['message' => 'Backend Error: ' . $response->get_error_message()], 500);
        return;
    }
    
    $body = wp_remote_retrieve_body($response);
    $data = json_decode($body, true);
    
    if (!$data || !$data['success']) {
        wp_send_json_error(['message' => $data['message'] ?? 'Backend returned error'], 500);
        return;
    }
    
    // Save Job Meta
    $job_id = $data['jobId'];
    $translation_jobs = get_post_meta($source_post_id, '_eonsr_aeo_translation_jobs', true);
    if (!is_array($translation_jobs)) $translation_jobs = [];
    
    $translation_jobs[$target_lang_code] = [
        'jobId'    => $job_id,
        'language' => $target_language,
        'status'   => 'queued',
        'queuedAt' => current_time('mysql')
    ];
    update_post_meta($source_post_id, '_eonsr_aeo_translation_jobs', $translation_jobs);
    
    if (function_exists('pll_set_post_language')) {
        pll_set_post_language($source_post_id, $source_lang_code);
    }
    
    wp_send_json_success([
        'message' => "Đang tạo bản dịch tiếng {$target_language}. Vui lòng đợi vài phút.",
        'jobId'   => $job_id
    ]);
}
add_action('wp_ajax_eonsr_aeo_agent_auto_translate_post', 'eonsr_aeo_agent_auto_translate_post_ajax');

/**
 * Helper: Trigger Translation Job (No Polylang dependency for API call).
 */
function eonsr_aeo_agent_trigger_translation_job_no_polylang($source_post_id, $target_lang_name) {
    $license_key = get_option('eonsr_aeo_agent_license_key');
    $source_post = get_post($source_post_id);
    
    $original_params = get_post_meta($source_post_id, '_eonsr_aeo_generation_params', true);
    if (!$original_params) {
        $original_params = [
            'primary_term' => $source_post->post_title,
            'model_name'   => 'gemini-2.5-pro'
        ];
    }

    $target_params = $original_params;
    $target_params['language'] = $target_lang_name;
    $target_params['primary_term'] = $original_params['primary_term']; 
    $target_params['skip_image_generation'] = true; 
    $target_params['auto_translate'] = false;       
    $target_params['source_post_id'] = $source_post_id; 

    if (isset($original_params['youtube_video_id'])) {
        $target_params['youtube_video_id'] = $original_params['youtube_video_id'];
        $target_params['gen_youtube'] = true;
    }

    $node_api_url = EONSR_AEO_AGENT_API_URL . '/api/v1/article/generate';
    $response = wp_remote_post($node_api_url, [
        'body'    => json_encode([
            'licenseKey' => $license_key, 
            'domain'     => site_url(), 
            'params'     => $target_params
        ]),
        'headers' => ['Content-Type' => 'application/json'], 
        'timeout' => 10,
        'blocking' => true 
    ]);

    if (is_wp_error($response)) return false;

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

    if (isset($data['success']) && $data['success']) {
        $job_id = $data['jobId'];
        $jobs = get_post_meta($source_post_id, '_eonsr_aeo_translation_jobs', true) ?: [];
        $jobs[$target_lang_name] = [
            'jobId'     => $job_id,
            'status'    => 'queued',
            'createdAt' => current_time('mysql')
        ];
        update_post_meta($source_post_id, '_eonsr_aeo_translation_jobs', $jobs);
        return true;
    }

    return false;
}

/* ==========================================================================
   SECTION 9: UTILITIES (SESSION, RECOVERY, LOGS, SETUP)
   ========================================================================== */

add_action('wp_ajax_eonsr_aeo_agent_check_active_session', function() {
    if (!current_user_can('edit_posts')) wp_send_json_error();
    check_ajax_referer('eonsr_aeo_agent_check_active_nonce', 'nonce');

    if (function_exists('eonsr_aeo_agent_user_has_active_job')) {
        $active_job = eonsr_aeo_agent_user_has_active_job();
        
        if ($active_job) {
            $job_id = $active_job['job_id'];
            // Detect Image Job via ID prefix
            if (strpos($job_id, 'img_gen_') === 0) {
                $post_id = str_replace('img_gen_', '', $job_id);
                wp_send_json_success([
                    'has_active_job' => true,
                    'type'           => 'image',
                    'job_id'         => $job_id,
                    'post_id'        => $post_id,
                    'primary_term'   => $active_job['primary_term'] ?? 'Đang tạo hình ảnh...',
                    'started_at'     => $active_job['started_at']
                ]);
            } else {
                wp_send_json_success([
                    'has_active_job' => true,
                    'type'           => 'text',
                    'job_id'         => $active_job['job_id'],
                    'primary_term'   => $active_job['primary_term'] ?? 'Bài viết đang tạo...',
                    'started_at'     => $active_job['started_at']
                ]);
            }
            return;
        }
    }
    wp_send_json_success(['has_active_job' => false]);
});

add_action('wp_ajax_eonsr_aeo_agent_poll_image_recovery', function() {
    if (!current_user_can('edit_posts')) wp_send_json_error();
    check_ajax_referer('eonsr_aeo_agent_check_active_nonce', 'nonce');

    $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
    $job_id  = 'img_gen_' . $post_id;

    $is_locked = false;
    if (function_exists('eonsr_aeo_agent_user_has_active_job')) {
        $active_job = eonsr_aeo_agent_user_has_active_job();
        if ($active_job && $active_job['job_id'] === $job_id) {
            $is_locked = true;
        }
    }

    if ($is_locked) {
        wp_send_json_success(['status' => 'processing']);
    } else {
        // Check if images exist
        $feat_id = get_post_thumbnail_id($post_id);
        $content_img_id = get_post_meta($post_id, '_eonsr_aeo_in_content_image_id', true);

        if ($feat_id || $content_img_id) {
            $feat_url = $feat_id ? wp_get_attachment_image_url($feat_id, 'full') : '';
            $content_img_url = $content_img_id ? wp_get_attachment_image_url($content_img_id, 'full') : '';
            
            $post = get_post($post_id);
            $tags = wp_get_post_tags($post_id, ['fields' => 'names']);

            wp_send_json_success([
                'status' => 'completed',
                'data'   => [
                    'updated_title'     => $post->post_title,
                    'updated_tags'      => $tags,
                    'featuredImageSrc'  => $feat_url,
                    'inContentImageSrc' => $content_img_url,
                    'post_id'           => $post_id
                ]
            ]);
        } else {
            wp_send_json_success([
                'status'  => 'failed', 
                'message' => 'Quá trình tạo ảnh bị gián đoạn hoặc thất bại.'
            ]);
        }
    }
});

// Save Wizard Setup
add_action('wp_ajax_eonsr_aeo_agent_save_initial_setup', function() {
    if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Permission denied'], 403);
    check_ajax_referer('eonsr_aeo_agent_setup_nonce', 'nonce');

    if (get_option('eonsr_aeo_agent_setup_completed')) {
        wp_send_json_error(['message' => 'Cài đặt đã hoàn tất, không thể thay đổi.'], 403);
    }

    $features   = get_option('eonsr_aeo_agent_license_features', []);
    $max_styles = isset($features['styleLimit']) && is_numeric($features['styleLimit']) ? (int)$features['styleLimit'] : 5;
    $max_langs  = isset($features['allowedLanguageCount']) ? (int)$features['allowedLanguageCount'] : 20;
    $max_countries = 5;

    $selected_langs     = isset($_POST['languages']) ? array_map('sanitize_text_field', wp_unslash((array)$_POST['languages'])) : [];   
    $selected_countries = isset($_POST['countries']) ? array_map('sanitize_text_field', wp_unslash((array)$_POST['countries'])) : [];
    $selected_styles    = isset($_POST['styles']) ? array_map('sanitize_text_field', wp_unslash((array)$_POST['styles'])) : [];

    if (empty($selected_langs) || count($selected_langs) > $max_langs) {
        wp_send_json_error(['message' => "Vui lòng chọn từ 1 đến $max_langs ngôn ngữ."]);
    }
    if (empty($selected_countries) || count($selected_countries) > $max_countries) {
        wp_send_json_error(['message' => "Vui lòng chọn từ 1 đến $max_countries quốc gia."]);
    }
    if (empty($selected_styles)) {
        wp_send_json_error(['message' => "Vui lòng chọn ít nhất 1 phong cách viết."]);
    }
    if (count($selected_styles) > $max_styles) {
        wp_send_json_error(['message' => "Gói của bạn chỉ được chọn tối đa $max_styles phong cách viết."]);
    }

    update_option('eonsr_aeo_agent_selected_languages', array_map('sanitize_text_field', $selected_langs));
    update_option('eonsr_aeo_agent_selected_countries', array_map('sanitize_text_field', $selected_countries));
    update_option('eonsr_aeo_agent_selected_styles', array_map('sanitize_text_field', $selected_styles));
    update_option('eonsr_aeo_agent_setup_completed', true);

    wp_send_json_success(['message' => 'Cài đặt thành công!']);
});

// Logo Handlers
add_action('wp_ajax_eonsr_aeo_agent_upload_logo', function() {
    if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Permission denied'], 403);
    check_ajax_referer('eonsr_aeo_agent_setup_nonce', 'nonce');

    $features = get_option('eonsr_aeo_agent_license_features', []);
    // if (empty($features['allowCustomLogo'])) {
    //     wp_send_json_error(['message' => 'Gói hiện tại không hỗ trợ Custom Logo.'], 403);
    // }

    if (isset($_FILES['custom_logo']) && 
    isset($_FILES['custom_logo']['error']) &&
    !empty($_FILES['custom_logo']['name']) && 
    $_FILES['custom_logo']['error'] === UPLOAD_ERR_OK) {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        $file = $_FILES['custom_logo'];
        $file_type = wp_check_filetype($file['name']);
        
        if ($file_type['ext'] !== 'png') {
            wp_send_json_error(['message' => 'Vui lòng chỉ upload file ảnh định dạng PNG.']);
        }

        require_once(ABSPATH . 'wp-admin/includes/image.php');
        require_once(ABSPATH . 'wp-admin/includes/file.php');
        require_once(ABSPATH . 'wp-admin/includes/media.php');

        $attachment_id = media_handle_upload('custom_logo', 0);

        if (is_wp_error($attachment_id)) {
            wp_send_json_error(['message' => 'Lỗi upload: ' . $attachment_id->get_error_message()]);
        }

        update_option('eonsr_aeo_agent_custom_logo_path', get_attached_file($attachment_id));
        update_option('eonsr_aeo_agent_custom_logo_url', wp_get_attachment_url($attachment_id));

        wp_send_json_success([
            'message' => 'Upload logo thành công!',
            'url'     => wp_get_attachment_url($attachment_id)
        ]);
    } else {
        wp_send_json_error(['message' => 'Không tìm thấy file.']);
    }
});

add_action('wp_ajax_eonsr_aeo_agent_remove_logo', function() {
    if (!current_user_can('manage_options')) wp_send_json_error([], 403);
    check_ajax_referer('eonsr_aeo_agent_setup_nonce', 'nonce');
    
    delete_option('eonsr_aeo_agent_custom_logo_path');
    delete_option('eonsr_aeo_agent_custom_logo_url');
    
    wp_send_json_success(['message' => 'Đã xóa logo tùy chỉnh.']);
});

// Log Management
add_action('wp_ajax_eonsr_aeo_agent_delete_bulk_log', function() {
    if (!current_user_can('publish_posts')) wp_send_json_error([], 403);
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce');

    $bulk_id = isset($_POST['bulkUploadId']) ? sanitize_text_field(wp_unslash($_POST['bulkUploadId'])) : '';
    if (empty($bulk_id)) {
        wp_send_json_error(['message' => 'Missing ID']);
    }
    global $wpdb;
    $table = $wpdb->prefix . 'eonsr_aeo_agent_bulk_logs';
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Delete from custom bulk tracking table
    $wpdb->delete($table, ['bulk_id' => $bulk_id]);
    
    wp_send_json_success(['message' => 'Đã xóa log.']);
});

add_action('wp_ajax_eonsr_aeo_agent_clear_all_history', function() {
    if (!current_user_can('publish_posts')) wp_send_json_error([], 403);
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce');

    global $wpdb;
    $table = $wpdb->prefix . 'eonsr_aeo_agent_bulk_logs';
    $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}eonsr_aeo_agent_bulk_logs");
    
    wp_send_json_success(['message' => 'Đã dọn dẹp toàn bộ lịch sử.']);
});

add_action('wp_ajax_eonsr_aeo_agent_get_bulk_history', function() {
    if (!current_user_can('publish_posts')) wp_send_json_error([], 403);
    check_ajax_referer('eonsr_aeo_agent_bulk_upload_csv', 'nonce');

    global $wpdb;
    $table = $wpdb->prefix . 'eonsr_aeo_agent_bulk_logs';

    $paged  = isset($_POST['paged']) ? max(1, intval($_POST['paged'])) : 1;
    $search = isset($_POST['search']) ? sanitize_text_field(wp_unslash($_POST['search'])) : '';
    $limit  = 10;
    $offset = ($paged - 1) * $limit;

    // Tách thành 2 trường hợp riêng biệt
    if (!empty($search)) {
        $like_query = '%' . $wpdb->esc_like($search) . '%';
        
        // Query có tìm kiếm
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Search count in custom bulk log table
        $total_items = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}eonsr_aeo_agent_bulk_logs WHERE filename LIKE %s OR bulk_id LIKE %s",
            $like_query,
            $like_query
        ));
        
    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Get paginated logs from custom bulk table
    $logs = $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}eonsr_aeo_agent_bulk_logs WHERE filename LIKE %s OR bulk_id LIKE %s ORDER BY created_at DESC LIMIT %d OFFSET %d",
        $like_query,
        $like_query,
        $limit,
        $offset
    ));
    } else {
        // Query không tìm kiếm
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Get total count from custom bulk log table for pagination
        $total_items = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM %i",
            $table
        ));
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Get paginated logs without search filter
        $logs = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}eonsr_aeo_agent_bulk_logs ORDER BY created_at DESC LIMIT %d OFFSET %d",
            $limit,
            $offset
        ));
    }

    wp_send_json_success([
        'logs' => $logs,
        'pagination' => [
            'total_items' => (int)$total_items,
            'total_pages' => ceil($total_items / $limit),
            'current_page' => $paged,
            'limit' => $limit
        ]
    ]);
});