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


// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy internal class name is retained for the canonical loader and upgrade compatibility; it is not an extension API.
class Product_Metabox
{
    /**
     * Guard flag to avoid recursive save loops when restoring JSON-LD.
     *
     * @var bool
     */
    private $is_restoring_jsonld = false;

    public function __construct()
    {
        // Register hooks
        $this->register_hook_callbacks();
    }

    public function register_hook_callbacks()
    {
        // Metabox hooks
        add_action('add_meta_boxes', [$this, 'add_product_metabox']);
        add_action('add_meta_boxes', [$this, 'add_page_metabox']);
        add_action('add_meta_boxes', [$this, 'add_post_metabox']);
        // Use lower priority (higher number) to make AYSA metabox appear after standard fields
        add_action('product_cat_add_form_fields', [$this, 'add_cat_metabox_new'], 20);
        add_action('product_cat_edit_form_fields', [$this, 'add_cat_metabox'], 50);
        add_action('edited_product_cat', [$this, 'save_category_seo_data']);
        add_action('create_product_cat', [$this, 'save_category_seo_data']);
        
        // Support for normal WordPress categories
        add_action('category_add_form_fields', [$this, 'add_normal_cat_metabox_new'], 20);
        add_action('category_edit_form_fields', [$this, 'add_normal_cat_metabox'], 50);
        add_action('edited_category', [$this, 'save_normal_category_seo_data']);
        add_action('create_category', [$this, 'save_normal_category_seo_data']);
        
        // ✅ NEW: Store initial permalinks for redirect tracking
        add_action('created_product_cat', [$this, 'store_initial_category_permalink'], 10, 2);
        add_action('wp_insert_post', [$this, 'store_initial_post_permalink'], 10, 3);
        
        // Save hooks
        add_action('save_post_product', [$this, 'save_product_seo_data'], 10, 2);
        add_action('save_post_page', [$this, 'save_page_seo_data'], 10, 2);
        add_action('save_post_post', [$this, 'save_post_seo_data'], 10, 2);
        
        // AJAX handlers
        add_action('wp_ajax_aysa_save_seo_data', [$this, 'handle_save_seo_data']);
        add_action('wp_ajax_aysa_get_seo_recommendations', [$this, 'handle_get_seo_recommendations']);
        add_action('wp_ajax_aysa_generate_seo_suggestion', [$this, 'handle_generate_seo_suggestion']);
        add_action('wp_ajax_aysa_metabox_async_start', [$this, 'handle_metabox_async_start']);
        add_action('wp_ajax_aysa_metabox_async_status', [$this, 'handle_metabox_async_status']);
        add_action('wp_ajax_aysa_start_keyword_validation', [$this, 'handle_start_keyword_validation']);
        add_action('wp_ajax_aysa_keyword_validation_status', [$this, 'handle_keyword_validation_status']);
        add_action('wp_ajax_aysa_get_term_description', [$this, 'get_term_description']);
        add_action('wp_ajax_aysa_set_featured_image', [$this, 'handle_set_featured_image']);
        add_action('wp_ajax_aysa_get_term_data', [$this, 'get_term_data']);
        add_action('wp_ajax_aysa_save_field', [$this, 'handle_save_field']);
        add_action('wp_ajax_aysa_category_association', [$this, 'handle_keyword_category_association']);
        add_action('wp_ajax_aysa_generate_article_image', [$this, 'handle_generate_article_image']);
        add_action('aysa_metabox_async_event', [$this, 'run_metabox_async_event'], 10, 1);
        
        // ✅ NEW: API call handlers for upsert and insert many
        add_action('wp_ajax_aysa_upsert_entity', [$this, 'handle_upsert_entity']);
        add_action('wp_ajax_aysa_insert_many_keywords', [$this, 'handle_insert_many_keywords']);
        add_action('wp_ajax_aysa_delete_entity_associations', [$this, 'handle_delete_entity_associations']);
        
        
        // ✅ NEW: Auto API call when category SEO data is saved
        // Auto-upsert category to API when SEO data is saved (handled by JavaScript now)
        // add_action('aysa_category_seo_data_saved', [$this, 'auto_upsert_category_to_api'], 10, 3);
        
        // Enqueue scripts
        add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
        
        // ✅ NEW: AJAX handler for adding redirects to .htaccess
        add_action('wp_ajax_aysa_add_htaccess_redirect', [$this, 'handle_add_htaccess_redirect']);
        
        // ✅ NEW: AJAX handler for getting entity details (name and URL)
        add_action('wp_ajax_aysa_get_entity_details', [$this, 'handle_get_entity_details']);
        add_action('wp_ajax_aysa_get_entities_inventory', [$this, 'handle_get_entities_inventory']);
    }

    private function resolve_bound_project_id($requested_project_id = 0) {
        $bound_project_id = intval(get_option('aysa_project_id', 0));
        $requested_project_id = intval($requested_project_id);
        if ($bound_project_id <= 0 || ($requested_project_id > 0 && $requested_project_id !== $bound_project_id)) {
            return 0;
        }
        return $bound_project_id;
    }

    private function current_user_can_edit_entity($entity_id, $entity_type) {
        $entity_id = intval($entity_id);
        $entity_type = strtolower(sanitize_key((string) $entity_type));
        if ($entity_id <= 0) {
            return false;
        }
        if (in_array($entity_type, array('product', 'page', 'post'), true)) {
            return current_user_can('edit_post', $entity_id);
        }
        if (in_array($entity_type, array('category', 'term'), true)) {
            $term = get_term($entity_id);
            return $term && !is_wp_error($term) && current_user_can('edit_terms', $term->taxonomy);
        }
        return false;
    }

    private function response_matches_project_run($result, $project_id, $run_id = 0) {
        if (!is_array($result) || intval($project_id) <= 0) {
            return false;
        }

        foreach (array($result, is_array($result['run'] ?? null) ? $result['run'] : array()) as $payload) {
            $response_project_id = intval($payload['project_id'] ?? $payload['projectId'] ?? 0);
            if ($response_project_id > 0 && $response_project_id !== intval($project_id)) {
                return false;
            }
        }

        $run_id = intval($run_id);
        $response_run_id = intval($result['run']['id'] ?? $result['run_id'] ?? 0);
        return $run_id <= 0 || $response_run_id <= 0 || $response_run_id === $run_id;
    }

    public function enqueue_scripts($hook)
    {
        // Check for category edit/add pages
        if ($hook == 'term.php' || $hook == 'edit-tags.php') {
            // Check if we're on a product category page or normal category page
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reviewed callback inventory: this read-only/core lifecycle request is authenticated by WordPress or an enclosing guard and performs no unapproved write.
            if (isset($_GET['taxonomy']) && ($_GET['taxonomy'] == 'product_cat' || $_GET['taxonomy'] == 'category')) {
                $this->load_aysa_scripts_styles();
            }
        }
        
        // Check for WooCommerce products, Pages, and Posts edit screens
        if ($hook == 'post.php' || $hook == 'post-new.php') {
            $screen = get_current_screen();
            if (isset($screen->post_type) && ($screen->post_type == 'product' || $screen->post_type == 'page' || $screen->post_type == 'post')) {
                $this->load_aysa_scripts_styles();
            }
        }
        

        
        // Check if we're on the keyword discovery page or any other page with keyword listing
        $screen = get_current_screen();
        if (isset($screen->id) && (
            $screen->id === 'toplevel_page_aysa' || 
            strpos($screen->id, 'aysa-research') !== false ||
            strpos($screen->id, 'aysa-keyword-discovery') !== false
        )) {
            wp_enqueue_script(
                'aysa_keyword_association-js',
                AYSA_PLUGIN_URL . 'assets/js/keyword-association.js',
                ['jquery'],
                AYSA_VERSION,
                true
            );
            
            // Add security nonce for AJAX requests
            wp_localize_script(
                'aysa_keyword_association-js',
                'aysaKeywordParams',
                [
                    'ajaxurl' => admin_url('admin-ajax.php'),
                    'nonce' => wp_create_nonce('keyword_category_association')
                ]
            );
        }
    }

    /**
     * Load AYSA scripts and styles
     */
    private function load_aysa_scripts_styles()
    {
        // Enqueue metabox CSS
        wp_enqueue_style(
            'aysa_product_metabox_admin-css',
            AYSA_PLUGIN_URL . 'assets/css/metabox.css',
            [],
            AYSA_VERSION,
            'all'
        );

        // Unified visual layer for all AYSA metabox entities
        wp_enqueue_style(
            'aysa_metabox_unified_admin-css',
            AYSA_PLUGIN_URL . 'admin/css/aysa-metabox.css',
            ['aysa_product_metabox_admin-css'],
            AYSA_VERSION,
            'all'
        );
        
        // Enqueue credits component CSS
        wp_enqueue_style(
            'aysa-credits-component',
            AYSA_PLUGIN_URL . 'assets/css/aysa-credits-component.css',
            array(),
            AYSA_VERSION
        );
        
        // Enqueue credits component JS with lower priority to avoid conflicts
        wp_enqueue_script(
            'aysa-credits-component',
            AYSA_PLUGIN_URL . 'assets/js/aysa-credits-component.js',
            array('jquery'),
            AYSA_VERSION,
            true // Load in footer to avoid conflicts with metabox scripts
        );
        
        // Localize credits component
        wp_localize_script(
            'aysa-credits-component',
            'aysa_credits_params',
            array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('aysa_credits_nonce'),
                'api_base_url' => AYSA_API_BASE_URL,
                'user_id' => get_current_user_id()
            )
        );
        
        // First, enqueue metabox.js (main script)
        wp_enqueue_script(
            'aysa_product_metabox_admin-js',
            AYSA_PLUGIN_URL . 'assets/js/metabox.js',
            ['jquery'],
            AYSA_VERSION . '.' . time(),
            true
        );
        
        // Then enqueue actions.js
        wp_enqueue_script(
            'aysa_actions-js',
            AYSA_PLUGIN_URL . 'admin/js/aysa-actions.js',
            ['jquery', 'aysa_product_metabox_admin-js'],
            AYSA_VERSION . '.' . time(),
            true
        );
        
        // Then enqueue keywords.js
        wp_enqueue_script(
            'aysa_keywords-js',
            AYSA_PLUGIN_URL . 'admin/js/aysa-keywords.js',
            ['jquery', 'aysa_product_metabox_admin-js', 'aysa_actions-js'],
            AYSA_VERSION . '.' . time(),
            true
        );
        
        // Get project ID from options
        $project_id = get_option('aysa_project_id', 0);
        
        // Instantiate Aysa_Media to get language settings
        if (!class_exists('Aysa_Media')) {
            // Asigură-te că AYSA_PLUGIN_DIR este definit corect sau înlocuiește cu calea potrivită
            if (!defined('AYSA_PLUGIN_DIR')) {
                 define('AYSA_PLUGIN_DIR', plugin_dir_path(dirname(__FILE__, 2))); // Ajustează la nevoie
            }
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-media.php';
        }
        $aysa_media_instance = new Aysa_Media();
        $current_language_optimize = $aysa_media_instance->get_context_language_optimize();
        $global_language_fallback = $aysa_media_instance->get_global_language_optimize();

        // Localize script with project ID and other necessary data
        wp_localize_script(
            'aysa_product_metabox_admin-js', // Acest script încarcă aysa-actions.js ca dependență
            'aysaParams',
            [
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('aysa_metabox_nonce'),
                'project_id' => $project_id,
                'api_base_url' => AYSA_API_BASE_URL,
                'language_optimize' => $current_language_optimize,
                'global_language_optimize' => $global_language_fallback,
                // Adaugă și entityType dacă este disponibil în acest context
                'entityType' => $this->get_current_entity_type() 
            ]
        );
    }

    /**
     * Helper function to get current entity type (product, category, page)
     * This should be adapted based on how entity type is determined in this class
     */
    private function get_current_entity_type() {
        $screen = get_current_screen();
        if ($screen) {
            if ($screen->post_type === 'product') return 'Product';
            if ($screen->taxonomy === 'product_cat') return 'Category';
            if ($screen->post_type === 'page') return 'Page';
        }
        return 'Page'; // Default
    }

    /**
     * Add inline styles to ensure the metabox displays correctly
     */
    private function add_inline_styles()
    {
        $css = "
            .aysa-category-metabox {
                padding: 10px;
                margin-top: 20px;
                background: #fff;
                border: 1px solid #e2e4e7;
            }
            
            #custom_product_meta_box.category_box {
                margin-top: 20px;
                border: 1px solid #ccd0d4;
                box-shadow: 0 1px 1px rgba(0,0,0,.04);
            }
            
            #custom_product_meta_box .inside {
                margin: 0;
                padding: 0;
            }
            
            /* Spinner and Success State */
            .btn-accept.save {
                position: relative;
            }
            
            .btn-accept.save.success {
                background-color: #46B450 !important;
            }
            
            .spinner {
                display: none;
                float: none;
                visibility: visible;
                margin-left: 5px;
                vertical-align: middle;
            }
            
            .spinner.is-active {
                display: inline-block;
            }

            /* Sidebar metabox styles */
            #side-sortables .keyword-row {
                flex-direction: column;
            }
            
            #side-sortables .keyword-column {
                width: 100%;
                margin-bottom: 10px;
            }
            
            #side-sortables .keyword-button {
                align-self: flex-start;
            }
            
            #side-sortables .tabs {
                flex-wrap: wrap;
            }
            
            #side-sortables .tabs li {
                padding: 8px 12px;
                font-size: 12px;
            }
            
            #side-sortables .btn-universal {
                font-size: 12px;
                padding: 4px 8px;
                margin-top: 5px;
            }
            
            #side-sortables #panel-advanced .robots-meta {
                grid-template-columns: 1fr;
            }
            
            #side-sortables .seo-score-box {
                transform: scale(0.8);
                transform-origin: top center;
                margin: 0;
            }
            
            #side-sortables #aysaSeoRadarChart {
                width: 260px !important;
                height: 260px !important;
            }
            
            #side-sortables .metric-pill {
                font-size: 11px;
                padding: 4px 10px;
            }
        ";
        
        wp_add_inline_style('aysa_product_metabox_admin-css', $css);
    }

    /**
     * Add metabox for the edit form (existing categories)
     */
    public function add_cat_metabox()
    {
        // Get the category ID
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reviewed callback inventory: this read-only/core lifecycle request is authenticated by WordPress or an enclosing guard and performs no unapproved write.
        $term_id = isset($_GET['tag_ID']) ? intval(wp_unslash($_GET['tag_ID'])) : 0;
        
        // Get stored keyword data
        $keyword_data = get_term_meta($term_id, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }
        
        // Get project ID from options
        $project_id = get_option('aysa_project_id', 0);
        
        // Enqueue and localize script with safe entityUrl
        wp_enqueue_script('aysa_product_metabox_admin-js');
        $term_link = get_term_link($term_id, 'product_cat');
        if ( is_wp_error( $term_link ) ) {
            $term_link = '';
        }
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'termId'     => $term_id,
                'entityType' => 'term',
                'entityUrl'  => $term_link,
                'project_id' => $project_id,
                'nonce'      => wp_create_nonce('aysa_metabox_nonce'),
                'ajax_url'   => admin_url('admin-ajax.php'),
                'api_base_url' => AYSA_API_BASE_URL
            )
        );
        echo '<div id="custom_product_meta_box" class="postbox category_box">
                <div class="postbox-header"><h2>AYSA SEO</h2></div>
                <div class="inside">';
        
        // Include the metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-category-metabox.php';
        
        echo '</div></div>';
        
        // Add nonce field for security
        wp_nonce_field('aysa_category_save', 'aysa_category_nonce');
        
        // Add JavaScript to move metabox to correct position if needed
        ?>
        <script>
        document.addEventListener('DOMContentLoaded', function() {
            // Move AYSA metabox after WordPress standard fields
            var aysaMetabox = document.getElementById('custom_product_meta_box');
            var targetElement = document.querySelector('.form-table');
            
            if (aysaMetabox && targetElement && targetElement.parentNode) {
                // Remove metabox from current position
                aysaMetabox.parentNode.removeChild(aysaMetabox);
                
                // Insert after the form table
                targetElement.parentNode.insertBefore(aysaMetabox, targetElement.nextSibling);
                
                console.log('AYSA metabox repositioned after standard fields');
            } else {
                console.log('Could not reposition AYSA metabox - elements not found');
            }
        });
        </script>
        <?php
        
        // Add JavaScript to handle data population
        ?>
        <script>
        document.addEventListener('DOMContentLoaded', function() {
            console.log('Category metabox loaded - DOM Content Loaded');
            
            // Verifică dacă funcția loadDataFromAPI există
            if (typeof window.loadDataFromAPI === 'function') {
                console.log('loadDataFromAPI function found, calling it');
                try {
                    // Load data from API
                    window.loadDataFromAPI();
                } catch (error) {
                    console.error('Error calling loadDataFromAPI:', error);
                }
            } else {
                console.info('AYSA metabox loader not available in this editor context; skipping remote preload.');
            }
            
            // Check for stored keyword data as fallback
            const keywordData = <?php echo json_encode($keyword_data); ?>;
            if (keywordData && Object.keys(keywordData).length > 0) {
                console.log('Using stored keyword data as fallback');
                try {
                    // Actualizare manuală a câmpurilor
                    var mainKeywordInput = document.getElementById('aysa-entity-keyword');
                    var secondKeywordInput = document.getElementById('aysa-entity-second-keyword');
                    var metaTitleInput = document.getElementById('aysa-entity-meta-title');
                    var metaDescriptionInput = document.getElementById('aysa-entity-meta-description');
                    var metaKeywordsInput = document.getElementById('aysa-entity-meta-keywords');
                    
                    if (mainKeywordInput && keywordData.keyword) {
                        mainKeywordInput.value = keywordData.keyword;
                    }
                    
                    if (secondKeywordInput && keywordData.second_keyword) {
                        secondKeywordInput.value = keywordData.second_keyword;
                    }
                    
                    if (metaTitleInput && keywordData.meta_title) {
                        metaTitleInput.value = keywordData.meta_title;
                    }
                    
                    if (metaDescriptionInput && keywordData.meta_description) {
                        metaDescriptionInput.value = keywordData.meta_description;
                    }
                    
                    if (metaKeywordsInput && keywordData.meta_keywords) {
                        metaKeywordsInput.value = keywordData.meta_keywords;
                    }
                } catch (error) {
                    console.error('Error updating fields with stored data:', error);
                }
            } else {
                console.log('No stored keyword data available');
            }
        });
        </script>
        <?php
    }
    
    /**
     * Add metabox for the add form (new categories)
     */
    public function add_cat_metabox_new()
    {
        // Enqueue and localize script for new category (no URL yet)
        wp_enqueue_script('aysa_product_metabox_admin-js');
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'termId'     => 0,
                'entityType' => 'term',
                'entityUrl'  => '',
                'nonce'      => wp_create_nonce('aysa_category_metabox')
            )
        );
        echo '<div id="custom_product_meta_box_new" class="form-field aysa-category-metabox">
                <h2>AYSA SEO</h2>
                <div class="inside">';
        
        // Include the metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-category-metabox.php';
        
        echo '</div></div>';
        
        // Add nonce field for security
        wp_nonce_field('aysa_category_save', 'aysa_category_nonce');
    }

    /**
     * Save category SEO data
     */
    public function save_category_seo_data($term_id)
    {
        // ✅ IMPROVED: Enhanced save mechanism for better compatibility
        
        // Basic validation first
        if (!$term_id || !is_numeric($term_id)) {
            return;
        }
        
        // Get the term to validate it exists
        $term = get_term($term_id);
        if (is_wp_error($term) || !$term) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Invalid term_id or term not found: ' . $term_id);
            }
            return;
        }
        
        // Check user capabilities
        if (!current_user_can('manage_categories') && !current_user_can('edit_terms', $term->taxonomy)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: User does not have permission to edit terms');
            }
            return;
        }
        
        // ✅ IMPROVED: Enhanced data collection and validation
        $has_aysa_data = false;
        $aysa_data_sources = array();
        
        // Check for AYSA nonce and data in POST
        if (isset($_POST['aysa_category_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aysa_category_nonce'])), 'aysa_category_save')) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'form_post';
        }
        
        // Check for direct POST data (without nonce - for WordPress core saves)
        $has_post_data = isset($_POST['aysa_meta_title']) || isset($_POST['aysa_meta_description']) || 
                        isset($_POST['aysa_meta_keywords']) || isset($_POST['keyword']) || isset($_POST['second_keyword']);
        
        if ($has_post_data) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'direct_post';
        }
        
        // Check for AYSA data in term meta (from AJAX saves)
        $temp_meta = get_term_meta($term_id, '_aysa_temp_save_data', true);
        if (!empty($temp_meta) && is_array($temp_meta)) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'temp_meta';
        }
        
        // Check for WordPress save preference
        $use_wordpress_save = apply_filters('aysa_use_wordpress_save', true);
        $force_save = apply_filters('aysa_force_save_category', false, $term_id, $term);
        
        // ✅ IMPROVED: Enhanced logging with POST data analysis
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA SEO: Category save triggered - Term ID: ' . $term_id . ' (' . $term->name . ')');
            aysa_debug_log('AYSA SEO: Term taxonomy: ' . $term->taxonomy);
            aysa_debug_log('AYSA SEO: Has AYSA data: ' . ($has_aysa_data ? 'yes' : 'no'));
            aysa_debug_log('AYSA SEO: Data sources: ' . implode(', ', $aysa_data_sources));
            aysa_debug_log('AYSA SEO: use_wordpress_save: ' . ($use_wordpress_save ? 'true' : 'false'));
            aysa_debug_log('AYSA SEO: force_save: ' . ($force_save ? 'true' : 'false'));
        aysa_debug_log('AYSA SEO: POST action: ' . (isset($_POST['action']) ? sanitize_key(wp_unslash($_POST['action'])) : 'not set'));
            aysa_debug_log('AYSA SEO: POST has submit: ' . (isset($_POST['submit']) ? 'yes' : 'no'));
            aysa_debug_log('AYSA SEO: POST has save: ' . (isset($_POST['save']) ? 'yes' : 'no'));
            aysa_debug_log('AYSA SEO: Will call redirect handler: ' . ($has_aysa_data || $force_save ? 'YES' : 'NO'));
        }
        
        // If we don't have AYSA data and aren't forcing save, skip
        if (!$has_aysa_data && !$force_save) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: No AYSA data found, skipping save');
            }
            return;
        }
        
        // If WordPress save is disabled and we're not forcing, skip
        if (!$use_wordpress_save && !$force_save) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: WordPress save disabled, data will be saved via API');
            }
            return;
        }
        
        // ✅ IMPROVED: Enhanced data collection and merging
        $existing_keyword_data = get_term_meta($term_id, 'aysa_keyword_data', true);
        if (!is_array($existing_keyword_data)) {
            $existing_keyword_data = array();
        }
        
        // Collect data from multiple sources
        $new_data = array();
        
        // From POST data (both nonce-verified and direct)
        if (in_array('form_post', $aysa_data_sources) || in_array('direct_post', $aysa_data_sources)) {
            $new_data = array_merge($new_data, array(
                'keyword' => isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '',
                'second_keyword' => isset($_POST['second_keyword']) ? sanitize_text_field(wp_unslash($_POST['second_keyword'])) : '',
                'track' => isset($_POST['track']) ? 1 : 0,
                'meta_title' => isset($_POST['aysa_meta_title']) ? sanitize_text_field(wp_unslash($_POST['aysa_meta_title'])) : '',
                'meta_description' => isset($_POST['aysa_meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['aysa_meta_description'])) : '',
                'meta_keywords' => isset($_POST['aysa_meta_keywords']) ? sanitize_text_field(wp_unslash($_POST['aysa_meta_keywords'])) : '',
            ));
        }
        
        // From temporary meta (AJAX saves)
        if (in_array('temp_meta', $aysa_data_sources)) {
            $temp_data = array(
                'keyword' => isset($temp_meta['keyword']) ? sanitize_text_field($temp_meta['keyword']) : '',
                'second_keyword' => isset($temp_meta['second_keyword']) ? sanitize_text_field($temp_meta['second_keyword']) : '',
                'track' => isset($temp_meta['track']) ? intval($temp_meta['track']) : 0,
                'meta_title' => isset($temp_meta['meta_title']) ? sanitize_text_field($temp_meta['meta_title']) : '',
                'meta_description' => isset($temp_meta['meta_description']) ? sanitize_textarea_field($temp_meta['meta_description']) : '',
                'meta_keywords' => isset($temp_meta['meta_keywords']) ? sanitize_text_field($temp_meta['meta_keywords']) : '',
            );
            
            // Merge temp data, giving priority to non-empty values
            foreach ($temp_data as $key => $value) {
                if (!empty($value) || !isset($new_data[$key]) || empty($new_data[$key])) {
                    $new_data[$key] = $value;
                }
            }
            
            // Clean up temp meta
            delete_term_meta($term_id, '_aysa_temp_save_data');
        }
        
        // ✅ IMPROVED: Merge with existing data, preserving important values
        $keyword_data = array_merge($existing_keyword_data, array_filter($new_data, function($value) {
            return $value !== '' && $value !== null;
        }));
        
        // Add metadata
        $keyword_data['last_updated'] = current_time('mysql');
        $keyword_data['saved_via'] = 'wordpress_hook';
        $keyword_data['data_sources'] = $aysa_data_sources;
        
        // ✅ IMPROVED: Enhanced save with backup
        $save_result = update_term_meta($term_id, 'aysa_keyword_data', $keyword_data);
        
        if ($save_result) {
            // Create backup of successful save
            update_term_meta($term_id, '_aysa_keyword_data_backup', array(
                'data' => $keyword_data,
                'timestamp' => current_time('timestamp'),
                'term_taxonomy' => $term->taxonomy
            ));
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Successfully saved category keyword data: ' . aysa_debug_export($keyword_data, true));
            }
            
            // Hook for other plugins/systems
            do_action('aysa_category_seo_data_saved', $term_id, $keyword_data, $term);
            
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Failed to save category keyword data for term_id: ' . $term_id);
            }
        }
        
        // Save advanced settings if present
        $this->save_advanced_category_settings($term_id);
        
        // ✅ NEW: Handle redirect API call if permalink changed
        // Call redirect handler ALWAYS (permalinks can change independently of AYSA data)
        $this->handle_category_redirect_if_needed($term_id, $term);
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA SEO: Called redirect handler for category ' . $term_id . ' (independent of AYSA data)');
        }
    }
    
    /**
     * Save advanced category settings
     */
    private function save_advanced_category_settings($term_id)
    {
        // Debug        
        if (defined('WP_DEBUG') && WP_DEBUG) {            
            aysa_debug_log('AYSA SEO: Saving advanced category settings for term_id: ' . $term_id);        
        }                
        
        // Get existing robots meta        
        $existing_robots_meta = get_term_meta($term_id, 'aysa_robots_meta', true);        
        if (!is_array($existing_robots_meta)) {            
            $existing_robots_meta = array();        
        }                
        
        // Save robots meta settings        
        $robots_meta = array(            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'index' => isset($_POST['index']) ? sanitize_text_field(wp_unslash($_POST['index'])) : (isset($existing_robots_meta['index']) ? $existing_robots_meta['index'] : ''),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'nofollow' => isset($_POST['nofollow']) ? 1 : (isset($existing_robots_meta['nofollow']) ? $existing_robots_meta['nofollow'] : 0),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'noarchive' => isset($_POST['noarchive']) ? 1 : (isset($existing_robots_meta['noarchive']) ? $existing_robots_meta['noarchive'] : 0),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'noimageindex' => isset($_POST['noimageindex']) ? 1 : (isset($existing_robots_meta['noimageindex']) ? $existing_robots_meta['noimageindex'] : 0),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'nosnippet' => isset($_POST['nosnippet']) ? 1 : (isset($existing_robots_meta['nosnippet']) ? $existing_robots_meta['nosnippet'] : 0),        
        );                
        
        update_term_meta($term_id, 'aysa_robots_meta', $robots_meta);                
        
        if (defined('WP_DEBUG') && WP_DEBUG) {            
            aysa_debug_log('AYSA SEO: Saved robots meta: ' . aysa_debug_export($robots_meta, true));        
        }                
        
        // Get existing advanced robots        
        $existing_advanced_robots = get_term_meta($term_id, 'aysa_advanced_robots', true);        
        if (!is_array($existing_advanced_robots)) {            
            $existing_advanced_robots = array();        
        }                
        
        // Save advanced robots settings        
        $advanced_robots = array(            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'max_snippet' => isset($_POST['max_snippet']) ? 1 : (isset($existing_advanced_robots['max_snippet']) ? $existing_advanced_robots['max_snippet'] : 0),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'max_snippet_value' => isset($_POST['number-max-snippet']) ? intval(wp_unslash($_POST['number-max-snippet'])) : (isset($existing_advanced_robots['max_snippet_value']) ? $existing_advanced_robots['max_snippet_value'] : -1),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'max_video_preview' => isset($_POST['max_video_preview']) ? 1 : (isset($existing_advanced_robots['max_video_preview']) ? $existing_advanced_robots['max_video_preview'] : 0),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'max_video_preview_value' => isset($_POST['number-max-video-preview']) ? intval(wp_unslash($_POST['number-max-video-preview'])) : (isset($existing_advanced_robots['max_video_preview_value']) ? $existing_advanced_robots['max_video_preview_value'] : -1),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'max_image_preview' => isset($_POST['max_image_preview']) ? 1 : (isset($existing_advanced_robots['max_image_preview']) ? $existing_advanced_robots['max_image_preview'] : 0),            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'image_preview' => isset($_POST['image_preview']) ? sanitize_text_field(wp_unslash($_POST['image_preview'])) : (isset($existing_advanced_robots['image_preview']) ? $existing_advanced_robots['image_preview'] : 'large'),        
        );                
        
        update_term_meta($term_id, 'aysa_advanced_robots', $advanced_robots);                
        
        if (defined('WP_DEBUG') && WP_DEBUG) {            
            aysa_debug_log('AYSA SEO: Saved advanced robots: ' . aysa_debug_export($advanced_robots, true));        
        }                
        
        // Save canonical URL        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $canonical_url = isset($_POST['canonical_url']) ? esc_url_raw(wp_unslash($_POST['canonical_url'])) : '';        
        update_term_meta($term_id, 'aysa_canonical_url', $canonical_url);                
        
        // Save SEO settings        
        $seo_settings = array(            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'targeted_se' => isset($_POST['targeted_se']) ? sanitize_text_field(wp_unslash($_POST['targeted_se'])) : '',            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'targeted_country' => isset($_POST['targeted_country']) ? sanitize_text_field(wp_unslash($_POST['targeted_country'])) : '',            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'targeted_region' => isset($_POST['targeted_region']) ? sanitize_text_field(wp_unslash($_POST['targeted_region'])) : '',            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'targeted_city' => isset($_POST['targeted_city']) ? sanitize_text_field(wp_unslash($_POST['targeted_city'])) : '',            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'update_frequency' => isset($_POST['update_frequency']) ? sanitize_text_field(wp_unslash($_POST['update_frequency'])) : '',            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'language_optimize' => isset($_POST['language_optimize']) ? sanitize_text_field(wp_unslash($_POST['language_optimize'])) : '',            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            'device_optimize' => isset($_POST['device_optimize']) ? sanitize_text_field(wp_unslash($_POST['device_optimize'])) : '',       
        );                
        
        update_term_meta($term_id, 'aysa_seo_settings', $seo_settings);    
    }
    
    /**
     * Ajax handler to get the original term description
     */
    public function get_term_description() {
        // Verify nonce
        check_ajax_referer('aysa_metabox_nonce', 'nonce');
        
        // Get term ID
        $term_id = isset($_POST['term_id']) ? intval(wp_unslash($_POST['term_id'])) : 0;
        
        if (!$term_id) {
            wp_send_json_error(array('message' => 'Invalid term ID'));
            return;
        }
        
        // Get the term
        $term = get_term($term_id, 'product_cat');
        
        if (is_wp_error($term) || !$term) {
            wp_send_json_error(array('message' => 'Term not found'));
            return;
        }
        
        // Return the term description
        wp_send_json_success(array(
            'description' => $term->description,
            'term_id' => $term_id
        ));
    }

    /**
     * Ajax handler to get all term data (name, slug, description)
     */
    public function get_term_data() {
        // Verify nonce
        check_ajax_referer('aysa_metabox_nonce', 'nonce');
        
        // Get term ID
        $term_id = isset($_POST['term_id']) ? intval(wp_unslash($_POST['term_id'])) : 0;
        
        if (!$term_id) {
            wp_send_json_error(array(
                'message' => 'Invalid term ID',
                'request' => $_POST
            ));
            return;
        }
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log("AYSA SEO: get_term_data called for term ID: $term_id");
        }
        
        // Try to get taxonomy from request
        $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field(wp_unslash($_POST['taxonomy'])) : 'product_cat';
        
        // Get the term
        $term = get_term($term_id, $taxonomy);
        
        // If not found with the provided taxonomy, try to get the term with any taxonomy
        if (is_wp_error($term) || !$term) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log("AYSA SEO: Term not found with taxonomy $taxonomy, trying without specific taxonomy");
            }
            $term = get_term($term_id);
        }
        
        if (is_wp_error($term) || !$term) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                if (is_wp_error($term)) {
                    aysa_debug_log("AYSA SEO: Term error: " . $term->get_error_message());
                } else {
                    aysa_debug_log("AYSA SEO: Term not found with ID: $term_id");
                }
            }
            
            wp_send_json_error(array(
                'message' => 'Term not found',
                'term_id' => $term_id,
                'taxonomy' => $taxonomy
            ));
            return;
        }
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log("AYSA SEO: Term found: " . aysa_debug_export($term, true));
        }
        
        // Return all term data
        wp_send_json_success(array(
            'name' => $term->name,
            'slug' => $term->slug,
            'description' => $term->description,
            'term_id' => $term_id,
            'taxonomy' => $term->taxonomy
        ));
    }
    
    /**
     * Handle AJAX request to associate a keyword with a category
     */
    public function handle_keyword_category_association()
    {
        // Verify nonce
        check_ajax_referer('keyword_category_association', 'nonce');
        
        // Get data from request
        $category_id = isset($_POST['category_id']) ? intval(wp_unslash($_POST['category_id'])) : 0;
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reviewed release input boundary: the guarded callback preserves structured input only until its field-specific validation or normalizer, before persistence, output, SQL, or a broker request.
        $keyword_data = isset($_POST['keyword_data']) ? wp_unslash($_POST['keyword_data']) : array();
        $keyword_id = isset($_POST['keyword_id']) ? intval(wp_unslash($_POST['keyword_id'])) : 0;
        $product_id = isset($_POST['product_id']) ? intval(wp_unslash($_POST['product_id'])) : 0;
        
        if (!$category_id) {
            wp_send_json_error('Invalid category ID');
            return;
        }

        $category = get_term($category_id);
        if (!$category || is_wp_error($category) || !current_user_can('edit_terms', $category->taxonomy)) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        if ($product_id > 0 && !current_user_can('edit_post', $product_id)) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        // Format keyword data to match our expected structure
        $formatted_keyword_data = array(
            'keyword' => isset($keyword_data['keyword']) ? sanitize_text_field($keyword_data['keyword']) : '',
            'second_keyword' => isset($keyword_data['second_keyword']) ? sanitize_text_field($keyword_data['second_keyword']) : '',
            'track' => 1, // Enable tracking by default for associated keywords
            'meta_title' => isset($keyword_data['meta_title']) ? sanitize_text_field($keyword_data['meta_title']) : '',
            'meta_description' => isset($keyword_data['meta_description']) ? sanitize_text_field($keyword_data['meta_description']) : '',
            'meta_keywords' => isset($keyword_data['keywords']) ? sanitize_text_field($keyword_data['keywords']) : '',
        );
        
        // If no meta title is provided, create one using the keyword
        if (empty($formatted_keyword_data['meta_title']) && !empty($formatted_keyword_data['keyword'])) {
            $formatted_keyword_data['meta_title'] = $formatted_keyword_data['keyword'] . ' | ' . get_bloginfo('name');
        }
        
        // If no meta description is provided, create one using the keyword
        if (empty($formatted_keyword_data['meta_description']) && !empty($formatted_keyword_data['keyword'])) {
            $formatted_keyword_data['meta_description'] = 'Shop our collection of ' . $formatted_keyword_data['keyword'] . '. Find the best quality products at competitive prices.';
        }
        
        // Save the keyword data to the category
        update_term_meta($category_id, 'aysa_keyword_data', $formatted_keyword_data);
        
        // Also store the keyword ID if provided
        if ($keyword_id) {
            update_term_meta($category_id, 'aysa_keyword_id', $keyword_id);
        }
        
        // If a product ID is provided, associate the keyword with that product as well
        if ($product_id) {
            // Save the keyword data to the product
            update_post_meta($product_id, 'aysa_keyword_data', $formatted_keyword_data);
            
            // Store the keyword ID
            if ($keyword_id) {
                update_post_meta($product_id, 'aysa_keyword_id', $keyword_id);
            }
            
            // Associate the product with the category
            wp_set_object_terms($product_id, $category_id, 'product_cat', true);
        }
        
        $this->track_wallet_usage(
            'aysa_category_association',
            'category_association',
            5,
            array(
                'module' => 'on_page_optimization',
                'category_id' => intval($category_id),
                'product_id' => intval($product_id),
            ),
            '/v1/onpage/category-association'
        );

        wp_send_json_success(array(
            'message' => 'Keyword associated with category successfully',
            'category_id' => $category_id,
            'product_id' => $product_id,
            'keyword_data' => $formatted_keyword_data
        ));
    }
    
    /**
     * Handle AJAX request to save a single field
     */
    public function handle_save_field()
    {
        // Verify nonce
        check_ajax_referer('aysa_category_metabox', 'nonce');
        
        // Get data from request
        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field(wp_unslash($_POST['entity_type'])) : 'term';
        $field_name = isset($_POST['field_name']) ? sanitize_text_field(wp_unslash($_POST['field_name'])) : '';
        $field_value = isset($_POST['field_value']) ? sanitize_text_field(wp_unslash($_POST['field_value'])) : '';
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log("AYSA SEO: Saving field - entity_id: $entity_id, entity_type: $entity_type, field_name: $field_name, field_value: $field_value");
        }
        
        if (!$entity_id || !$field_name) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log("AYSA SEO: Missing required data for field save");
            }
            wp_send_json_error('Missing required data');
            return;
        }
        
        // Get existing keyword data based on entity type
        $keyword_data = array();
        
        if ($entity_type === 'term') {
            $keyword_data = get_term_meta($entity_id, 'aysa_keyword_data', true);
        } else {
            $keyword_data = get_post_meta($entity_id, 'aysa_keyword_data', true);
        }
        
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log("AYSA SEO: Existing keyword data: " . aysa_debug_export($keyword_data, true));
        }
        
        // Map field name to database field
        $db_field = $field_name;
        switch ($field_name) {
            case 'keyword':
                $db_field = 'keyword';
                break;
            case 'second-keyword':
                $db_field = 'second_keyword';
                break;
            case 'meta-title':
                $db_field = 'meta_title';
                break;
            case 'meta-description':
                $db_field = 'meta_description';
                break;
            case 'meta-keywords':
                $db_field = 'meta_keywords';
                break;
        }
        
        // Update keyword data
        $keyword_data[$db_field] = $field_value;
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log("AYSA SEO: Updated keyword data: " . aysa_debug_export($keyword_data, true));
        }
        
        // Save updated data
        $result = false;
        if ($entity_type === 'term') {
            $result = update_term_meta($entity_id, 'aysa_keyword_data', $keyword_data);
        } else {
            $result = update_post_meta($entity_id, 'aysa_keyword_data', $keyword_data);
        }
        
        if ($result !== false) {
            $this->track_wallet_usage(
                'aysa_save_field',
                'save_field',
                5,
                array(
                    'module' => 'on_page_optimization',
                    'entity_id' => intval($entity_id),
                    'entity_type' => (string) $entity_type,
                    'field' => (string) $db_field,
                ),
                '/v1/onpage/save-field'
            );
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log("AYSA SEO: Field saved successfully");
            }
            wp_send_json_success(array(
                'message' => 'Field saved successfully',
                'field' => $db_field,
                'value' => $field_value
            ));
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log("AYSA SEO: Failed to save field");
            }
            wp_send_json_error(array(
                'message' => 'Failed to save field',
                'field' => $db_field,
                'value' => $field_value
            ));
        }
    }
    
    /**
     * Handle AJAX request to get SEO recommendations
     */
    public function handle_get_seo_recommendations()
    {
        // Verify nonce
        check_ajax_referer('aysa_category_metabox', 'nonce');
        
        // Get term ID from request
        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field(wp_unslash($_POST['entity_type'])) : 'term';
        
        if (!$entity_id) {
            wp_send_json_error('Missing entity ID');
            return;
        }
        
        // Get entity data
        $entity_name = '';
        $entity_description = '';
        
        if ($entity_type === 'term') {
            $term = get_term($entity_id, 'product_cat');
            if ($term && !is_wp_error($term)) {
                $entity_name = $term->name;
                $entity_description = $term->description;
            }
        } else {
            $post = get_post($entity_id);
            if ($post) {
                $entity_name = $post->post_title;
                $entity_description = $post->post_excerpt ?: $post->post_content;
                
                // Trim description if needed
                if (strlen($entity_description) > 300) {
                    $entity_description = substr($entity_description, 0, 297) . '...';
                }
            }
        }
        
        if (empty($entity_name)) {
            wp_send_json_error('Could not get entity data');
            return;
        }
        
        // Generate recommendations
        $site_name = get_bloginfo('name');
        
        $recommendations = array(
            'suggested_keyword' => $entity_name,
            'suggested_second_keyword' => $entity_name . ' ' . $site_name,
            'suggested_meta_title' => $entity_name . ' | ' . $site_name,
            'suggested_meta_description' => 'Shop our collection of ' . $entity_name . '. Find the best quality products at competitive prices.',
            'suggested_meta_keywords' => $entity_name . ', ' . strtolower($entity_name) . ', buy ' . strtolower($entity_name)
        );
        
        wp_send_json_success($recommendations);
    }

    /**
     * Generate one SEO suggestion via local broker (/v1/content/seo-suggestion).
     */
    private function metabox_async_supported_tasks() {
        return array('seo_suggestion', 'article_image');
    }

    private function metabox_async_status_key($task, $project_id, $user_id) {
        return 'aysa_metabox_async_' . sanitize_key((string) $task) . '_' . intval($project_id) . '_' . intval($user_id);
    }

    private function metabox_async_get_status($task, $project_id, $user_id) {
        $status = get_option($this->metabox_async_status_key($task, $project_id, $user_id), array());
        return is_array($status) ? $status : array();
    }

    private function metabox_async_save_status($task, $project_id, $user_id, $status) {
        update_option($this->metabox_async_status_key($task, $project_id, $user_id), is_array($status) ? $status : array(), false);
    }

    private function metabox_async_output_status($status) {
        if (!is_array($status)) {
            return array(
                'status' => 'idle',
                'result' => null,
                'error' => '',
            );
        }
        $clean = $status;
        unset($clean['_request']);
        return $clean;
    }

    private function prepare_seo_suggestion_request($input) {
        $type = isset($input['type']) ? sanitize_key((string) $input['type']) : '';
        $keyword = isset($input['keyword']) ? sanitize_text_field((string) $input['keyword']) : '';
        $language = isset($input['language']) ? sanitize_text_field((string) $input['language']) : 'ro';
        $brandName = isset($input['brand_name']) ? sanitize_text_field((string) $input['brand_name']) : '';
        $projectId = $this->resolve_bound_project_id(isset($input['project_id']) ? intval($input['project_id']) : 0);

        $requestPayload = array();
        if (isset($input['payload'])) {
            $decoded = json_decode(wp_unslash((string) $input['payload']), true);
            if (is_array($decoded)) {
                $requestPayload = $decoded;
            }
        }

        if ($type === '') {
            return new WP_Error('missing_type', 'Missing suggestion type');
        }

        if ($projectId <= 0) {
            return new WP_Error('invalid_project_id', 'Invalid project ID');
        }

        if ($keyword === '' && $type !== 'second-keyword') {
            return new WP_Error('missing_keyword', 'Missing keyword');
        }

        $siteHost = wp_parse_url(home_url(), PHP_URL_HOST);
        $siteHost = is_string($siteHost) ? $siteHost : '';

        $brokerPayload = array(
            'project_id' => $projectId,
            'site_host' => $siteHost,
            'suggestion_type' => $type,
            'keyword' => $keyword,
            'language' => $language !== '' ? $language : 'ro',
            'brand_name' => $brandName,
            'request_payload' => $requestPayload,
        );

        return array(
            'task' => 'seo_suggestion',
            'project_id' => $projectId,
            'request_hash' => md5(wp_json_encode($brokerPayload)),
            'payload' => $brokerPayload,
            'type' => $type,
            'keyword' => $keyword,
            'language' => $language,
            'request_payload' => $requestPayload,
        );
    }

    private function execute_seo_suggestion_request($request) {
        $type = (string) ($request['type'] ?? '');
        $keyword = (string) ($request['keyword'] ?? '');
        $language = (string) ($request['language'] ?? 'ro');
        $projectId = intval($request['project_id'] ?? 0);
        $requestPayload = isset($request['request_payload']) && is_array($request['request_payload']) ? $request['request_payload'] : array();
        $brokerPayload = isset($request['payload']) && is_array($request['payload']) ? $request['payload'] : array();

        $result = $this->broker_request('POST', '/v1/content/seo-suggestion', $brokerPayload, 120);
        if (is_wp_error($result)) {
            $fallback = $this->legacy_generate_seo_suggestion($type, $keyword, $language, $requestPayload, $projectId);
            if (is_wp_error($fallback)) {
                return new WP_Error('broker_failed', $result->get_error_message());
            }

            $this->track_wallet_usage(
                'aysa_generate_seo_suggestion',
                'generate_seo_suggestion',
                25,
                array(
                    'module' => 'on_page_optimization',
                    'type' => (string) $type,
                    'provider' => (string) ($fallback['provider'] ?? 'legacy_api'),
                    'source' => 'fallback',
                ),
                '/v1/content/seo-suggestion'
            );

            return array(
                'suggestion' => (string) ($fallback['suggestion'] ?? ''),
                'type' => $type,
                'provider' => (string) ($fallback['provider'] ?? 'legacy_api'),
                'response' => $fallback,
            );
        }

        $suggestion = '';
        if (isset($result['suggestion']) && is_string($result['suggestion'])) {
            $suggestion = $result['suggestion'];
        }

        $this->track_wallet_usage(
            'aysa_generate_seo_suggestion',
            'generate_seo_suggestion',
            25,
            array(
                'module' => 'on_page_optimization',
                'type' => (string) $type,
                'provider' => (string) ($result['provider'] ?? 'broker'),
                'source' => 'broker',
            ),
            '/v1/content/seo-suggestion'
        );

        return array(
            'suggestion' => $suggestion,
            'type' => $type,
            'provider' => (string) ($result['provider'] ?? 'broker'),
            'response' => $result,
        );
    }

    private function prepare_article_image_request($input) {
        $post_id = intval($input['post_id'] ?? 0);
        if ($post_id <= 0 || !current_user_can('edit_post', $post_id)) {
            return new WP_Error('invalid_post', 'Invalid post or insufficient permissions');
        }

        $article_topic = sanitize_text_field((string) ($input['article_topic'] ?? ''));
        $primary_keyword = sanitize_text_field((string) ($input['primary_keyword'] ?? ''));
        $secondary_keywords_raw = (string) ($input['secondary_keywords'] ?? '');
        $secondary_keywords = array_values(array_filter(array_map('sanitize_text_field', array_map('trim', explode(',', $secondary_keywords_raw)))));
        $language = sanitize_text_field((string) ($input['language'] ?? 'ro'));
        $brand_name = sanitize_text_field((string) get_option('aysa_company_name', get_bloginfo('name')));
        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $project_id = intval(get_option('aysa_project_id', 0));

        if ($project_id <= 0 || $article_topic === '' || $primary_keyword === '') {
            return new WP_Error('missing_required', 'Missing required data for image generation');
        }

        $broker_payload = array(
            'project_id' => $project_id,
            'site_host' => is_string($site_host) ? $site_host : '',
            'article_topic' => $article_topic,
            'primary_keyword' => $primary_keyword,
            'secondary_keywords' => $secondary_keywords,
            'language' => $language !== '' ? $language : 'ro',
            'brand_name' => $brand_name,
            'post_id' => $post_id,
            'image_roles' => array('featured'),
        );

        return array(
            'task' => 'article_image',
            'project_id' => $project_id,
            'post_id' => $post_id,
            'request_hash' => md5(wp_json_encode($broker_payload)),
            'payload' => $broker_payload,
            'language' => $language,
            'secondary_keyword_count' => count($secondary_keywords),
        );
    }

    private function execute_article_image_request($request) {
        $post_id = intval($request['post_id'] ?? 0);
        $language = sanitize_text_field((string) ($request['language'] ?? 'ro'));
        $secondary_keyword_count = intval($request['secondary_keyword_count'] ?? 0);
        $broker_payload = isset($request['payload']) && is_array($request['payload']) ? $request['payload'] : array();

        $broker_result = $this->broker_request('POST', '/v1/content/generate-article-image.php', $broker_payload, 90);
        if (is_wp_error($broker_result)) {
            return $broker_result;
        }

        $image_url = '';
        if (is_array($broker_result)) {
            $image_url = (string) ($broker_result['image_url'] ?? $broker_result['imageUrl'] ?? '');
        }
        if ($image_url === '') {
            return new WP_Error('image_url_missing', 'Image URL missing from broker response');
        }

        $this->track_wallet_usage(
            'aysa_generate_article_image',
            'generate_article_image:' . $post_id,
            40,
            array(
                'module' => 'on_page_optimization',
                'post_id' => intval($post_id),
                'language' => $language !== '' ? $language : 'ro',
                'secondary_keyword_count' => $secondary_keyword_count,
            ),
            '/v1/content/generate-article-image.php'
        );

        return array(
            'image_url' => esc_url_raw($image_url),
            'provider' => (string) ($broker_result['provider'] ?? 'api.aysa.ai'),
        );
    }

    private function prepare_metabox_async_request($task, $input) {
        if ($task === 'seo_suggestion') {
            return $this->prepare_seo_suggestion_request($input);
        }
        if ($task === 'article_image') {
            return $this->prepare_article_image_request($input);
        }
        return new WP_Error('unsupported_task', 'Unsupported metabox async task.');
    }

    private function execute_metabox_async_request($request) {
        $task = sanitize_key((string) ($request['task'] ?? ''));
        if ($task === 'seo_suggestion') {
            return $this->execute_seo_suggestion_request($request);
        }
        if ($task === 'article_image') {
            return $this->execute_article_image_request($request);
        }
        return new WP_Error('unsupported_task', 'Unsupported metabox async task.');
    }

    public function handle_metabox_async_start() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed', 'code' => 'NONCE_FAILED'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Insufficient permissions', 'code' => 'NO_PERMISSION'));
            return;
        }

        $task = sanitize_key((string) wp_unslash($_POST['task'] ?? ''));
        if (!in_array($task, $this->metabox_async_supported_tasks(), true)) {
            wp_send_json_error(array('message' => 'Unsupported metabox async task.', 'code' => 'UNSUPPORTED_TASK'));
            return;
        }

        $request = $this->prepare_metabox_async_request($task, $_POST);
        if (is_wp_error($request)) {
            wp_send_json_error(array('message' => $request->get_error_message(), 'code' => $request->get_error_code()));
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $user_id = get_current_user_id();
        $request_hash = (string) ($request['request_hash'] ?? '');
        $existing = $this->metabox_async_get_status($task, $project_id, $user_id);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            wp_send_json_success($this->metabox_async_output_status($existing));
            return;
        }

        $run_id = uniqid('metabox_', true);
        $status = array(
            'task' => $task,
            'project_id' => $project_id,
            'user_id' => $user_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
            '_request' => $request,
        );
        $this->metabox_async_save_status($task, $project_id, $user_id, $status);
        wp_schedule_single_event(time() + 1, 'aysa_metabox_async_event', array(array(
            'task' => $task,
            'project_id' => $project_id,
            'user_id' => $user_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        )));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }

        wp_send_json_success($this->metabox_async_output_status($status));
    }

    public function handle_metabox_async_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed', 'code' => 'NONCE_FAILED'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Insufficient permissions', 'code' => 'NO_PERMISSION'));
            return;
        }

        $task = sanitize_key((string) wp_unslash($_POST['task'] ?? ''));
        $project_id = $this->get_project_id_for_wallet();
        $user_id = get_current_user_id();
        if ($project_id <= 0 || !in_array($task, $this->metabox_async_supported_tasks(), true)) {
            wp_send_json_error(array('message' => 'Invalid metabox async status request.', 'code' => 'INVALID_STATUS_REQUEST'));
            return;
        }

        $status = $this->metabox_async_get_status($task, $project_id, $user_id);
        if (empty($status)) {
            wp_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reviewed release input boundary: the guarded callback preserves structured input only until its field-specific validation or normalizer, before persistence, output, SQL, or a broker request.
        $requested_run_id = (string) wp_unslash($_POST['run_id'] ?? '');
        if ($requested_run_id !== '' && (string) ($status['run_id'] ?? '') !== $requested_run_id) {
            wp_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }

        $state = (string) ($status['status'] ?? 'idle');
        $updated_at = intval($status['updated_at'] ?? 0);
        if (in_array($state, array('queued', 'running'), true) && ($updated_at <= 0 || (time() - $updated_at) > 20) && !empty($status['_request'])) {
            $this->run_metabox_async_event(array(
                'task' => $task,
                'project_id' => $project_id,
                'user_id' => $user_id,
                'run_id' => (string) ($status['run_id'] ?? ''),
                'request_hash' => (string) ($status['request_hash'] ?? ''),
                'request' => is_array($status['_request']) ? $status['_request'] : array(),
            ));
            $status = $this->metabox_async_get_status($task, $project_id, $user_id);
        }

        wp_send_json_success($this->metabox_async_output_status($status));
    }

    public function run_metabox_async_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $task = sanitize_key((string) ($payload['task'] ?? ''));
        $project_id = $this->resolve_bound_project_id(intval($payload['project_id'] ?? 0));
        $user_id = intval($payload['user_id'] ?? 0);
        $run_id = (string) ($payload['run_id'] ?? '');
        $request_hash = (string) ($payload['request_hash'] ?? '');
        $request = is_array($payload['request'] ?? null) ? $payload['request'] : array();
        if (
            $project_id <= 0
            || intval($request['project_id'] ?? 0) !== $project_id
            || $user_id <= 0
            || $run_id === ''
            || $request_hash === ''
            || !in_array($task, $this->metabox_async_supported_tasks(), true)
        ) {
            return;
        }

        $status = $this->metabox_async_get_status($task, $project_id, $user_id);
        if (
            empty($status) ||
            (string) ($status['run_id'] ?? '') !== $run_id ||
            (string) ($status['request_hash'] ?? '') !== $request_hash
        ) {
            return;
        }

        $status['status'] = 'running';
        $status['updated_at'] = time();
        $status['error'] = '';
        $this->metabox_async_save_status($task, $project_id, $user_id, $status);

        $result = $this->execute_metabox_async_request($request);
        $status['updated_at'] = time();
        $status['finished_at'] = time();
        unset($status['_request']);
        if (is_wp_error($result)) {
            $status['status'] = 'failed';
            $status['result'] = null;
            $status['error'] = $result->get_error_message();
        } else {
            $status['status'] = 'completed';
            $status['result'] = $result;
            $status['error'] = '';
        }
        $this->metabox_async_save_status($task, $project_id, $user_id, $status);
    }

    public function handle_generate_seo_suggestion()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed', 'code' => 'NONCE_FAILED'));
            return;
        }

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Insufficient permissions', 'code' => 'NO_PERMISSION'));
            return;
        }

        $request = $this->prepare_seo_suggestion_request($_POST);
        if (is_wp_error($request)) {
            wp_send_json_error(array('message' => $request->get_error_message(), 'code' => strtoupper((string) $request->get_error_code())));
            return;
        }

        $result = $this->execute_seo_suggestion_request($request);
        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => $result->get_error_message(),
                'code' => 'BROKER_FAILED',
            ));
            return;
        }

        wp_send_json_success($result);
    }

    public function handle_start_keyword_validation()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed', 'code' => 'NONCE_FAILED'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Insufficient permissions', 'code' => 'NO_PERMISSION'));
            return;
        }

        $projectId = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        if ($projectId <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID', 'code' => 'MISSING_PROJECT_ID'));
            return;
        }

        $entityId = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entityType = isset($_POST['entity_type']) ? sanitize_text_field((string) wp_unslash($_POST['entity_type'])) : 'Page';
        $taxonomy = isset($_POST['taxonomy']) ? sanitize_key((string) wp_unslash($_POST['taxonomy'])) : '';
        $language = isset($_POST['language']) ? sanitize_text_field((string) wp_unslash($_POST['language'])) : '';
        $countryCode = isset($_POST['country_code']) ? sanitize_key((string) wp_unslash($_POST['country_code'])) : '';
        $locationCode = isset($_POST['location_code']) ? intval(wp_unslash($_POST['location_code'])) : 0;

        $entityKey = $this->resolve_metabox_entity_key($entityId, $entityType, $taxonomy);
        $adHocPage = $this->build_metabox_keyword_validation_page_payload($_POST);

        if ($entityKey === '' && $adHocPage === array()) {
            wp_send_json_error(array('message' => 'Missing entity context for keyword validation.', 'code' => 'MISSING_CONTEXT'));
            return;
        }

        $selectionMeta = array();
        if ($entityKey !== '') {
            $selectionMeta[] = $this->build_metabox_keyword_validation_selection_meta($entityKey, $entityId, $entityType, $taxonomy, $_POST);
        }

        $payload = array(
            'project_id' => $projectId,
            'run_type' => 'keyword_validate_for_page',
            'website' => home_url('/'),
            'selection' => $entityKey !== '' ? array($entityKey) : array(),
            'selection_meta' => $selectionMeta,
            'run_source' => 'legacy_metabox',
        );

        if ($adHocPage !== array()) {
            $payload['ad_hoc_page'] = $adHocPage;
        }
        if ($language !== '') {
            $payload['language'] = $language;
        }
        if ($countryCode !== '') {
            $payload['country_code'] = $countryCode;
        }
        if ($locationCode > 0) {
            $payload['location_code'] = $locationCode;
        }

        $result = $this->broker_request('POST', '/v1/onpage-runs/start', $payload, 45);
        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => $result->get_error_message(),
                'code' => 'BROKER_FAILED',
            ));
            return;
        }

        if (!$this->response_matches_project_run($result, $projectId)) {
            wp_send_json_error(array('message' => 'Project mismatch in validation response.', 'code' => 'PROJECT_MISMATCH'));
            return;
        }

        if (!empty($result['run']['id'])) {
            $this->track_wallet_usage(
                'onpage_keyword_validate_page',
                'onpage_run:' . intval($result['run']['id']),
                20,
                array(
                    'module' => 'on_page_optimization',
                    'run_type' => 'keyword_validate_for_page',
                    'entity_id' => intval($entityId),
                    'entity_type' => (string) $entityType,
                    'has_ad_hoc_page' => !empty($adHocPage),
                ),
                '/v1/onpage-runs/start'
            );
        }

        wp_send_json_success(array(
            'run_id' => intval($result['run']['id'] ?? 0),
            'status' => $result['run']['status'] ?? 'queued',
            'billing' => $result['run']['estimated_credits_cents'] ?? null,
        ));
    }

    public function handle_keyword_validation_status()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed', 'code' => 'NONCE_FAILED'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Insufficient permissions', 'code' => 'NO_PERMISSION'));
            return;
        }

        $projectId = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        $runId = isset($_POST['run_id']) ? intval(wp_unslash($_POST['run_id'])) : 0;
        if ($projectId <= 0 || $runId <= 0) {
            wp_send_json_error(array('message' => 'Missing run context.', 'code' => 'MISSING_RUN_CONTEXT'));
            return;
        }

        $result = $this->broker_request('POST', '/v1/onpage-runs/status', array(
            'project_id' => $projectId,
            'run_id' => $runId,
            'after_event_id' => isset($_POST['after_event_id']) ? intval(wp_unslash($_POST['after_event_id'])) : 0,
        ), 30);
        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => $result->get_error_message(),
                'code' => 'BROKER_FAILED',
            ));
            return;
        }

        if (!$this->response_matches_project_run($result, $projectId, $runId)) {
            wp_send_json_error(array('message' => 'Project or run mismatch in validation response.', 'code' => 'RUN_SCOPE_MISMATCH'));
            return;
        }

        wp_send_json_success(array(
            'run' => $result['run'] ?? array(),
            'events' => $result['events'] ?? array(),
            'validation' => $this->extract_keyword_validation_status_payload($result),
        ));
    }

    private function build_metabox_keyword_validation_page_payload(array $request): array
    {
        $payload = array(
            'title' => isset($request['title']) ? sanitize_text_field((string) $request['title']) : '',
            'meta_description' => isset($request['meta_description']) ? sanitize_textarea_field((string) $request['meta_description']) : '',
            'page_text' => isset($request['page_text']) ? sanitize_textarea_field((string) $request['page_text']) : '',
            'url' => isset($request['url']) ? esc_url_raw((string) $request['url']) : '',
        );

        return array_filter($payload, static function ($value) {
            return $value !== '';
        });
    }

    private function normalize_metabox_entity_type(string $entityType): string
    {
        $normalized = strtolower(trim($entityType));
        return match ($normalized) {
            'product' => 'product',
            'post', 'article' => 'post',
            'category', 'term' => 'term',
            default => 'page',
        };
    }

    private function resolve_metabox_entity_key(int $entityId, string $entityType, string $taxonomy = ''): string
    {
        if ($entityId <= 0) {
            return '';
        }

        $normalizedType = $this->normalize_metabox_entity_type($entityType);
        if ($normalizedType === 'term') {
            $taxonomy = $taxonomy !== '' ? $taxonomy : 'category';
            return 'term:' . $taxonomy . ':' . $entityId;
        }

        return 'post:' . $entityId;
    }

    private function build_metabox_keyword_validation_selection_meta(string $entityKey, int $entityId, string $entityType, string $taxonomy, array $request): array
    {
        $projectId = isset($request['project_id']) ? intval($request['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($projectId > 0 && class_exists('Aysa_Onpage_Workspace')) {
            $workspace = new Aysa_Onpage_Workspace();
            $resolved = $workspace->build_selection_meta_for_entity($projectId, $entityKey);
            if ($resolved !== array()) {
                return $resolved;
            }
        }

        $normalizedType = $this->normalize_metabox_entity_type($entityType);
        $objectKind = $normalizedType === 'term' ? 'term' : 'post';
        $postType = $normalizedType === 'product' ? 'product' : ($normalizedType === 'post' ? 'post' : ($normalizedType === 'page' ? 'page' : ''));
        $entityGroup = $normalizedType === 'product'
            ? 'product'
            : ($normalizedType === 'post' ? 'article' : ($normalizedType === 'term' && $taxonomy === 'product_cat' ? 'ecommerce_category' : ($normalizedType === 'term' ? 'other' : 'page')));

        return array(
            'entity_key' => $entityKey,
            'entity_id' => $entityId,
            'object_kind' => $objectKind,
            'post_type' => $postType,
            'taxonomy' => $normalizedType === 'term' ? $taxonomy : '',
            'entity_group' => $entityGroup,
            'url' => isset($request['url']) ? esc_url_raw((string) $request['url']) : '',
            'title' => isset($request['title']) ? sanitize_text_field((string) $request['title']) : '',
            'publication_state' => 'published',
            'mapping_state' => !empty($request['existing_keyword']) ? 'mapped' : 'needs_mapping',
            'main_keyword' => isset($request['existing_keyword']) ? sanitize_text_field((string) $request['existing_keyword']) : '',
            'seo_state' => 'not_optimized',
            'optimization_scope' => 'none',
            'meta_state' => 'none',
            'rewrite_state' => 'not_needed',
            'output_state' => 'none',
        );
    }

    private function decode_broker_task_output(array $task): array
    {
        $output = $task['output_json'] ?? null;
        if (is_array($output)) {
            return $output;
        }
        if (is_string($output) && $output !== '') {
            $decoded = json_decode($output, true);
            if (is_array($decoded)) {
                return $decoded;
            }
        }
        return array();
    }

    private function extract_keyword_validation_status_payload(array $result): array
    {
        $payload = array(
            'comparison_result' => '',
            'comparison_reason' => '',
            'decision_reason' => '',
            'existing_keyword' => '',
            'candidate_keyword' => '',
            'selected_keyword' => '',
            'secondary_keywords' => array(),
            'persisted_mapping' => false,
            'persist_reason' => '',
            'confidence_score' => null,
            'status_label' => '',
            'next_step' => '',
        );

        $tasks = isset($result['tasks']) && is_array($result['tasks']) ? $result['tasks'] : array();
        foreach ($tasks as $task) {
            if (!is_array($task)) {
                continue;
            }
            $taskGroup = (string) ($task['task_group'] ?? '');
            $output = $this->decode_broker_task_output($task);
            if ($taskGroup === 'onpage_keyword_rank_and_select') {
                $payload['comparison_result'] = (string) ($output['comparison_result'] ?? $payload['comparison_result']);
                $payload['comparison_reason'] = (string) ($output['comparison_reason'] ?? $payload['comparison_reason']);
                $payload['decision_reason'] = (string) ($output['decision_reason'] ?? $payload['decision_reason']);
                $payload['existing_keyword'] = (string) ($output['existing_keyword'] ?? $payload['existing_keyword']);
                $payload['candidate_keyword'] = (string) ($output['candidate_keyword'] ?? $payload['candidate_keyword']);
                $payload['selected_keyword'] = (string) ($output['selected_keyword'] ?? $payload['selected_keyword']);
                $payload['confidence_score'] = isset($output['confidence_score']) ? floatval($output['confidence_score']) : $payload['confidence_score'];
                if (isset($output['secondary_keywords']) && is_array($output['secondary_keywords'])) {
                    $payload['secondary_keywords'] = array_values(array_filter(array_map(static function ($row): string {
                        if (is_array($row)) {
                            return trim((string) ($row['keyword'] ?? ''));
                        }
                        return trim((string) $row);
                    }, $output['secondary_keywords'])));
                }
            } elseif ($taskGroup === 'onpage_keyword_mapping_persist_if_confident') {
                $payload['persisted_mapping'] = !empty($output['persisted_mapping']);
                $payload['persist_reason'] = (string) ($output['persist_reason'] ?? $payload['persist_reason']);
            } elseif ($taskGroup === 'onpage_projection_sync') {
                $projection = isset($output['projection']) && is_array($output['projection']) ? $output['projection'] : array();
                $rows = isset($projection['inventory_rows']) && is_array($projection['inventory_rows']) ? $projection['inventory_rows'] : array();
                if (!empty($rows[0]) && is_array($rows[0])) {
                    $payload['selected_keyword'] = (string) ($rows[0]['main_keyword'] ?? $payload['selected_keyword']);
                    $payload['decision_reason'] = (string) ($rows[0]['decision_reason'] ?? $payload['decision_reason']);
                    $payload['next_step'] = (string) ($rows[0]['next_step'] ?? $payload['next_step']);
                }
            }
        }

        $payload['status_label'] = match ($payload['comparison_result']) {
            'keep_existing' => 'Existing mapping confirmed',
            'mapping_review_better_candidate', 'mapping_review_conflict' => 'Review keyword upgrade',
            'unmapped' => 'Suggested keyword',
            default => ($payload['selected_keyword'] !== '' ? 'Suggested keyword' : 'Validation in progress'),
        };

        return $payload;
    }

    /**
     * Legacy local API fallback for SEO suggestions (server-side only).
     *
     * @param string $type
     * @param string $keyword
     * @param string $language
     * @param array<string,mixed> $payload
     * @param int $projectId
     * @return array<string,mixed>|WP_Error
     */
    private function legacy_generate_seo_suggestion($type, $keyword, $language, $payload, $projectId)
    {
        $base = defined('AYSA_API_BASE_URL')
            ? rtrim((string) AYSA_API_BASE_URL, '/')
            : (class_exists('Aysa_API_Config') ? rtrim((string) Aysa_API_Config::get_base_url(), '/') : 'https://api.aysa.ai');
        $type = (string) $type;
        $keyword = (string) $keyword;
        $language = (string) $language;
        $payload = is_array($payload) ? $payload : array();

        $request = null;
        $requestMethod = 'POST';

        if ($type === 'second-keyword') {
            $requestMethod = 'GET';
            $request = $base . '/api/SEO/keywords-for-keywords-for-plugin?keyword=' . rawurlencode($keyword)
                . '&locationName=' . rawurlencode('Romania')
                . '&projectId=' . intval($projectId);
        } elseif ($type === 'meta-title') {
            $request = array('url' => $base . '/api/chatgpt/seo-meta-title', 'body' => $payload);
        } elseif ($type === 'meta-keywords') {
            $request = array('url' => $base . '/api/chatgpt/seo-meta-keywords', 'body' => $payload);
        } elseif ($type === 'meta-description') {
            $request = array('url' => $base . '/api/chatgpt/seo-meta-description', 'body' => $payload);
        } elseif ($type === 'seo-optimized-content' || $type === 'product-description') {
            $request = array('url' => $base . '/api/chatgpt/seo-optimized-content', 'body' => $payload);
        } elseif ($type === 'product-short-description') {
            $request = array('url' => $base . '/api/chatgpt/short-optimized-content', 'body' => $payload);
        } elseif ($type === 'product-permalink' || $type === 'category-permalink') {
            $requestMethod = 'GET';
            $pageType = $type === 'category-permalink' ? 'category' : 'product';
            $request = $base . '/api/chatgpt/seo-friendly-url?target_keyword=' . rawurlencode($keyword)
                . '&language=' . rawurlencode($language !== '' ? $language : 'ro')
                . '&page_type=' . rawurlencode($pageType)
                . '&projectId=' . intval($projectId);
        } elseif ($type === 'product-or-category-title') {
            $requestMethod = 'GET';
            $pageType = isset($payload['pageType']) ? sanitize_text_field((string) $payload['pageType']) : 'product';
            $request = $base . '/api/chatgpt/product-or-category-title?target_keyword=' . rawurlencode($keyword)
                . '&language=' . rawurlencode($language !== '' ? $language : 'ro')
                . '&page_type=' . rawurlencode($pageType)
                . '&projectId=' . intval($projectId);
        } else {
            return new WP_Error('legacy_unsupported_type', 'Unsupported suggestion type');
        }

        $resp = null;
        if ($requestMethod === 'GET') {
            $resp = wp_remote_get((string) $request, array(
                'timeout' => 120,
                'headers' => array('Accept' => 'application/json'),
            ));
        } else {
            $url = isset($request['url']) ? (string) $request['url'] : '';
            $body = isset($request['body']) && is_array($request['body']) ? $request['body'] : array();
            $resp = wp_remote_post($url, array(
                'timeout' => 120,
                'headers' => array(
                    'Content-Type' => 'application/json',
                    'Accept' => 'application/json',
                ),
                'body' => wp_json_encode($body),
            ));
        }

        if (is_wp_error($resp)) {
            return $resp;
        }

        $code = (int) wp_remote_retrieve_response_code($resp);
        $rawBody = (string) wp_remote_retrieve_body($resp);
        if ($code < 200 || $code >= 300) {
            return new WP_Error('legacy_http_error', 'Legacy API request failed with HTTP ' . $code);
        }

        $json = json_decode($rawBody, true);
        if (!is_array($json)) {
            if ($type === 'product-permalink' || $type === 'category-permalink' || $type === 'product-or-category-title') {
                return array(
                    'ok' => true,
                    'provider' => 'legacy_api',
                    'suggestion' => trim($rawBody),
                );
            }
            return new WP_Error('legacy_invalid_json', 'Legacy API returned invalid JSON');
        }

        $suggestion = '';
        if ($type === 'second-keyword') {
            $items = array();
            if (isset($json['tasks'][0]['result']) && is_array($json['tasks'][0]['result'])) {
                $items = $json['tasks'][0]['result'];
            } elseif (isset($json['result']) && is_array($json['result'])) {
                $items = $json['result'];
            }
            $keywords = array();
            foreach ($items as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $kw = trim((string) ($row['keyword'] ?? ''));
                if ($kw === '' || strcasecmp($kw, $keyword) === 0) {
                    continue;
                }
                $keywords[strtolower($kw)] = $kw;
            }
            $suggestion = implode(', ', array_values(array_slice($keywords, 0, 20)));
        } elseif ($type === 'meta-title') {
            $suggestion = trim((string) ($json['seoMetaTitle'] ?? ''));
        } elseif ($type === 'meta-keywords') {
            $suggestion = trim((string) ($json['seoMetaKeywords'] ?? ''));
        } elseif ($type === 'meta-description') {
            $suggestion = trim((string) ($json['seoMetaDescription'] ?? ''));
        } elseif ($type === 'seo-optimized-content' || $type === 'product-description') {
            $suggestion = trim((string) ($json['seoOptimizedContent'] ?? $json['content'] ?? ''));
        } elseif ($type === 'product-short-description') {
            $suggestion = trim((string) ($json['shortOptimizedContent'] ?? $json['content'] ?? ''));
        } elseif ($type === 'product-permalink' || $type === 'category-permalink') {
            $suggestion = trim((string) ($json['seoFriendlyUrl'] ?? $json['slug'] ?? ''));
        } elseif ($type === 'product-or-category-title') {
            $suggestion = trim((string) ($json['title'] ?? $json['productTitle'] ?? $json['categoryTitle'] ?? ''));
        }

        return array(
            'ok' => true,
            'provider' => 'legacy_api',
            'suggestion' => $suggestion,
            'response' => $json,
        );
    }

    /**
     * Enhanced AJAX handler for saving SEO data
     */
    public function handle_save_seo_data() {
        // Nonce is mandatory for this endpoint.
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(['message' => 'Security check failed', 'code' => 'NONCE_FAILED']);
            return;
        }
        
        // Get data from request
        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field(wp_unslash($_POST['entity_type'])) : '';
        
        if (!$entity_id || !$entity_type) {
            wp_send_json_error(['message' => 'Missing entity ID or type', 'code' => 'MISSING_DATA']);
            return;
        }
        
        // ✅ NORMALIZE entity type to lowercase for consistency
        $entity_type = strtolower($entity_type);
        
        // Debug (without dumping full payload)
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA AJAX SAVE: Received entity_id=' . $entity_id . ', entity_type=' . $entity_type);
        }
        
        // ✅ IMPROVED: Enhanced data validation and collection
        $keyword_data = array(
            'keyword' => isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '',
            'second_keyword' => isset($_POST['second_keyword']) ? sanitize_text_field(wp_unslash($_POST['second_keyword'])) : '',
            'meta_title' => isset($_POST['meta_title']) ? sanitize_text_field(wp_unslash($_POST['meta_title'])) : '',
            'meta_description' => isset($_POST['meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['meta_description'])) : '',
            'meta_keywords' => isset($_POST['meta_keywords']) ? sanitize_text_field(wp_unslash($_POST['meta_keywords'])) : '',
            'url_recomandat' => isset($_POST['url_recomandat']) ? sanitize_text_field(wp_unslash($_POST['url_recomandat'])) : '',
            'track' => isset($_POST['track']) ? intval(wp_unslash($_POST['track'])) : 0,
            'last_updated' => current_time('mysql'),
            'saved_via' => 'ajax_request',
            'ajax_timestamp' => current_time('timestamp')
        );
        
        // ✅ IMPROVED: Enhanced entity type validation and capabilities check
        $is_valid_entity = false;
        $capability_check = false;
        
        if ($entity_type === 'product') {
            $post = get_post($entity_id);
            $is_valid_entity = ($post && $post->post_type === 'product');
            $capability_check = current_user_can('edit_post', $entity_id);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AJAX SAVE: Product validation - post exists: ' . ($post ? 'yes' : 'no') . ', post_type: ' . ($post ? $post->post_type : 'none') . ', can edit: ' . ($capability_check ? 'yes' : 'no'));
            }
        } elseif ($entity_type === 'page') {
            $post = get_post($entity_id);
            $is_valid_entity = ($post && $post->post_type === 'page');
            $capability_check = current_user_can('edit_post', $entity_id);
        } elseif ($entity_type === 'post') {
            $post = get_post($entity_id);
            $is_valid_entity = ($post && $post->post_type === 'post');
            $capability_check = current_user_can('edit_post', $entity_id);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AJAX SAVE: Post validation - post exists: ' . ($post ? 'yes' : 'no') . ', post_type: ' . ($post ? $post->post_type : 'none') . ', can edit: ' . ($capability_check ? 'yes' : 'no'));
            }
        } elseif ($entity_type === 'term' || $entity_type === 'category') {
            $term = get_term($entity_id);
            $is_valid_entity = (!is_wp_error($term) && $term);
            $capability_check = current_user_can('manage_categories') || 
                              ($term && current_user_can('edit_terms', $term->taxonomy));
        }
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA AJAX SAVE: Final validation - is_valid_entity: ' . ($is_valid_entity ? 'yes' : 'no') . ', capability_check: ' . ($capability_check ? 'yes' : 'no'));
        }
        
        if (!$is_valid_entity) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AJAX SAVE: FAILED - Invalid entity');
            }
            wp_send_json_error([
                'message' => 'Invalid entity or entity not found',
                'code' => 'INVALID_ENTITY',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type
            ]);
            return;
        }
        
        if (!$capability_check) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AJAX SAVE: FAILED - Insufficient permissions');
            }
            wp_send_json_error([
                'message' => 'Insufficient permissions to edit this entity',
                'code' => 'INSUFFICIENT_PERMISSIONS',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type
            ]);
            return;
        }
        
        // Normalize/fallback generated SEO fields so products/pages/categories are not left blank.
        $entity_name = '';
        if ($entity_type === 'product' || $entity_type === 'page' || $entity_type === 'post') {
            $entity_post = get_post($entity_id);
            if ($entity_post && !is_wp_error($entity_post)) {
                $entity_name = sanitize_text_field($entity_post->post_title);
            }
        } elseif ($entity_type === 'term' || $entity_type === 'category') {
            $entity_term = get_term($entity_id);
            if ($entity_term && !is_wp_error($entity_term)) {
                $entity_name = sanitize_text_field($entity_term->name);
            }
        }

        if (empty($keyword_data['keyword']) && !empty($entity_name)) {
            $keyword_data['keyword'] = $entity_name;
        }

        $site_name = get_bloginfo('name');
        if (empty($keyword_data['meta_title']) && !empty($keyword_data['keyword'])) {
            $keyword_data['meta_title'] = $keyword_data['keyword'] . ' | ' . $site_name;
        }
        if (empty($keyword_data['meta_description']) && !empty($keyword_data['keyword'])) {
            $keyword_data['meta_description'] = 'Descopera ' . $keyword_data['keyword'] . ' la ' . $site_name . '. Livrare rapida si oferta actualizata.';
        }
        if (empty($keyword_data['meta_keywords']) && !empty($keyword_data['second_keyword'])) {
            $keyword_data['meta_keywords'] = $keyword_data['second_keyword'];
        }

        // ✅ IMPROVED: Use temporary meta storage for better integration with hooks
        $use_temp_storage = apply_filters('aysa_use_temp_storage_ajax', true, $entity_id, $entity_type);
        $force_immediate_save = apply_filters('aysa_force_immediate_ajax_save', false, $entity_id, $entity_type);

        // Persist immediately for existing entities; metabox UI must reflect saved values right away.
        if (in_array($entity_type, array('product', 'page', 'post', 'term', 'category'), true)) {
            $force_immediate_save = true;
        }
        
        $result = false;
        $save_method = '';
        
        if ($use_temp_storage && !$force_immediate_save) {
            // ✅ IMPROVED: Store in temporary meta for hook integration
            if ($entity_type === 'product' || $entity_type === 'page') {
                $result = update_post_meta($entity_id, '_aysa_temp_save_data', $keyword_data);
                $save_method = 'temp_post_meta';
            } elseif ($entity_type === 'term' || $entity_type === 'category' || $entity_type === 'Category') {
                $result = update_term_meta($entity_id, '_aysa_temp_save_data', $keyword_data);
                $save_method = 'temp_term_meta';
            }
            
            // Set a flag to trigger save on next WordPress save action
            if ($result) {
                if ($entity_type === 'product' || $entity_type === 'page' || $entity_type === 'post') {
                    update_post_meta($entity_id, '_aysa_has_pending_save', true);
                } else {
                    update_term_meta($entity_id, '_aysa_has_pending_save', true);
                }
            }
            
        } else {
            // ✅ IMPROVED: Direct save for immediate requirement
            if ($entity_type === 'product' || $entity_type === 'page' || $entity_type === 'post') {
                $existing_data = get_post_meta($entity_id, 'aysa_keyword_data', true);
                if (!is_array($existing_data)) {
                    $existing_data = array();
                }
                
                // ✅ NEW: Include new field formats for posts
                if ($entity_type === 'post') {
                    if (isset($_POST['aysa_keyword'])) {
                        $keyword_data['aysa_keyword'] = sanitize_text_field(wp_unslash($_POST['aysa_keyword']));
                    }
                    if (isset($_POST['aysa_secondary_keywords'])) {
                        $keyword_data['aysa_secondary_keywords'] = sanitize_text_field(wp_unslash($_POST['aysa_secondary_keywords']));
                    }
                }
                
                $merged_data = array_merge($existing_data, array_filter($keyword_data, function($value) {
                    return $value !== '' && $value !== null;
                }));
                
                $result = update_post_meta($entity_id, 'aysa_keyword_data', $merged_data);
                $save_method = 'direct_post_meta';
                
                // Trigger save hook manually
                if ($result) {
                    if (!empty($merged_data['meta_title'])) {
                        update_post_meta($entity_id, 'aysa_meta_title', $merged_data['meta_title']);
                        update_post_meta($entity_id, '_aysa_seo_title', $merged_data['meta_title']);
                    }
                    if (!empty($merged_data['meta_description'])) {
                        update_post_meta($entity_id, 'aysa_meta_description', $merged_data['meta_description']);
                        update_post_meta($entity_id, '_aysa_seo_description', $merged_data['meta_description']);
                    }
                    if (!empty($merged_data['meta_keywords'])) {
                        update_post_meta($entity_id, 'aysa_meta_keywords', $merged_data['meta_keywords']);
                        update_post_meta($entity_id, '_aysa_seo_keywords', $merged_data['meta_keywords']);
                    }
                    do_action('aysa_ajax_data_saved', $entity_id, $entity_type, $merged_data);
                }
                
            } elseif ($entity_type === 'term' || $entity_type === 'category' || $entity_type === 'Category') {
                $existing_data = get_term_meta($entity_id, 'aysa_keyword_data', true);
                if (!is_array($existing_data)) {
                    $existing_data = array();
                }
                
                $merged_data = array_merge($existing_data, array_filter($keyword_data, function($value) {
                    return $value !== '' && $value !== null;
                }));
                
                $result = update_term_meta($entity_id, 'aysa_keyword_data', $merged_data);
                $save_method = 'direct_term_meta';
                
                // Trigger save hook manually
                if ($result) {
                    if (!empty($merged_data['meta_title'])) {
                        update_term_meta($entity_id, 'aysa_meta_title', $merged_data['meta_title']);
                        update_term_meta($entity_id, '_aysa_seo_title', $merged_data['meta_title']);
                    }
                    if (!empty($merged_data['meta_description'])) {
                        update_term_meta($entity_id, 'aysa_meta_description', $merged_data['meta_description']);
                        update_term_meta($entity_id, '_aysa_seo_description', $merged_data['meta_description']);
                    }
                    if (!empty($merged_data['meta_keywords'])) {
                        update_term_meta($entity_id, 'aysa_meta_keywords', $merged_data['meta_keywords']);
                        update_term_meta($entity_id, '_aysa_seo_keywords', $merged_data['meta_keywords']);
                    }
                    do_action('aysa_ajax_data_saved', $entity_id, $entity_type, $merged_data);
                }
            }
        }
        
        // ✅ IMPROVED: Enhanced response with detailed information
        if ($result) {
            $response_data = [
                'message' => 'SEO data saved successfully',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'save_method' => $save_method,
                'use_temp_storage' => $use_temp_storage,
                'timestamp' => current_time('mysql'),
                'data_summary' => [
                    'has_keyword' => !empty($keyword_data['keyword']),
                    'has_secondary' => !empty($keyword_data['second_keyword']),
                    'has_meta_title' => !empty($keyword_data['meta_title']),
                    'has_meta_description' => !empty($keyword_data['meta_description']),
                    'has_meta_keywords' => !empty($keyword_data['meta_keywords'])
                ]
            ];
            
            // Add hooks for other systems
            do_action('aysa_ajax_save_success', $entity_id, $entity_type, $keyword_data, $save_method);
            $this->log_onpage_output_to_history($entity_id, $entity_type, $keyword_data);
            $this->sync_onpage_keyword_to_local_associations($entity_id, $entity_type, $keyword_data);
            $this->sync_onpage_workspace_inventory_keyword($entity_id, $entity_type, $keyword_data);
            $this->track_wallet_usage(
                'aysa_save_seo_data',
                'save_seo_data',
                5,
                array(
                    'module' => 'on_page_optimization',
                    'entity_id' => intval($entity_id),
                    'entity_type' => (string) $entity_type,
                    'save_method' => (string) $save_method,
                ),
                '/v1/onpage/save-seo-data'
            );
            
            wp_send_json_success($response_data);
            
        } else {
            $error_data = [
                'message' => 'Failed to save SEO data',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'save_method' => $save_method,
                'code' => 'SAVE_FAILED'
            ];
            
            // Add hooks for error handling
            do_action('aysa_ajax_save_error', $entity_id, $entity_type, $keyword_data, $save_method);
            
            wp_send_json_error($error_data);
        }
    }

    private function log_onpage_output_to_history(int $entity_id, string $entity_type, array $keyword_data): void
    {
        if ($entity_id <= 0 || !function_exists('aysa_get_seo_history')) {
            return;
        }
        $history = aysa_get_seo_history();
        if (!$history || !method_exists($history, 'log_seo_generation')) {
            return;
        }

        $entity_type = strtolower($entity_type);
        $history_entity_type = $entity_type;
        $entity_name = '';

        if (in_array($entity_type, array('product', 'page', 'post'), true)) {
            $post = get_post($entity_id);
            if ($post instanceof WP_Post) {
                $entity_name = (string) $post->post_title;
                $history_entity_type = (string) $post->post_type;
            }
        } else {
            $term = get_term($entity_id);
            if ($term && !is_wp_error($term)) {
                $entity_name = (string) $term->name;
                $history_entity_type = (string) $term->taxonomy;
            }
        }

        $history->log_seo_generation(array(
            'entity_id' => $entity_id,
            'entity_type' => $history_entity_type,
            'entity_name' => $entity_name,
            'keyword' => sanitize_text_field((string) ($keyword_data['keyword'] ?? $entity_name)),
            'language' => sanitize_text_field((string) get_locale()),
            'website_name' => sanitize_text_field((string) get_bloginfo('name')),
            'meta_title' => $keyword_data['meta_title'] ?? null,
            'meta_description' => $keyword_data['meta_description'] ?? null,
            'meta_keywords' => $keyword_data['meta_keywords'] ?? null,
            'content_title' => $keyword_data['content_title'] ?? null,
            'content_description' => $keyword_data['content_description'] ?? null,
            'short_description' => $keyword_data['short_description'] ?? null,
            'generator_type' => 'metabox',
            'generation_method' => 'local_apply',
            'status' => 'applied',
            'is_applied' => 1,
            'confidence_score' => 1,
            'generation_context' => array(
                'source' => 'metabox',
                'save_method' => 'handle_save_seo_data',
            ),
        ));
    }

    private function sync_onpage_keyword_to_local_associations(int $entity_id, string $entity_type, array $keyword_data): void
    {
        $entity_id = intval($entity_id);
        $entity_type = strtolower(trim($entity_type));
        $keyword = trim((string) ($keyword_data['keyword'] ?? ''));
        if ($entity_id <= 0 || $keyword === '') {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        if ($project_id <= 0) {
            return;
        }

        $association = $this->build_local_keyword_association_payload($project_id, $entity_id, $entity_type, $keyword);
        if (!is_array($association) || empty($association['associatedOn']) || empty($association['value'])) {
            return;
        }

        $option_key = 'aysa_keyword_discovery_local_associations_' . $project_id;
        $rows = get_option($option_key, array());
        if (!is_array($rows)) {
            $rows = array();
        }

        $keyword_norm = strtolower($keyword);
        $associated_norm = strtolower((string) $association['associatedOn']);
        $new_rows = array();
        $replaced = false;

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $row_assoc = strtolower((string) ($row['associatedOn'] ?? $row['associated_on'] ?? ''));
            $row_id = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
            $row_keyword = strtolower(trim((string) ($row['keyword'] ?? '')));

            if ($row_assoc === $associated_norm && $row_id === $entity_id && $row_keyword === $keyword_norm) {
                if (!$replaced) {
                    $new_rows[] = array_merge($row, $association);
                    $replaced = true;
                }
                continue;
            }

            $new_rows[] = $row;
        }

        if (!$replaced) {
            array_unshift($new_rows, $association);
        }

        update_option($option_key, array_values($new_rows), false);
        $this->sync_onpage_keyword_to_discovery_table($project_id, $association);
    }

    private function build_local_keyword_association_payload(int $project_id, int $entity_id, string $entity_type, string $keyword): array
    {
        $associated_on = '';
        $value = '';
        $target_url = '';

        if (in_array($entity_type, array('product', 'page', 'post', 'article'), true)) {
            $post = get_post($entity_id);
            if (!$post instanceof WP_Post) {
                return array();
            }
            $associated_on = (string) $post->post_type;
            $value = (string) $post->post_title;
            $target_url = (string) get_permalink($post);
        } elseif (in_array($entity_type, array('term', 'category'), true)) {
            $term = get_term($entity_id);
            if (!$term || is_wp_error($term)) {
                return array();
            }
            $associated_on = (string) $term->taxonomy;
            $value = (string) $term->name;
            $term_link = get_term_link($term);
            $target_url = is_string($term_link) ? $term_link : '';
        } else {
            return array();
        }

        if ($associated_on === '' || $value === '') {
            return array();
        }

        return array(
            'id' => $this->build_keyword_discovery_stable_local_id($keyword, $associated_on, $entity_id),
            'projectId' => $project_id,
            'keyword' => $keyword,
            'associatedOn' => $associated_on,
            'idIntern' => $entity_id,
            'value' => $value,
            'desiredPosition' => null,
            'source' => 'metabox_local_apply',
            'updatedAt' => current_time('mysql'),
            'link' => $target_url,
            'target_url' => $target_url,
            'isMain' => 1,
        );
    }

    private function sync_onpage_keyword_to_discovery_table(int $project_id, array $association): void
    {
        global $wpdb;

        $table = esc_sql($wpdb->prefix . 'aysa_keyword_discovery');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            return;
        }

        $keyword = trim((string) ($association['keyword'] ?? ''));
        $target_url = esc_url_raw((string) ($association['target_url'] ?? $association['link'] ?? ''));
        if ($keyword === '' || $target_url === '') {
            return;
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $existing_id = intval($wpdb->get_var($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "SELECT id FROM {$table} WHERE project_id = %d AND keyword = %s LIMIT 1",
            $project_id,
            $keyword
        )));

        $data = array(
            'project_id' => $project_id,
            'keyword' => $keyword,
            'target_url' => $target_url,
            'source' => 'metabox_local_apply',
            'updated_at' => current_time('mysql'),
        );

        if ($existing_id > 0) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
            $wpdb->update($table, $data, array('id' => $existing_id));
            return;
        }

        $data['created_at'] = current_time('mysql');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $wpdb->insert($table, $data);
    }

    private function sync_onpage_workspace_inventory_keyword(int $entity_id, string $entity_type, array $keyword_data): void
    {
        global $wpdb;

        $entity_id = intval($entity_id);
        $entity_type = strtolower(trim($entity_type));
        $keyword = trim((string) ($keyword_data['keyword'] ?? ''));
        if ($entity_id <= 0 || $keyword === '') {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        if ($project_id <= 0) {
            return;
        }

        $inventory_table = esc_sql($wpdb->prefix . 'aysa_onpage_inventory');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $inventory_table));
        if ($exists !== $inventory_table) {
            return;
        }

        $entity_key = '';
        if (in_array($entity_type, array('product', 'page', 'post', 'article'), true)) {
            $entity_key = 'post:' . $entity_id;
        } elseif (in_array($entity_type, array('term', 'category'), true)) {
            $term = get_term($entity_id);
            if ($term && !is_wp_error($term) && !empty($term->taxonomy)) {
                $entity_key = 'term:' . $term->taxonomy . ':' . $entity_id;
            }
        }

        if ($entity_key === '') {
            return;
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $current = $wpdb->get_row(
            $wpdb->prepare(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
                "SELECT seo_state, rewrite_state, output_state, next_step FROM {$inventory_table} WHERE project_id = %d AND entity_key = %s LIMIT 1",
                $project_id,
                $entity_key
            ),
            ARRAY_A
        );
        if (!is_array($current)) {
            return;
        }

        $seo_state = sanitize_key((string) ($current['seo_state'] ?? 'not_optimized'));
        $rewrite_state = sanitize_key((string) ($current['rewrite_state'] ?? 'not_needed'));
        $output_state = sanitize_key((string) ($current['output_state'] ?? 'none'));

        $next_step = 'No action needed';
        if ($rewrite_state === 'recommended') {
            $next_step = 'Review content rewrite';
        } elseif ($rewrite_state === 'review_required') {
            $next_step = 'Review product rewrite';
        } elseif ($seo_state !== 'optimized' || $output_state === 'none') {
            $next_step = 'Generate meta package';
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $wpdb->update(
            $inventory_table,
            array(
                'main_keyword' => $keyword,
                'mapping_state' => 'mapped',
                'next_step' => $next_step,
                'last_evaluated_at' => current_time('mysql'),
            ),
            array(
                'project_id' => $project_id,
                'entity_key' => $entity_key,
            )
        );
    }

    private function build_keyword_discovery_stable_local_id(string $keyword, string $associated_on, int $id_intern): int
    {
        $base = strtolower(trim($keyword)) . '|' . strtolower(trim($associated_on)) . '|' . intval($id_intern);
        return abs(crc32($base));
    }

    /**
     * Add metabox for WooCommerce products
     */
    public function add_product_metabox() 
    {
        add_meta_box(
            'aysa_product_seo',
            'AYSA SEO',
            [$this, 'render_product_metabox'],
            'product',
            'normal',
            'high'
        );
        
        // Add a filter to control the metabox order
        add_filter('get_user_option_meta-box-order_product', [$this, 'set_product_metabox_order']);
    }
    
    /**
     * Set the metabox order to ensure AYSA SEO appears right after Publish
     */
    public function set_product_metabox_order($order)
    {
        // Force AYSA box in the normal column and remove it from side.
        if (!is_array($order)) {
            $order = array(
                'normal' => 'aysa_product_seo',
                'side' => 'submitdiv'
            );
        } else {
            $normal_order = isset($order['normal']) ? explode(',', (string) $order['normal']) : array();
            $side_order = isset($order['side']) ? explode(',', (string) $order['side']) : array();

            // Remove AYSA from side if present.
            $side_order = array_values(array_filter($side_order, function($id) {
                return $id !== 'aysa_product_seo' && $id !== '';
            }));
            if (empty($side_order)) {
                $side_order = array('submitdiv');
            }

            // Ensure AYSA appears in normal.
            if (!in_array('aysa_product_seo', $normal_order, true)) {
                array_unshift($normal_order, 'aysa_product_seo');
            }
            $normal_order = array_values(array_filter($normal_order, function($id) {
                return $id !== '';
            }));

            $order['normal'] = implode(',', $normal_order);
            $order['side'] = implode(',', $side_order);
        }
        
        return $order;
    }

    /**
     * Render the metabox for WooCommerce products
     */
    public function render_product_metabox($post) 
    {
        // Get stored keyword data
        $keyword_data = get_post_meta($post->ID, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }
        
        // Get project ID from options
        $project_id = get_option('aysa_project_id', 0);
        
        // Enqueue and localize script with safe front-end post URL
        wp_enqueue_script('aysa_product_metabox_admin-js');
        $post_link = get_permalink($post->ID);
        if ( is_wp_error( $post_link ) ) {
            $post_link = '';
        }
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'postId'     => $post->ID,
                'entityType' => 'product',
                'entityUrl'  => $post_link,
                'project_id' => $project_id,
                'nonce'      => wp_create_nonce('aysa_metabox_nonce'),
                'ajax_url'   => admin_url('admin-ajax.php'),
                'api_base_url' => AYSA_API_BASE_URL
            )
        );
        
        // Include the metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-category-metabox.php';
        
        // Add nonce fields for security
        wp_nonce_field('aysa_product_save', 'aysa_product_nonce');
        wp_nonce_field('aysa_metabox_nonce', 'aysa_metabox_nonce'); // ✅ Add AJAX nonce as hidden field
        
        // Add JavaScript to handle data population
        ?>
        <script>
        document.addEventListener('DOMContentLoaded', function() {
            console.log('Product metabox loaded - DOM Content Loaded');
            
            // Verifică dacă funcția loadDataFromAPI există
            if (typeof window.loadDataFromAPI === 'function') {
                console.log('loadDataFromAPI function found, calling it');
                try {
                    // Load data from API
                    window.loadDataFromAPI();
                } catch (error) {
                    console.error('Error calling loadDataFromAPI:', error);
                }
            } else {
                console.info('AYSA metabox loader not available in this editor context; skipping remote preload.');
            }
            
            // Check for stored keyword data as fallback
            const keywordData = <?php echo json_encode($keyword_data); ?>;
            if (keywordData && Object.keys(keywordData).length > 0) {
                console.log('Using stored keyword data as fallback');
                try {
                    // Actualizare manuală a câmpurilor
                    var mainKeywordInput = document.getElementById('aysa-entity-keyword');
                    var secondKeywordInput = document.getElementById('aysa-entity-second-keyword');
                    var metaTitleInput = document.getElementById('aysa-entity-meta-title');
                    var metaDescriptionInput = document.getElementById('aysa-entity-meta-description');
                    var metaKeywordsInput = document.getElementById('aysa-entity-meta-keywords');
                    
                    if (mainKeywordInput && keywordData.keyword) {
                        mainKeywordInput.value = keywordData.keyword;
                    }
                    
                    if (secondKeywordInput && keywordData.second_keyword) {
                        secondKeywordInput.value = keywordData.second_keyword;
                    }
                    
                    if (metaTitleInput && keywordData.meta_title) {
                        metaTitleInput.value = keywordData.meta_title;
                    }
                    
                    if (metaDescriptionInput && keywordData.meta_description) {
                        metaDescriptionInput.value = keywordData.meta_description;
                    }
                    
                    if (metaKeywordsInput && keywordData.meta_keywords) {
                        metaKeywordsInput.value = keywordData.meta_keywords;
                    }
                } catch (error) {
                    console.error('Error updating fields with stored data:', error);
                }
            } else {
                console.log('No stored keyword data available');
            }
        });
        </script>
        <?php
    }

    /**
     * Save WooCommerce product SEO data
     */
    public function save_product_seo_data($post_id, $post) 
    {
        if ($this->is_restoring_jsonld) {
            return;
        }

        // ✅ IMPROVED: Enhanced save mechanism for better compatibility
        
        // Basic validation first
        if (!$post_id || !$post || $post->post_type !== 'product') {
            return;
        }
        
        // Don't save on autosave, revisions or trashed posts
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }
        
        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
            return;
        }
        
        if (get_post_status($post_id) === 'trash') {
            return;
        }
        
        // Check user capabilities
        if (!current_user_can('edit_post', $post_id)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: User does not have permission to edit post');
            }
            return;
        }
        
        // ✅ IMPROVED: Enhanced nonce handling and data collection
        $has_aysa_data = false;
        $aysa_data_sources = array();
        
        // Check for AYSA nonce and data in POST
        if (isset($_POST['aysa_product_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aysa_product_nonce'])), 'aysa_product_save')) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'form_post';
        }
        
        // Check for AYSA data in meta (from AJAX saves)
        $temp_meta = get_post_meta($post_id, '_aysa_temp_save_data', true);
        if (!empty($temp_meta) && is_array($temp_meta)) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'temp_meta';
        }
        
        // Check for WordPress save preference
        $use_wordpress_save = apply_filters('aysa_use_wordpress_save', true);
        $force_save = apply_filters('aysa_force_save_product', false, $post_id, $post);
        
        // ✅ IMPROVED: Enhanced logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA SEO: Product save triggered - Post ID: ' . $post_id);
            aysa_debug_log('AYSA SEO: Has AYSA data: ' . ($has_aysa_data ? 'yes' : 'no'));
            aysa_debug_log('AYSA SEO: Data sources: ' . implode(', ', $aysa_data_sources));
            aysa_debug_log('AYSA SEO: use_wordpress_save: ' . ($use_wordpress_save ? 'true' : 'false'));
            aysa_debug_log('AYSA SEO: force_save: ' . ($force_save ? 'true' : 'false'));
        }
        
        // If we don't have AYSA data and aren't forcing save, skip
        if (!$has_aysa_data && !$force_save) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: No AYSA data found, skipping save');
            }
            return;
        }
        
        // If WordPress save is disabled and we're not forcing, skip
        if (!$use_wordpress_save && !$force_save) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: WordPress save disabled, data will be saved via API');
            }
            return;
        }
        
        // ✅ IMPROVED: Enhanced data collection and merging
        $existing_keyword_data = get_post_meta($post_id, 'aysa_keyword_data', true);
        if (!is_array($existing_keyword_data)) {
            $existing_keyword_data = array();
        }
        
        // Collect data from multiple sources
        $new_data = array();
        
        // From POST data
        if (in_array('form_post', $aysa_data_sources)) {
            $new_data = array_merge($new_data, array(
                'keyword' => isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '',
                'second_keyword' => isset($_POST['second_keyword']) ? sanitize_text_field(wp_unslash($_POST['second_keyword'])) : '',
                'track' => isset($_POST['track']) ? 1 : 0,
                'meta_title' => isset($_POST['aysa_meta_title']) ? sanitize_text_field(wp_unslash($_POST['aysa_meta_title'])) : '',
                'meta_description' => isset($_POST['aysa_meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['aysa_meta_description'])) : '',
                'meta_keywords' => isset($_POST['aysa_meta_keywords']) ? sanitize_text_field(wp_unslash($_POST['aysa_meta_keywords'])) : '',
            ));
        }
        
        // From temporary meta (AJAX saves)
        if (in_array('temp_meta', $aysa_data_sources)) {
            $temp_data = array(
                'keyword' => isset($temp_meta['keyword']) ? sanitize_text_field($temp_meta['keyword']) : '',
                'second_keyword' => isset($temp_meta['second_keyword']) ? sanitize_text_field($temp_meta['second_keyword']) : '',
                'track' => isset($temp_meta['track']) ? intval($temp_meta['track']) : 0,
                'meta_title' => isset($temp_meta['meta_title']) ? sanitize_text_field($temp_meta['meta_title']) : '',
                'meta_description' => isset($temp_meta['meta_description']) ? sanitize_textarea_field($temp_meta['meta_description']) : '',
                'meta_keywords' => isset($temp_meta['meta_keywords']) ? sanitize_text_field($temp_meta['meta_keywords']) : '',
            );
            
            // Merge temp data, giving priority to non-empty values
            foreach ($temp_data as $key => $value) {
                if (!empty($value) || !isset($new_data[$key]) || empty($new_data[$key])) {
                    $new_data[$key] = $value;
                }
            }
            
            // Clean up temp meta
            delete_post_meta($post_id, '_aysa_temp_save_data');
        }
        
        // ✅ IMPROVED: Merge with existing data, preserving important values
        $keyword_data = array_merge($existing_keyword_data, array_filter($new_data, function($value) {
            return $value !== '' && $value !== null;
        }));
        
        // Add metadata
        $keyword_data['last_updated'] = current_time('mysql');
        $keyword_data['saved_via'] = 'wordpress_hook';
        $keyword_data['data_sources'] = $aysa_data_sources;
        
        // ✅ IMPROVED: Enhanced save with backup
        $save_result = update_post_meta($post_id, 'aysa_keyword_data', $keyword_data);
        
        if ($save_result) {
            // Create backup of successful save
            update_post_meta($post_id, '_aysa_keyword_data_backup', array(
                'data' => $keyword_data,
                'timestamp' => current_time('timestamp'),
                'post_status' => get_post_status($post_id)
            ));
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Successfully saved product keyword data: ' . aysa_debug_export($keyword_data, true));
            }
            
            // Hook for other plugins/systems
            do_action('aysa_product_seo_data_saved', $post_id, $keyword_data, $post);
            
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Failed to save product keyword data for post_id: ' . $post_id);
            }
        }
        
        // Save advanced settings if present
        $this->save_advanced_product_settings($post_id);
        
        // ✅ NEW: Handle redirect API call if permalink changed
        $this->handle_product_redirect_if_needed($post_id, $post);
        
        // ✅ IMPROVED: Trigger WordPress actions to ensure compatibility
        do_action('aysa_after_product_save', $post_id, $post, $keyword_data);

        // Keep JSON-LD blocks generated by AYSA even if editor stripped them on submit.
        $this->restore_jsonld_scripts_from_request($post_id, 'product');
    }
    
    /**
     * Save advanced WooCommerce product settings
     */
    private function save_advanced_product_settings($post_id) 
    {
        // Save robots meta settings
        $robots_meta_fields = [
            'robots_index',
            'robots_noindex',
            'robots_nofollow',
            'robots_noarchive',
            'robots_noimageindex',
            'robots_nosnippet'
        ];
        
        foreach ($robots_meta_fields as $field) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                update_post_meta($post_id, 'aysa_' . $field, 1);
            } else {
                delete_post_meta($post_id, 'aysa_' . $field);
            }
        }
        
        // Save other advanced settings
        $advanced_fields = [
            'max_snippet',
            'max_video_preview',
            'max_image_preview',
            'canonical_url',
            'targeted_se',
            'targeted_country',
            'targeted_region',
            'targeted_city',
            'update_frequency',
            'language_optimize',
            'device_optimize'
        ];
        
        foreach ($advanced_fields as $field) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                update_post_meta($post_id, 'aysa_' . $field, sanitize_text_field(wp_unslash($_POST[$field])));
            }
        }
    }

    /**
     * Add metabox for WordPress pages
     */
    public function add_page_metabox() 
    {
        add_meta_box(
            'aysa_page_seo',
            'AYSA SEO',
            [$this, 'render_page_metabox'],
            'page',
            'normal',
            'high'
        );

        // Force page metabox in center column (normal), never in sidebar.
        add_filter('get_user_option_meta-box-order_page', [$this, 'set_page_metabox_order']);
    }

    /**
     * Keep page metabox in normal column and remove accidental side placement.
     */
    public function set_page_metabox_order($order)
    {
        if (!is_array($order)) {
            $order = array(
                'normal' => 'aysa_page_seo',
                'side' => 'submitdiv',
            );
        } else {
            $normal_order = isset($order['normal']) ? explode(',', (string) $order['normal']) : array();
            $side_order = isset($order['side']) ? explode(',', (string) $order['side']) : array();

            $side_order = array_values(array_filter($side_order, function ($id) {
                return $id !== 'aysa_page_seo' && $id !== '';
            }));
            if (empty($side_order)) {
                $side_order = array('submitdiv');
            }

            if (!in_array('aysa_page_seo', $normal_order, true)) {
                array_unshift($normal_order, 'aysa_page_seo');
            }
            $normal_order = array_values(array_filter($normal_order, function ($id) {
                return $id !== '';
            }));

            $order['normal'] = implode(',', $normal_order);
            $order['side'] = implode(',', $side_order);
        }

        return $order;
    }

    /**
     * Render the metabox for WordPress pages
     */
    public function render_page_metabox($post) 
    {
        // Get stored keyword data
        $keyword_data = get_post_meta($post->ID, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }
        
        // Get project ID from options
        $project_id = get_option('aysa_project_id', 0);
        
        // Enqueue and localize script with safe front-end page URL
        wp_enqueue_script('aysa_product_metabox_admin-js');
        $page_link = get_permalink($post->ID);
        if ( is_wp_error( $page_link ) ) {
            $page_link = '';
        }
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'postId'     => $post->ID,
                'entityType' => 'Page',
                'entityUrl'  => $page_link,
                'project_id' => $project_id,
                'nonce'      => wp_create_nonce('aysa_metabox_nonce'),
                'ajax_url'   => admin_url('admin-ajax.php'),
                'api_base_url' => AYSA_API_BASE_URL
            )
        );
        
        // ✅ CRITICAL: Also enqueue the modern metabox script for pages to get conflict detection
        wp_enqueue_script(
            'aysa-metabox-modern-js',
            AYSA_PLUGIN_URL . 'admin/js/aysa-metabox.js',
            ['jquery', 'aysa_product_metabox_admin-js'],
            AYSA_VERSION . '.' . time(),
            true
        );
        
        // Include the metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-category-metabox.php';
        
        // Add nonce fields for security
        wp_nonce_field('aysa_page_save', 'aysa_page_nonce');
        wp_nonce_field('aysa_metabox_nonce', 'aysa_metabox_nonce'); // ✅ Add AJAX nonce as hidden field
        
        // Add JavaScript to handle data population
        ?>
        <script>
        document.addEventListener('DOMContentLoaded', function() {
            console.log('Page metabox loaded - DOM Content Loaded');
            
            // Verifică dacă funcția loadDataFromAPI există
            if (typeof window.loadDataFromAPI === 'function') {
                console.log('loadDataFromAPI function found, calling it');
                try {
                    // Load data from API
                    window.loadDataFromAPI();
                } catch (error) {
                    console.error('Error calling loadDataFromAPI:', error);
                }
            } else {
                console.info('AYSA metabox loader not available in this editor context; skipping remote preload.');
            }
            
            // Check for stored keyword data as fallback
            const keywordData = <?php echo json_encode($keyword_data); ?>;
            if (keywordData && Object.keys(keywordData).length > 0) {
                console.log('Using stored keyword data as fallback');
                try {
                    // Actualizare manuală a câmpurilor
                    var mainKeywordInput = document.getElementById('aysa-entity-keyword');
                    var secondKeywordInput = document.getElementById('aysa-entity-second-keyword');
                    var metaTitleInput = document.getElementById('aysa-entity-meta-title');
                    var metaDescriptionInput = document.getElementById('aysa-entity-meta-description');
                    var metaKeywordsInput = document.getElementById('aysa-entity-meta-keywords');
                    
                    if (mainKeywordInput && keywordData.keyword) {
                        mainKeywordInput.value = keywordData.keyword;
                    }
                    
                    if (secondKeywordInput && keywordData.second_keyword) {
                        secondKeywordInput.value = keywordData.second_keyword;
                    }
                    
                    if (metaTitleInput && keywordData.meta_title) {
                        metaTitleInput.value = keywordData.meta_title;
                    }
                    
                    if (metaDescriptionInput && keywordData.meta_description) {
                        metaDescriptionInput.value = keywordData.meta_description;
                    }
                    
                    if (metaKeywordsInput && keywordData.meta_keywords) {
                        metaKeywordsInput.value = keywordData.meta_keywords;
                    }
                } catch (error) {
                    console.error('Error updating fields with stored data:', error);
                }
            } else {
                console.log('No stored keyword data available');
            }
        });
        </script>
        <?php
    }

    /**
     * Save WordPress page SEO data
     */
    public function save_page_seo_data($post_id, $post) 
    {
        if ($this->is_restoring_jsonld) {
            return;
        }

        // ✅ IMPROVED: Enhanced save mechanism for better compatibility
        
        // Basic validation first
        if (!$post_id || !$post || $post->post_type !== 'page') {
            return;
        }
        
        // Don't save on autosave, revisions or trashed posts
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }
        
        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
            return;
        }
        
        if (get_post_status($post_id) === 'trash') {
            return;
        }
        
        // Check user capabilities
        if (!current_user_can('edit_post', $post_id)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: User does not have permission to edit post');
            }
            return;
        }
        
        // ✅ IMPROVED: Enhanced nonce handling and data collection
        $has_aysa_data = false;
        $aysa_data_sources = array();
        
        // Check for AYSA nonce and data in POST
        if (isset($_POST['aysa_page_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aysa_page_nonce'])), 'aysa_page_save')) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'form_post';
        }
        
        // Check for AYSA data in meta (from AJAX saves)
        $temp_meta = get_post_meta($post_id, '_aysa_temp_save_data', true);
        if (!empty($temp_meta) && is_array($temp_meta)) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'temp_meta';
        }
        
        // Check for WordPress save preference
        $use_wordpress_save = apply_filters('aysa_use_wordpress_save', true);
        $force_save = apply_filters('aysa_force_save_page', false, $post_id, $post);
        
        // ✅ IMPROVED: Enhanced logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA SEO: Page save triggered - Post ID: ' . $post_id);
            aysa_debug_log('AYSA SEO: Has AYSA data: ' . ($has_aysa_data ? 'yes' : 'no'));
            aysa_debug_log('AYSA SEO: Data sources: ' . implode(', ', $aysa_data_sources));
            aysa_debug_log('AYSA SEO: use_wordpress_save: ' . ($use_wordpress_save ? 'true' : 'false'));
            aysa_debug_log('AYSA SEO: force_save: ' . ($force_save ? 'true' : 'false'));
        }
        
        // If we don't have AYSA data and aren't forcing save, skip
        if (!$has_aysa_data && !$force_save) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: No AYSA data found, skipping save');
            }
            return;
        }
        
        // If WordPress save is disabled and we're not forcing, skip
        if (!$use_wordpress_save && !$force_save) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: WordPress save disabled, data will be saved via API');
            }
            return;
        }
        
        // ✅ IMPROVED: Enhanced data collection and merging
        $existing_keyword_data = get_post_meta($post_id, 'aysa_keyword_data', true);
        if (!is_array($existing_keyword_data)) {
            $existing_keyword_data = array();
        }
        
        // Collect data from multiple sources
        $new_data = array();
        
        // From POST data
        if (in_array('form_post', $aysa_data_sources)) {
            $new_data = array_merge($new_data, array(
                'keyword' => isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '',
                'second_keyword' => isset($_POST['second_keyword']) ? sanitize_text_field(wp_unslash($_POST['second_keyword'])) : '',
                'track' => isset($_POST['track']) ? 1 : 0,
                'meta_title' => isset($_POST['aysa_meta_title']) ? sanitize_text_field(wp_unslash($_POST['aysa_meta_title'])) : '',
                'meta_description' => isset($_POST['aysa_meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['aysa_meta_description'])) : '',
                'meta_keywords' => isset($_POST['aysa_meta_keywords']) ? sanitize_text_field(wp_unslash($_POST['aysa_meta_keywords'])) : '',
            ));
        }
        
        // From temporary meta (AJAX saves)
        if (in_array('temp_meta', $aysa_data_sources)) {
            $temp_data = array(
                'keyword' => isset($temp_meta['keyword']) ? sanitize_text_field($temp_meta['keyword']) : '',
                'second_keyword' => isset($temp_meta['second_keyword']) ? sanitize_text_field($temp_meta['second_keyword']) : '',
                'track' => isset($temp_meta['track']) ? intval($temp_meta['track']) : 0,
                'meta_title' => isset($temp_meta['meta_title']) ? sanitize_text_field($temp_meta['meta_title']) : '',
                'meta_description' => isset($temp_meta['meta_description']) ? sanitize_textarea_field($temp_meta['meta_description']) : '',
                'meta_keywords' => isset($temp_meta['meta_keywords']) ? sanitize_text_field($temp_meta['meta_keywords']) : '',
            );
            
            // Merge temp data, giving priority to non-empty values
            foreach ($temp_data as $key => $value) {
                if (!empty($value) || !isset($new_data[$key]) || empty($new_data[$key])) {
                    $new_data[$key] = $value;
                }
            }
            
            // Clean up temp meta
            delete_post_meta($post_id, '_aysa_temp_save_data');
        }
        
        // ✅ IMPROVED: Merge with existing data, preserving important values
        $keyword_data = array_merge($existing_keyword_data, array_filter($new_data, function($value) {
            return $value !== '' && $value !== null;
        }));
        
        // Add metadata
        $keyword_data['last_updated'] = current_time('mysql');
        $keyword_data['saved_via'] = 'wordpress_hook';
        $keyword_data['data_sources'] = $aysa_data_sources;
        
        // ✅ IMPROVED: Enhanced save with backup
        $save_result = update_post_meta($post_id, 'aysa_keyword_data', $keyword_data);
        
        if ($save_result) {
            // Create backup of successful save
            update_post_meta($post_id, '_aysa_keyword_data_backup', array(
                'data' => $keyword_data,
                'timestamp' => current_time('timestamp'),
                'post_status' => get_post_status($post_id)
            ));
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Successfully saved page keyword data: ' . aysa_debug_export($keyword_data, true));
            }
            
            // Hook for other plugins/systems
            do_action('aysa_page_seo_data_saved', $post_id, $keyword_data, $post);
            
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Failed to save page keyword data for post_id: ' . $post_id);
            }
        }
        
        // Save advanced settings if present
        $this->save_advanced_page_settings($post_id);
        
        // ✅ NEW: Handle redirect API call if permalink changed
        $this->handle_page_redirect_if_needed($post_id, $post);
        
        // ✅ IMPROVED: Trigger WordPress actions to ensure compatibility
        do_action('aysa_after_page_save', $post_id, $post, $keyword_data);

        // Keep JSON-LD blocks generated by AYSA even if editor stripped them on submit.
        $this->restore_jsonld_scripts_from_request($post_id, 'page');
    }
    
    /**
     * Save advanced WordPress page settings
     */
    private function save_advanced_page_settings($post_id) 
    {
        // Save robots meta settings
        $robots_meta_fields = [
            'robots_index',
            'robots_noindex',
            'robots_nofollow',
            'robots_noarchive',
            'robots_noimageindex',
            'robots_nosnippet'
        ];
        
        foreach ($robots_meta_fields as $field) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                update_post_meta($post_id, 'aysa_' . $field, 1);
            } else {
                delete_post_meta($post_id, 'aysa_' . $field);
            }
        }
        
        // Save other advanced settings
        $advanced_fields = [
            'max_snippet',
            'max_video_preview',
            'max_image_preview',
            'canonical_url',
            'targeted_se',
            'targeted_country',
            'targeted_region',
            'targeted_city',
            'update_frequency',
            'language_optimize',
            'device_optimize'
        ];
        
        foreach ($advanced_fields as $field) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                update_post_meta($post_id, 'aysa_' . $field, sanitize_text_field(wp_unslash($_POST[$field])));
            }
        }
    }

    /**
     * Add metabox for WordPress posts
     */
    public function add_post_metabox() 
    {
        add_meta_box(
            'aysa_post_seo',
            'AYSA SEO - Blog Content Generator',
            [$this, 'render_post_metabox'],
            'post',
            'normal',
            'high'
        );

        // Force blog metabox in the center column, not in sidebar.
        add_filter('get_user_option_meta-box-order_post', [$this, 'set_post_metabox_order']);
    }

    /**
     * Keep post metabox in normal column and remove accidental side placement.
     */
    public function set_post_metabox_order($order)
    {
        if (!is_array($order)) {
            $order = array(
                'normal' => 'aysa_post_seo',
                'side' => 'submitdiv',
            );
        } else {
            $normal_order = isset($order['normal']) ? explode(',', (string) $order['normal']) : array();
            $side_order = isset($order['side']) ? explode(',', (string) $order['side']) : array();

            $side_order = array_values(array_filter($side_order, function ($id) {
                return $id !== 'aysa_post_seo' && $id !== '';
            }));
            if (empty($side_order)) {
                $side_order = array('submitdiv');
            }

            if (!in_array('aysa_post_seo', $normal_order, true)) {
                array_unshift($normal_order, 'aysa_post_seo');
            }
            $normal_order = array_values(array_filter($normal_order, function ($id) {
                return $id !== '';
            }));

            $order['normal'] = implode(',', $normal_order);
            $order['side'] = implode(',', $side_order);
        }

        return $order;
    }

    /**
     * Render the metabox for WordPress posts
     */
    public function render_post_metabox($post) 
    {
        // Get stored keyword data
        $keyword_data = get_post_meta($post->ID, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }
        
        // Get project ID from options
        $project_id = get_option('aysa_project_id', 0);
        
        // Enqueue and localize script
        wp_enqueue_script('aysa_product_metabox_admin-js');
        $post_link = get_permalink($post->ID);
        if ( is_wp_error( $post_link ) ) {
            $post_link = '';
        }
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'postId'     => $post->ID,
                'entityType' => 'Post',
                'entityUrl'  => $post_link,
                'project_id' => $project_id,
                'nonce'      => wp_create_nonce('aysa_metabox_nonce'),
                'ajax_url'   => admin_url('admin-ajax.php'),
                'api_base_url' => AYSA_API_BASE_URL
            )
        );
        
        // First ensure the main metabox script is loaded
        $this->load_aysa_scripts_styles();
        
        // Enqueue the blog content generation script
        wp_enqueue_script(
            'aysa-blog-content-js',
            AYSA_PLUGIN_URL . 'admin/js/aysa-blog-content.js',
            ['jquery', 'aysa_product_metabox_admin-js'],
            AYSA_VERSION . '.' . time(),
            true
        );
        
        // Add inline script to ensure our script loads after everything else
        wp_add_inline_script('aysa-blog-content-js', '
            console.log("AYSA Blog Content Script: Inline script loaded");
            console.log("AYSA Blog Content Script: AysaBlogContent available?", typeof AysaBlogContent !== "undefined");
        ');
        
        // Localize the blog content script with AJAX data and WordPress options
        wp_localize_script(
            'aysa-blog-content-js',
            'aysaBlogContentAjax',
            array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('aysa_metabox_nonce'),
                'post_id' => $post->ID,
                // WordPress options data
                'brand_name' => get_option('aysa_brand_name', get_bloginfo('name')),
                'client_info' => trim(get_option('aysa_phone_number', '') . ' ' . get_option('aysa_email_address', get_option('admin_email'))),
                'publication_location' => get_option('aysa_publication_location', get_bloginfo('name')),
                'language' => get_option('aysa_language', 'ro'),
                'city' => get_option('aysa_city', ''),
                'target_audience' => get_option('aysa_target_audience', 'general public'),
                'audience_level' => get_option('aysa_audience_level', ''),
                'tone' => get_option('aysa_tone', 'profesionist'),
                'phone_number' => get_option('aysa_phone_number', ''),
                'email_address' => get_option('aysa_email_address', get_option('admin_email')),
                'project_id' => get_option('aysa_project_id', 0),
                'website_context' => $this->get_website_context(),
                'secondary_keywords' => $this->get_post_secondary_keywords($post->ID),
                'related_keywords' => $this->get_post_secondary_keywords($post->ID)
            )
        );
        
        // Include the blog content metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-blog-content-metabox.php';
        
        // Add nonce fields for security
        wp_nonce_field('aysa_post_save', 'aysa_post_nonce');
        wp_nonce_field('aysa_metabox_nonce', 'aysa_metabox_nonce');
        
        // Add JavaScript to handle data population
        ?>
        <script>
        document.addEventListener('DOMContentLoaded', function() {
            console.log('Blog metabox loaded - DOM Content Loaded');
            
            // Verifică dacă funcția loadDataFromAPI există
            if (typeof window.loadDataFromAPI === 'function') {
                console.log('loadDataFromAPI function found, calling it');
                try {
                    // Load data from API
                    window.loadDataFromAPI();
                } catch (error) {
                    console.error('Error calling loadDataFromAPI:', error);
                }
            } else {
                console.info('AYSA metabox loader not available in this editor context; skipping remote preload.');
            }
            
            // Check for stored keyword data as fallback
            const keywordData = <?php echo json_encode($keyword_data); ?>;
            if (keywordData && Object.keys(keywordData).length > 0) {
                console.log('Using stored keyword data as fallback');
                try {
                    // Actualizare manuală a câmpurilor
                    var mainKeywordInput = document.getElementById('aysa-entity-keyword');
                    var secondKeywordInput = document.getElementById('aysa-entity-second-keyword');
                    var metaTitleInput = document.getElementById('aysa-entity-meta-title');
                    var metaDescriptionInput = document.getElementById('aysa-entity-meta-description');
                    var metaKeywordsInput = document.getElementById('aysa-entity-meta-keywords');
                    
                    if (mainKeywordInput && keywordData.keyword) {
                        mainKeywordInput.value = keywordData.keyword;
                    }
                    
                    if (secondKeywordInput && keywordData.second_keyword) {
                        secondKeywordInput.value = keywordData.second_keyword;
                    }
                    
                    if (metaTitleInput && keywordData.meta_title) {
                        metaTitleInput.value = keywordData.meta_title;
                    }
                    
                    if (metaDescriptionInput && keywordData.meta_description) {
                        metaDescriptionInput.value = keywordData.meta_description;
                    }
                    
                    if (metaKeywordsInput && keywordData.meta_keywords) {
                        metaKeywordsInput.value = keywordData.meta_keywords;
                    }
                } catch (error) {
                    console.error('Error updating fields with stored data:', error);
                }
            }
        });
        </script>
        <?php
    }

    /**
     * Save WordPress post SEO data
     */
    public function save_post_seo_data($post_id, $post) 
    {
        if ($this->is_restoring_jsonld) {
            return;
        }

        // Basic validation first
        if (!$post_id || !$post || $post->post_type !== 'post') {
            return;
        }
        
        // Don't save on autosave, revisions or trashed posts
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }
        
        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
            return;
        }
        
        if (get_post_status($post_id) === 'trash') {
            return;
        }
        
        // Check user capabilities
        if (!current_user_can('edit_post', $post_id)) {
            return;
        }
        
        // Enhanced nonce handling and data collection
        $has_aysa_data = false;
        
        // Check for AYSA nonce and data in POST
        if (isset($_POST['aysa_post_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aysa_post_nonce'])), 'aysa_post_save')) {
            $has_aysa_data = true;
        }
        
        // Check for direct POST data (without nonce - for WordPress core saves)
        $has_post_data = isset($_POST['aysa_meta_title']) || isset($_POST['aysa_meta_description']) || 
                        isset($_POST['aysa_meta_keywords']) || isset($_POST['aysa_url_recomandat']) || 
                        isset($_POST['meta_title']) || isset($_POST['meta_description']) || isset($_POST['meta_keywords']) ||
                        isset($_POST['keyword']) || isset($_POST['second_keyword']) ||
                        isset($_POST['aysa_keyword']) || isset($_POST['aysa_secondary_keywords']);
        
        if ($has_post_data) {
            $has_aysa_data = true;
            aysa_debug_log("AYSA: Detected POST data for post ID: $post_id. Fields: " . json_encode(array_keys(array_filter([
                'aysa_meta_title' => isset($_POST['aysa_meta_title']),
                'aysa_meta_description' => isset($_POST['aysa_meta_description']),
                'aysa_url_recomandat' => isset($_POST['aysa_url_recomandat'])
            ]))));
        }
        
        // Check for AYSA data in meta (from AJAX saves)
        $temp_meta = get_post_meta($post_id, '_aysa_temp_save_data', true);
        if (!empty($temp_meta) && is_array($temp_meta)) {
            $has_aysa_data = true;
        }
        
        // If we have AYSA data, save it
        if ($has_aysa_data || apply_filters('aysa_force_save_post', false, $post_id, $post)) {
            aysa_debug_log("AYSA: Saving meta data for post ID: $post_id");
            $this->save_post_meta_data($post_id);
            $this->save_advanced_post_settings($post_id);
            
            // Clean up temp data
            delete_post_meta($post_id, '_aysa_temp_save_data');
            aysa_debug_log("AYSA: Meta data saved successfully for post ID: $post_id");
            
            // ✅ NEW: Trigger association creation for posts
            $this->maybe_create_post_associations($post_id, $post);

            // Keep JSON-LD blocks generated by AYSA even if editor stripped them on submit.
            $this->restore_jsonld_scripts_from_request($post_id, 'post');
        } else {
            aysa_debug_log("AYSA: No AYSA data detected for post ID: $post_id");
        }
    }

    /**
     * Restore JSON-LD scripts sent by AYSA UI into post content after save.
     * This prevents loss of FAQ/structured data when the visual editor strips script tags.
     *
     * @param int    $post_id
     * @param string $post_type
     * @return void
     */
    private function restore_jsonld_scripts_from_request($post_id, $post_type = 'post')
    {
        if ($this->is_restoring_jsonld) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        if (!$post_id || !isset($_POST['aysa_jsonld_scripts'])) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow. Reviewed release input boundary: the guarded callback preserves structured input only until its field-specific validation or normalizer, before persistence, output, SQL, or a broker request.
        $raw_payload = (string) wp_unslash($_POST['aysa_jsonld_scripts']);
        if ($raw_payload === '') {
            return;
        }

        $decoded_payload = json_decode($raw_payload, true);
        if (!is_array($decoded_payload) || empty($decoded_payload)) {
            return;
        }

        $scripts = array();
        foreach ($decoded_payload as $candidate) {
            if (!is_string($candidate)) {
                continue;
            }

            $candidate = trim($candidate);
            if ($candidate === '') {
                continue;
            }

            if (!preg_match('/<script[^>]*type=["\']application\/ld\+json["\'][^>]*>([\s\S]*?)<\/script>/i', $candidate, $m)) {
                continue;
            }

            $json_raw = trim((string) $m[1]);
            if ($json_raw === '') {
                continue;
            }

            $json_data = json_decode($json_raw, true);
            if (!is_array($json_data)) {
                continue;
            }

            $scripts[] = '<script type="application/ld+json">' . wp_json_encode($json_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '</script>';
        }

        if (empty($scripts)) {
            return;
        }

        $current_content = (string) get_post_field('post_content', $post_id, 'raw');
        if ($current_content === '') {
            return;
        }

        $scripts_to_add = array();
        foreach (array_unique($scripts) as $script) {
            if (stripos($current_content, $script) === false) {
                $scripts_to_add[] = $script;
            }
        }

        if (empty($scripts_to_add)) {
            return;
        }

        $updated_content = rtrim($current_content) . "\n\n" . implode("\n", $scripts_to_add) . "\n";
        $this->is_restoring_jsonld = true;

        if ($post_type === 'product') {
            remove_action('save_post_product', array($this, 'save_product_seo_data'), 10);
        } elseif ($post_type === 'page') {
            remove_action('save_post_page', array($this, 'save_page_seo_data'), 10);
        } else {
            remove_action('save_post_post', array($this, 'save_post_seo_data'), 10);
        }

        $save_callback = function () use ($post_id, $updated_content) {
            return wp_update_post(array(
                'ID' => (int) $post_id,
                'post_content' => $updated_content
            ), true);
        };

        if (function_exists('aysa_execute_without_sanitization')) {
            aysa_execute_without_sanitization($save_callback);
        } else {
            call_user_func($save_callback);
        }

        if ($post_type === 'product') {
            add_action('save_post_product', array($this, 'save_product_seo_data'), 10, 2);
        } elseif ($post_type === 'page') {
            add_action('save_post_page', array($this, 'save_page_seo_data'), 10, 2);
        } else {
            add_action('save_post_post', array($this, 'save_post_seo_data'), 10, 2);
        }

        $this->is_restoring_jsonld = false;
    }

    /**
     * Save post meta data
     */
    private function save_post_meta_data($post_id) 
    {
        $meta_fields = [
            'keyword' => 'aysa_keyword',
            'second_keyword' => 'aysa_second_keyword',
            'aysa_keyword' => 'aysa_keyword',                    // New main keyword field
            'aysa_secondary_keywords' => 'aysa_secondary_keywords', // New secondary keywords field
            'meta_title' => 'aysa_meta_title',                   // Blog content meta title
            'meta_description' => 'aysa_meta_description',       // Blog content meta description
            'meta_keywords' => 'aysa_meta_keywords',             // Blog content meta keywords
            'aysa_meta_title' => 'aysa_meta_title',
            'aysa_meta_description' => 'aysa_meta_description',
            'aysa_meta_keywords' => 'aysa_meta_keywords',
            'aysa_url_recomandat' => 'aysa_url_recomandat',
            'blog_content_outline' => 'aysa_blog_content_outline',
            'blog_content_tldr' => 'aysa_blog_content_tldr',
            'blog_content_introduction' => 'aysa_blog_content_introduction',
            'blog_content_extended' => 'aysa_blog_content_extended',
            'blog_content_faq' => 'aysa_blog_content_faq',
            'blog_content_conclusion' => 'aysa_blog_content_conclusion',
            'blog_content_image_url' => 'aysa_blog_content_image_url',
            'featured_image_url' => 'aysa_featured_image_url'
        ];
        
        $keyword_data = array();
        
        foreach ($meta_fields as $field => $meta_key) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                $value = sanitize_textarea_field(wp_unslash($_POST[$field]));
                update_post_meta($post_id, $meta_key, $value);
                $keyword_data[$field] = $value;
                
                // Also save with _aysa_seo_ prefix for frontend display compatibility
                if ($field === 'aysa_meta_title' || $field === 'meta_title') {
                    update_post_meta($post_id, '_aysa_seo_title', $value);
                } elseif ($field === 'aysa_meta_description' || $field === 'meta_description') {
                    update_post_meta($post_id, '_aysa_seo_description', $value);
                } elseif ($field === 'aysa_meta_keywords' || $field === 'meta_keywords') {
                    update_post_meta($post_id, '_aysa_seo_keywords', $value);
                }
                
                // Special handling for URL recomandat - update post slug
                if ($field === 'aysa_url_recomandat' && !empty($value)) {
                    // Extract slug from URL if it's a full URL, otherwise use as is
                    $slug_value = $value;
                    
                    // If it looks like a full URL, extract the last part
                    if (preg_match('/^https?:\/\//', $value)) {
                        $parsed_url = wp_parse_url($value);
                        if (isset($parsed_url['path'])) {
                            $slug_value = trim($parsed_url['path'], '/');
                            // Get only the last segment if there are multiple segments
                            $segments = explode('/', $slug_value);
                            $slug_value = end($segments);
                        }
                    }
                    
                    // Remove any remaining URL parts and clean the slug
                    $slug_value = preg_replace('/^https?:\/\/[^\/]+\//', '', $slug_value);
                    $new_slug = sanitize_title($slug_value);
                    
                    if (!empty($new_slug)) {
                        // Prevent infinite loops during post update
                        remove_action('save_post_post', [$this, 'save_post_seo_data'], 10);
                        
                        $update_result = wp_update_post(array(
                            'ID' => $post_id,
                            'post_name' => $new_slug
                        ), true); // Return WP_Error on failure
                        
                        // Re-add the hook after update
                        add_action('save_post_post', [$this, 'save_post_seo_data'], 10, 2);
                        
                        if (is_wp_error($update_result)) {
                            aysa_debug_log("AYSA: Failed to update post slug: " . $update_result->get_error_message());
                        } else {
                            aysa_debug_log("AYSA: Original URL recomandat: '$value', Extracted slug: '$slug_value', Final slug: '$new_slug' for post ID: $post_id");
                        }
                    }
                }
            }
        }
        
        // Save all keyword data as a single meta
        if (!empty($keyword_data)) {
            aysa_debug_log("AYSA: Saving keyword_data for post ID $post_id: " . json_encode($keyword_data));
            update_post_meta($post_id, 'aysa_keyword_data', $keyword_data);
            
            // Also ensure consistency between old and new field names
            if (isset($keyword_data['aysa_keyword']) && !isset($keyword_data['keyword'])) {
                $keyword_data['keyword'] = $keyword_data['aysa_keyword'];
            }
            if (isset($keyword_data['aysa_secondary_keywords']) && !isset($keyword_data['second_keyword'])) {
                $keyword_data['second_keyword'] = $keyword_data['aysa_secondary_keywords'];
            }
            
            // Save the consolidated data
            update_post_meta($post_id, 'aysa_keyword_data', $keyword_data);
        }

        // Force immediate recompute for On-Page table quality/content-source after save.
        $this->invalidate_entity_quality_cache('post', (int) $post_id);
    }

    /**
     * Create associations for posts after save
     */
    private function maybe_create_post_associations($post_id, $post) 
    {
        // Get API key and project ID
        $api_key = get_option('aysa_api_key');
        $project_id = get_option('aysa_project_id', 0);
        
        if (empty($api_key) || empty($project_id)) {
            aysa_debug_log("AYSA: Cannot create associations - missing API key or project ID");
            return;
        }
        
        // Get saved keyword data
        $keyword_data = get_post_meta($post_id, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            aysa_debug_log("AYSA: No keyword data found for post ID: $post_id");
            return;
        }
        
        // Get main keyword
        $main_keyword = '';
        if (!empty($keyword_data['aysa_keyword'])) {
            $main_keyword = $keyword_data['aysa_keyword'];
        } elseif (!empty($keyword_data['keyword'])) {
            $main_keyword = $keyword_data['keyword'];
        }
        
        // Get secondary keywords
        $secondary_keywords_str = '';
        if (!empty($keyword_data['aysa_secondary_keywords'])) {
            $secondary_keywords_str = $keyword_data['aysa_secondary_keywords'];
        } elseif (!empty($keyword_data['second_keyword'])) {
            $secondary_keywords_str = $keyword_data['second_keyword'];
        }
        
        if (empty($main_keyword)) {
            aysa_debug_log("AYSA: No main keyword found for post ID: $post_id");
            return;
        }
        
        aysa_debug_log("AYSA: Creating associations for post ID: $post_id, main: '$main_keyword', secondary: '$secondary_keywords_str'");
        
        // Prepare keywords array
        $all_keywords = [$main_keyword];
        if (!empty($secondary_keywords_str)) {
            $secondary_keywords = array_map('trim', explode(',', $secondary_keywords_str));
            $secondary_keywords = array_filter($secondary_keywords, function($keyword) {
                return !empty($keyword);
            });
            $all_keywords = array_merge($all_keywords, $secondary_keywords);
        }
        
        // Remove duplicates
        $all_keywords = array_unique($all_keywords);
        
        aysa_debug_log("AYSA: All keywords for associations: " . json_encode($all_keywords));
        
        // Create associations array
        $associations_array = array();
        foreach ($all_keywords as $index => $keyword) {
            $is_main = ($index === 0); // First keyword is main
            
            $associations_array[] = array(
                'IdIntern' => $post_id,
                'AssociatedOn' => 'Post',
                'Value' => $post->post_title,
                'Link' => get_permalink($post_id),
                'Keyword' => $keyword,
                'ProjectId' => $project_id,
                'IsMain' => $is_main,
                
                // SEO fields from keyword data
                'MetaTitle' => isset($keyword_data['aysa_meta_title']) ? $keyword_data['aysa_meta_title'] : '',
                'MetaDescription' => isset($keyword_data['aysa_meta_description']) ? $keyword_data['aysa_meta_description'] : '',
                'MetaKeywords' => isset($keyword_data['aysa_meta_keywords']) ? $keyword_data['aysa_meta_keywords'] : '',
                
                // Tracking - assume tracked if it's main keyword
                'IsTracked' => $is_main
            );
        }
        
        aysa_debug_log("AYSA: Creating " . count($associations_array) . " associations for post ID: $post_id");
        
        // Make API call to create associations
        $api_result = $this->make_insert_many_api_call($associations_array, $api_key);
        
        if ($api_result['success']) {
            aysa_debug_log("AYSA: Successfully created associations for post ID: $post_id");
            
            // Store successful API response
            update_post_meta($post_id, '_aysa_last_insert_many', array(
                'timestamp' => current_time('timestamp'),
                'data' => $associations_array,
                'response' => $api_result['data']
            ));
        } else {
            aysa_debug_log("AYSA: Failed to create associations for post ID: $post_id. Error: " . $api_result['message']);
        }
    }
    
    /**
     * Save advanced post settings
     */
    private function save_advanced_post_settings($post_id) 
    {
        // Save robots meta settings
        $robots_meta_fields = [
            'robots_index',
            'robots_noindex',
            'robots_nofollow',
            'robots_noarchive',
            'robots_noimageindex',
            'robots_nosnippet'
        ];
        
        foreach ($robots_meta_fields as $field) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                update_post_meta($post_id, 'aysa_' . $field, 1);
            } else {
                delete_post_meta($post_id, 'aysa_' . $field);
            }
        }
        
        // Save other advanced settings
        $advanced_fields = [
            'max_snippet',
            'max_video_preview',
            'max_image_preview',
            'canonical_url',
            'targeted_se',
            'targeted_country',
            'targeted_region',
            'targeted_city',
            'update_frequency',
            'language_optimize',
            'device_optimize'
        ];
        
        foreach ($advanced_fields as $field) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
            if (isset($_POST[$field])) {
                // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                update_post_meta($post_id, 'aysa_' . $field, sanitize_text_field(wp_unslash($_POST[$field])));
            }
        }
    }

    /**
     * ✅ NEW: Handle UPSERT API call BEFORE WordPress save
     */
    public function handle_upsert_entity() {
        // ✅ Enhanced nonce debugging with fallbacks
        $received_nonce = '';
        $nonce_source = 'none';
        
        // Try multiple sources for nonce
        if (isset($_POST['nonce']) && !empty($_POST['nonce'])) {
            $received_nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
            $nonce_source = 'post_nonce';
        } elseif (isset($_POST['aysa_metabox_nonce']) && !empty($_POST['aysa_metabox_nonce'])) {
            $received_nonce = sanitize_text_field(wp_unslash($_POST['aysa_metabox_nonce']));
            $nonce_source = 'post_aysa_metabox_nonce';
        } elseif (isset($_POST['_wpnonce']) && !empty($_POST['_wpnonce'])) {
            $received_nonce = sanitize_text_field(wp_unslash($_POST['_wpnonce']));
            $nonce_source = 'post_wpnonce';
        }
        
        $nonce_action = 'aysa_metabox_nonce';
        
        // Debug logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA UPSERT: Received nonce: ' . substr($received_nonce, 0, 10) . '...');
            aysa_debug_log('AYSA UPSERT: Nonce source: ' . $nonce_source);
            aysa_debug_log('AYSA UPSERT: Nonce action: ' . $nonce_action);
            aysa_debug_log('AYSA UPSERT: Nonce verification result: ' . (wp_verify_nonce($received_nonce, $nonce_action) ? 'SUCCESS' : 'FAILED'));
            aysa_debug_log('AYSA UPSERT: Current user ID: ' . get_current_user_id());
            aysa_debug_log('AYSA UPSERT: POST data keys: ' . implode(', ', array_keys($_POST)));
        }
        
        // Verify nonce
        $nonce_valid = wp_verify_nonce($received_nonce, $nonce_action) || wp_verify_nonce($received_nonce, 'aysa_nonce');
        
        if (!$nonce_valid) {
            // Additional debug for nonce failure
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA UPSERT: NONCE FAILED - received: ' . $received_nonce);
                aysa_debug_log('AYSA UPSERT: NONCE FAILED - expected action: ' . $nonce_action);
                
                // Try to generate a new nonce to see what it would be
                $fresh_nonce = wp_create_nonce($nonce_action);
                aysa_debug_log('AYSA UPSERT: Fresh nonce would be: ' . substr($fresh_nonce, 0, 10) . '...');
                
                // Check if nonce is stale/expired
                $nonce_life = wp_nonce_tick() * 2;
                aysa_debug_log('AYSA UPSERT: Nonce lifetime setting: ' . $nonce_life . ' seconds');
                
                // ✅ TEMPORARY: Try alternative nonce actions for debugging
                $alt_actions = ['aysa_product_meta_box', 'aysa_product_save', '_wpnonce'];
                foreach ($alt_actions as $alt_action) {
                    $alt_verify = wp_verify_nonce($received_nonce, $alt_action);
                    aysa_debug_log('AYSA UPSERT: Alternative action "' . $alt_action . '": ' . ($alt_verify ? 'SUCCESS' : 'FAILED'));
                }
            }
            
            wp_send_json_error([
                'message' => 'Security check failed', 
                'code' => 'NONCE_FAILED',
                'debug' => [
                    'received_nonce' => substr($received_nonce, 0, 10) . '...',
                    'nonce_source' => $nonce_source,
                    'action' => $nonce_action,
                    'user_id' => get_current_user_id()
                ]
            ]);
            return;
        }
        
        // Get data from request
        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field(wp_unslash($_POST['entity_type'])) : '';
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        
        if (!$entity_id || !$entity_type || !$project_id) {
            wp_send_json_error(['message' => 'Missing required data', 'code' => 'MISSING_DATA']);
            return;
        }

        if (!$this->current_user_can_edit_entity($entity_id, $entity_type)) {
            wp_send_json_error(['message' => 'Insufficient permissions', 'code' => 'INSUFFICIENT_PERMISSIONS']);
            return;
        }
        
        // ✅ Prepare upsert data in EXACT JavaScript format
        $entity_name = '';
        $entity_link = '';
        
        if ($entity_type === 'Product') {
            $post = get_post($entity_id);
            $entity_name = $post ? $post->post_title : '';
            // ✅ For WooCommerce products, use proper permalink
            if (function_exists('wc_get_product')) {
                $product = wc_get_product($entity_id);
                $entity_link = $product ? $product->get_permalink() : get_permalink($entity_id);
            } else {
                $entity_link = get_permalink($entity_id);
            }
        } elseif ($entity_type === 'Category') {
            $term = get_term($entity_id);
            $entity_name = $term ? $term->name : '';
            $entity_link = get_term_link($entity_id);
        } elseif ($entity_type === 'Page') {
            $post = get_post($entity_id);
            $entity_name = $post ? $post->post_title : '';
            $entity_link = get_permalink($entity_id);
        } elseif ($entity_type === 'Post') {
            // ✅ Handle WordPress posts (Blog posts)
            $post = get_post($entity_id);
            $entity_name = $post ? $post->post_title : '';
            $entity_link = get_permalink($entity_id);
        } else {
            // ✅ Default fallback for any other entity type
            $post = get_post($entity_id);
            $entity_name = $post ? $post->post_title : '';
            $entity_link = get_permalink($entity_id);
        }
        
        // ✅ CRITICAL: Get association ID if provided (for updates)
        $association_id = isset($_POST['association_id']) ? intval(wp_unslash($_POST['association_id'])) : null;
        
        $upsert_data = array(
            'IdIntern' => $entity_id,
            'AssociatedOn' => $entity_type,
            'Value' => $entity_name,
            'Link' => $entity_link,  // ✅ Use the correctly determined link
            'Keyword' => isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '',
            'ProjectId' => $project_id,
            'IsMain' => true,
            
            // SEO fields
            'MetaTitle' => isset($_POST['meta_title']) ? sanitize_text_field(wp_unslash($_POST['meta_title'])) : '',
            'MetaDescription' => isset($_POST['meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['meta_description'])) : '',
            'MetaKeywords' => isset($_POST['meta_keywords']) ? sanitize_text_field(wp_unslash($_POST['meta_keywords'])) : '',
            
            // SEO Settings - from metabox
            'TargetedSearchEngine' => isset($_POST['targeted_search_engine']) ? sanitize_text_field(wp_unslash($_POST['targeted_search_engine'])) : '',
            'TargetedCountry' => isset($_POST['targeted_country']) ? sanitize_text_field(wp_unslash($_POST['targeted_country'])) : '',
            'TargetedRegion' => isset($_POST['targeted_region']) ? sanitize_text_field(wp_unslash($_POST['targeted_region'])) : '',
            'TargetedCity' => isset($_POST['targeted_city']) ? sanitize_text_field(wp_unslash($_POST['targeted_city'])) : '',
            'UpdateFrequency' => isset($_POST['update_frequency']) ? sanitize_text_field(wp_unslash($_POST['update_frequency'])) : '',
            'LanguageOptimize' => isset($_POST['language_optimize']) ? sanitize_text_field(wp_unslash($_POST['language_optimize'])) : '',
            'DeviceOptimize' => isset($_POST['device_optimize']) ? sanitize_text_field(wp_unslash($_POST['device_optimize'])) : '',
            
            // Advanced Settings - from metabox
            'Index' => isset($_POST['index']) ? (bool)$_POST['index'] : null,
            'NoFollow' => isset($_POST['no_follow']) ? (bool)$_POST['no_follow'] : null,
            'NoArchive' => isset($_POST['no_archive']) ? (bool)$_POST['no_archive'] : null,
            'NoImageIndex' => isset($_POST['no_image_index']) ? (bool)$_POST['no_image_index'] : null,
            'NoSnippet' => isset($_POST['no_snippet']) ? (bool)$_POST['no_snippet'] : null,
            'MaxSnippet' => isset($_POST['max_snippet']) && !empty($_POST['max_snippet']) ? intval(wp_unslash($_POST['max_snippet'])) : null,
            'MaxVideoPreview' => isset($_POST['max_video_preview']) && !empty($_POST['max_video_preview']) ? intval(wp_unslash($_POST['max_video_preview'])) : null,
            'MaxImagePreview' => isset($_POST['max_image_preview']) && !empty($_POST['max_image_preview']) ? sanitize_text_field(wp_unslash($_POST['max_image_preview'])) : null,
            'CanonicalUrl' => isset($_POST['canonical_url']) ? esc_url_raw(wp_unslash($_POST['canonical_url'])) : '',
            
            // Secondary keywords
            'SecondKeyword' => '',
            
            // Tracking
            'IsTracked' => isset($_POST['track']) ? (bool)$_POST['track'] : false,
            
            // ✅ CRITICAL: Add ForceOverride parameter for keyword conflict resolution
            'ForceOverride' => isset($_POST['force_override']) ? (bool)$_POST['force_override'] : false
        );
        
        // Include Id only for updates; never send null.
        if ($association_id && $association_id > 0) {
            $upsert_data['Id'] = $association_id;
        }
        
        // Debug logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA UPSERT: Starting upsert for entity ' . $entity_id . ' (' . $entity_type . ')');
            aysa_debug_log('AYSA UPSERT: Association ID: ' . ($association_id ? $association_id : 'NULL (new association)'));
        aysa_debug_log('AYSA UPSERT: ForceOverride flag: ' . (isset($_POST['force_override']) && rest_sanitize_boolean(wp_unslash($_POST['force_override'])) ? 'TRUE' : 'FALSE'));
        aysa_debug_log('AYSA UPSERT: Max fields received - max_snippet: ' . (isset($_POST['max_snippet']) ? sanitize_text_field(wp_unslash($_POST['max_snippet'])) : 'NOT SET') . ', max_video_preview: ' . (isset($_POST['max_video_preview']) ? sanitize_text_field(wp_unslash($_POST['max_video_preview'])) : 'NOT SET') . ', max_image_preview: ' . (isset($_POST['max_image_preview']) ? sanitize_text_field(wp_unslash($_POST['max_image_preview'])) : 'NOT SET'));
            aysa_debug_log('AYSA UPSERT: Data: ' . aysa_debug_export($upsert_data, true));
        }
        
        // Make API call to AYSA
        $api_result = $this->make_upsert_api_call($upsert_data, '');
        
        if ($api_result['success']) {
            // Store successful API response
            if ($entity_type === 'product' || $entity_type === 'page') {
                update_post_meta($entity_id, '_aysa_last_upsert', array(
                    'timestamp' => current_time('timestamp'),
                    'data' => $upsert_data,
                    'response' => $api_result['data']
                ));
            } elseif ($entity_type === 'term' || $entity_type === 'category') {
                update_term_meta($entity_id, '_aysa_last_upsert', array(
                    'timestamp' => current_time('timestamp'),
                    'data' => $upsert_data,
                    'response' => $api_result['data']
                ));
            }

            $this->track_wallet_usage(
                'aysa_upsert_entity',
                'upsert_entity',
                5,
                array(
                    'module' => 'on_page_optimization',
                    'entity_id' => intval($entity_id),
                    'entity_type' => (string) $entity_type,
                ),
                '/v1/association/upsert'
            );
            
            wp_send_json_success([
                'message' => 'UPSERT completed successfully',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'api_response' => $api_result['data']
            ]);
            
        } else {
            wp_send_json_error([
                'message' => 'UPSERT failed: ' . $api_result['error'],
                'code' => 'UPSERT_API_FAILED',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'api_error' => $api_result['error']
            ]);
        }
    }
    
    /**
     * ✅ NEW: Handle INSERT MANY API call for secondary keywords
     */
    public function handle_insert_many_keywords() {
        // ✅ Enhanced nonce debugging
        $received_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
        $nonce_action = 'aysa_metabox_nonce';
        
        // Debug logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA INSERT MANY: Received nonce: ' . substr($received_nonce, 0, 10) . '...');
            aysa_debug_log('AYSA INSERT MANY: Nonce verification result: ' . (wp_verify_nonce($received_nonce, $nonce_action) ? 'SUCCESS' : 'FAILED'));
        }
        
        // Verify nonce
        $nonce_valid = wp_verify_nonce($received_nonce, $nonce_action);
        
        if (!$nonce_valid) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA INSERT MANY: NONCE FAILED - received: ' . $received_nonce);
            }
            wp_send_json_error([
                'message' => 'Security check failed', 
                'code' => 'NONCE_FAILED',
                'debug' => [
                    'received_nonce' => substr($received_nonce, 0, 10) . '...',
                    'action' => $nonce_action
                ]
            ]);
            return;
        }
        
        // Get data from request
        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field(wp_unslash($_POST['entity_type'])) : '';
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reviewed release input boundary: the guarded callback preserves structured input only until its field-specific validation or normalizer, before persistence, output, SQL, or a broker request.
        $secondary_keywords = isset($_POST['secondary_keywords']) ? wp_unslash($_POST['secondary_keywords']) : array();
        
        if (!$entity_id || !$entity_type || !$project_id || empty($secondary_keywords)) {
            wp_send_json_error(['message' => 'Missing required data or no keywords', 'code' => 'MISSING_DATA']);
            return;
        }

        if (!$this->current_user_can_edit_entity($entity_id, $entity_type)) {
            wp_send_json_error(['message' => 'Insufficient permissions', 'code' => 'INSUFFICIENT_PERMISSIONS']);
            return;
        }
        
        // Ensure secondary_keywords is an array
        if (!is_array($secondary_keywords)) {
            $secondary_keywords = array($secondary_keywords);
        }
        
        // Clean and validate keywords
        $clean_keywords = array();
        foreach ($secondary_keywords as $keyword) {
            $clean_keyword = sanitize_text_field($keyword);
            if (!empty($clean_keyword)) {
                $clean_keywords[] = $clean_keyword;
            }
        }
        
        if (empty($clean_keywords)) {
            wp_send_json_error(['message' => 'No valid keywords provided', 'code' => 'NO_VALID_KEYWORDS']);
            return;
        }
        
        // ✅ Prepare insert many data as ARRAY of associations (like JavaScript)
        $entity_name = '';
        $entity_link = '';
        
        if ($entity_type === 'Product') {
            $post = get_post($entity_id);
            $entity_name = $post ? $post->post_title : '';
            // ✅ For WooCommerce products, use proper permalink
            if (function_exists('wc_get_product')) {
                $product = wc_get_product($entity_id);
                $entity_link = $product ? $product->get_permalink() : get_permalink($entity_id);
            } else {
                $entity_link = get_permalink($entity_id);
            }
        } elseif ($entity_type === 'Category') {
            $term = get_term($entity_id);
            $entity_name = $term ? $term->name : '';
            $entity_link = get_term_link($entity_id);
        } else {
            // For pages and other post types
            $post = get_post($entity_id);
            $entity_name = $post ? $post->post_title : '';
            $entity_link = get_permalink($entity_id);
        }
        
        // ✅ Validate we have the required data
        if (empty($entity_name)) {
            wp_send_json_error(['message' => 'Could not retrieve entity name', 'code' => 'ENTITY_NAME_ERROR']);
            return;
        }
        
        if (empty($entity_link) || is_wp_error($entity_link)) {
            wp_send_json_error(['message' => 'Could not retrieve entity link', 'code' => 'ENTITY_LINK_ERROR']);
            return;
        }
        
        $associations_array = array();
        foreach ($clean_keywords as $keyword) {
            $associations_array[] = array(
                'IdIntern' => $entity_id,
                'AssociatedOn' => $entity_type,
                'Value' => $entity_name,
                'Link' => $entity_link,
                'Keyword' => $keyword,
                'ProjectId' => $project_id,
                'IsMain' => false, // Secondary keywords are not main
                
                // SEO fields (empty for secondary keywords - they don't have individual meta)
                'MetaTitle' => '',
                'MetaDescription' => '',
                'MetaKeywords' => '',
                
                // ✅ SEO Settings - from metabox (same as main keyword)
                'TargetedSearchEngine' => isset($_POST['targeted_search_engine']) ? sanitize_text_field(wp_unslash($_POST['targeted_search_engine'])) : '',
                'TargetedCountry' => isset($_POST['targeted_country']) ? sanitize_text_field(wp_unslash($_POST['targeted_country'])) : '',
                'TargetedRegion' => isset($_POST['targeted_region']) ? sanitize_text_field(wp_unslash($_POST['targeted_region'])) : '',
                'TargetedCity' => isset($_POST['targeted_city']) ? sanitize_text_field(wp_unslash($_POST['targeted_city'])) : '',
                'UpdateFrequency' => isset($_POST['update_frequency']) ? sanitize_text_field(wp_unslash($_POST['update_frequency'])) : '',
                'LanguageOptimize' => isset($_POST['language_optimize']) ? sanitize_text_field(wp_unslash($_POST['language_optimize'])) : '',
                'DeviceOptimize' => isset($_POST['device_optimize']) ? sanitize_text_field(wp_unslash($_POST['device_optimize'])) : '',
                
                // ✅ Advanced Settings - from metabox (same as main keyword)
                'Index' => isset($_POST['index']) ? (bool)$_POST['index'] : null,
                'NoFollow' => isset($_POST['no_follow']) ? (bool)$_POST['no_follow'] : null,
                'NoArchive' => isset($_POST['no_archive']) ? (bool)$_POST['no_archive'] : null,
                'NoImageIndex' => isset($_POST['no_image_index']) ? (bool)$_POST['no_image_index'] : null,
                'NoSnippet' => isset($_POST['no_snippet']) ? (bool)$_POST['no_snippet'] : null,
                'MaxSnippet' => isset($_POST['max_snippet']) && !empty($_POST['max_snippet']) ? intval(wp_unslash($_POST['max_snippet'])) : null,
                'MaxVideoPreview' => isset($_POST['max_video_preview']) && !empty($_POST['max_video_preview']) ? intval(wp_unslash($_POST['max_video_preview'])) : null,
                'MaxImagePreview' => isset($_POST['max_image_preview']) && !empty($_POST['max_image_preview']) ? sanitize_text_field(wp_unslash($_POST['max_image_preview'])) : null,
                'CanonicalUrl' => isset($_POST['canonical_url']) ? esc_url_raw(wp_unslash($_POST['canonical_url'])) : '',
                
                // ✅ Tracking - from metabox
                'IsTracked' => isset($_POST['track']) ? (bool)$_POST['track'] : false
            );
        }
        
        $insert_data = $associations_array;
        
        // ✅ Enhanced debug logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA INSERT MANY: Starting insert for entity ' . $entity_id . ' (' . $entity_type . ')');
            aysa_debug_log('AYSA INSERT MANY: Entity name: ' . $entity_name);
            aysa_debug_log('AYSA INSERT MANY: Entity link: ' . $entity_link);
            aysa_debug_log('AYSA INSERT MANY: Keywords: ' . aysa_debug_export($clean_keywords, true));
            aysa_debug_log('AYSA INSERT MANY: Associations array count: ' . count($associations_array));
            aysa_debug_log('AYSA INSERT MANY: Full associations data: ' . aysa_debug_export($associations_array, true));
        }
        
        // Make API call to AYSA
        $api_result = $this->make_insert_many_api_call($insert_data, '');
        
        if ($api_result['success']) {
            // Store successful API response
            if ($entity_type === 'product' || $entity_type === 'page') {
                update_post_meta($entity_id, '_aysa_last_insert_many', array(
                    'timestamp' => current_time('timestamp'),
                    'data' => $insert_data,
                    'response' => $api_result['data']
                ));
            } elseif ($entity_type === 'term' || $entity_type === 'category') {
                update_term_meta($entity_id, '_aysa_last_insert_many', array(
                    'timestamp' => current_time('timestamp'),
                    'data' => $insert_data,
                    'response' => $api_result['data']
                ));
            }

            $this->track_wallet_usage(
                'aysa_insert_many_keywords',
                'insert_many_keywords',
                5,
                array(
                    'module' => 'on_page_optimization',
                    'entity_id' => intval($entity_id),
                    'entity_type' => (string) $entity_type,
                    'keywords_count' => count($clean_keywords),
                ),
                '/v1/association/insert-many'
            );
            
            wp_send_json_success([
                'message' => 'INSERT MANY completed successfully',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'keywords_processed' => count($clean_keywords),
                'api_response' => $api_result['data']
            ]);
            
        } else {
            wp_send_json_error([
                'message' => 'INSERT MANY failed: ' . $api_result['error'],
                'code' => 'INSERT_MANY_API_FAILED',
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'keywords_count' => count($clean_keywords),
                'api_error' => $api_result['error']
            ]);
        }
    }

    public function handle_delete_entity_associations() {
        $nonce = isset($_POST['nonce']) ? sanitize_text_field((string) wp_unslash($_POST['nonce'])) : '';
        if (!wp_verify_nonce($nonce, 'aysa_metabox_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed', 'code' => 'NONCE_FAILED'));
            return;
        }

        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field((string) wp_unslash($_POST['entity_type'])) : '';
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        if ($entity_id <= 0 || $entity_type === '' || $project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing required data', 'code' => 'MISSING_DATA'));
            return;
        }
        if (!$this->current_user_can_edit_entity($entity_id, $entity_type)) {
            wp_send_json_error(array('message' => 'Insufficient permissions', 'code' => 'INSUFFICIENT_PERMISSIONS'));
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $endpoint = 'api/Association/by-entity/'
            . rawurlencode((string) $entity_id) . '/'
            . rawurlencode((string) $entity_type) . '/'
            . rawurlencode((string) $project_id);
        $deleted = Aysa_Auth_Manager::get_instance()->make_authenticated_request($endpoint, 'DELETE');
        if ($deleted === false) {
            wp_send_json_error(array('message' => 'Association deletion failed', 'code' => 'DELETE_FAILED'));
            return;
        }

        wp_send_json_success(array(
            'deleted' => true,
            'project_id' => $project_id,
            'entity_id' => $entity_id,
            'entity_type' => $entity_type,
        ));
    }

    /**
     * ✅ NEW: Make actual UPSERT API call to AYSA
     */
    private function make_upsert_api_call($data, $api_key) {
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $result = $auth_manager->make_authenticated_request_with_meta('api/Association/upsert', 'PUT', $data);

        if (empty($result['ok']) && intval($result['status'] ?? 0) === 400) {
            $response_body = (string) ($result['body'] ?? '');
            if (stripos($response_body, 'associationDto') !== false) {
                $result = $auth_manager->make_authenticated_request_with_meta(
                    'api/Association/upsert',
                    'PUT',
                    array('associationDto' => $data)
                );
            }
        }

        if (empty($result['ok'])) {
            $status = intval($result['status'] ?? 0);
            $body = (string) ($result['body'] ?? '');
            return array(
                'success' => false,
                'error' => 'API Error: HTTP ' . $status . ($body !== '' ? ' - ' . $body : '')
            );
        }

        return array('success' => true, 'data' => $result['data']);
    }
    
    /**
     * ✅ NEW: Make actual INSERT MANY API call to AYSA
     */
    private function make_insert_many_api_call($data, $api_key) {
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $result = Aysa_Auth_Manager::get_instance()->make_authenticated_request_with_meta(
            'api/Association/insertmany',
            'POST',
            $data
        );
        if (empty($result['ok'])) {
            $status = intval($result['status'] ?? 0);
            $body = (string) ($result['body'] ?? '');
            return array(
                'success' => false,
                'error' => 'API Error: HTTP ' . $status . ($body !== '' ? ' - ' . $body : '')
            );
        }
        return array('success' => true, 'data' => $result['data']);
    }
    
    /**
     * ✅ NEW: Handle redirect API call for categories if permalink changed
     */
    private function handle_category_redirect_if_needed($term_id, $term) {
        // Only handle redirects for existing categories (not new ones)
        if (!$term_id || !$term || is_wp_error($term)) {
            return;
        }
        
        // Skip if this is triggered by AJAX or autosave
        if (defined('DOING_AJAX') && DOING_AJAX) {
            return;
        }
        
        // ✅ FIXED: WordPress category edit forms use different submit button names
        // Check for category-specific submit buttons and form submissions
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $is_category_submit = isset($_POST['submit']) || // Generic submit
                             // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                             isset($_POST['save']) ||   // Save button
                             // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                             (isset($_POST['action']) && $_POST['action'] === 'editedtag') || // Category update action
                             // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                             (isset($_POST['action']) && $_POST['action'] === 'edit-tags') || // Category management
                             // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                             isset($_POST['tag-name']) || // Category name field indicates form submission
                             // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                             isset($_POST['name']); // Alternative name field
        
        if (!$is_category_submit) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Category ' . $term_id . ' - Not a form submission, skipping redirect check');
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        aysa_debug_log('AYSA REDIRECT: POST action: ' . (isset($_POST['action']) ? sanitize_key(wp_unslash($_POST['action'])) : 'not set'));
                // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
                aysa_debug_log('AYSA REDIRECT: POST keys: ' . implode(', ', array_keys($_POST)));
            }
            return;
        }
        
        // Get stored old permalink
        $old_permalink = get_term_meta($term_id, '_aysa_old_permalink', true);
        $current_permalink = get_term_link($term_id, $term->taxonomy);
        
        // Handle WP_Error for current permalink
        if (is_wp_error($current_permalink)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Could not get current permalink for term ' . $term_id . ': ' . $current_permalink->get_error_message());
            }
            return;
        }
        
        // ✅ ENHANCED: Debug logging with more details
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA REDIRECT: Category ' . $term_id . ' (' . $term->name . ') redirect check started');
            aysa_debug_log('AYSA REDIRECT: Category ' . $term_id . ' - Old: ' . ($old_permalink ?: 'EMPTY') . ' | Current: ' . $current_permalink);
            aysa_debug_log('AYSA REDIRECT: Category ' . $term_id . ' - Taxonomy: ' . $term->taxonomy);
            aysa_debug_log('AYSA REDIRECT: Category ' . $term_id . ' - Form submission detected: YES');
        }
        
        // If we have an old permalink and it's different from current, create redirect
        if (!empty($old_permalink) && $old_permalink !== $current_permalink) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: ✅ CREATING redirect for category ' . $term_id . ' (' . $term->name . ')');
                aysa_debug_log('AYSA REDIRECT: ✅ FROM: ' . $old_permalink);
                aysa_debug_log('AYSA REDIRECT: ✅ TO: ' . $current_permalink);
            }
            
            $redirect_result = $this->create_redirect_api_call($old_permalink, $current_permalink, 'category', $term_id);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                if ($redirect_result) {
                    aysa_debug_log('AYSA REDIRECT: ✅ Successfully created redirect for category ' . $term_id);
                } else {
                    aysa_debug_log('AYSA REDIRECT: ❌ Failed to create redirect for category ' . $term_id);
                }
            }
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                if (empty($old_permalink)) {
                    aysa_debug_log('AYSA REDIRECT: ⚪ No old permalink stored for category ' . $term_id . ' (new category or first save)');
                } else {
                    aysa_debug_log('AYSA REDIRECT: ⚪ No redirect needed for category ' . $term_id . ' (permalink unchanged)');
                }
            }
        }
        
        // Always update the stored permalink for next time
        update_term_meta($term_id, '_aysa_old_permalink', $current_permalink);
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA REDIRECT: Updated stored permalink for category ' . $term_id . ' to: ' . $current_permalink);
        }
    }
    
    /**
     * ✅ NEW: Handle redirect API call for products if permalink changed
     */
    private function handle_product_redirect_if_needed($post_id, $post) {
        // Only handle redirects for existing products (not new ones)
        if (!$post_id || !$post || $post->post_status === 'auto-draft') {
            return;
        }
        
        // Skip if this is triggered by AJAX or autosave
        if (defined('DOING_AJAX') && DOING_AJAX) {
            return;
        }
        
        // Only process published posts (not drafts)
        if ($post->post_status !== 'publish') {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Skipping redirect for product ' . $post_id . ' - post status: ' . $post->post_status);
            }
            return;
        }
        
        // Get stored old permalink
        $old_permalink = get_post_meta($post_id, '_aysa_old_permalink', true);
        $current_permalink = get_permalink($post_id);
        
        // Handle WP_Error for current permalink
        if (is_wp_error($current_permalink)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Could not get current permalink for post ' . $post_id . ': ' . $current_permalink->get_error_message());
            }
            return;
        }
        
        // Debug logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA REDIRECT: Product ' . $post_id . ' - Old: ' . $old_permalink . ' | Current: ' . $current_permalink);
        }
        
        // If we have an old permalink and it's different from current, create redirect
        if (!empty($old_permalink) && $old_permalink !== $current_permalink) {
            $this->create_redirect_api_call($old_permalink, $current_permalink, 'product', $post_id);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Created redirect for product ' . $post_id . ' from ' . $old_permalink . ' to ' . $current_permalink);
            }
        }
        
        // Always update the stored permalink for next time
        update_post_meta($post_id, '_aysa_old_permalink', $current_permalink);
    }
    
    /**
     * ✅ NEW: Handle redirect API call for pages if permalink changed
     */
    private function handle_page_redirect_if_needed($post_id, $post) {
        // Only handle redirects for existing pages (not new ones)
        if (!$post_id || !$post || $post->post_status === 'auto-draft') {
            return;
        }
        
        // Skip if this is triggered by AJAX or autosave
        if (defined('DOING_AJAX') && DOING_AJAX) {
            return;
        }
        
        // Only process published posts (not drafts)
        if ($post->post_status !== 'publish') {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Skipping redirect for page ' . $post_id . ' - post status: ' . $post->post_status);
            }
            return;
        }
        
        // Get stored old permalink
        $old_permalink = get_post_meta($post_id, '_aysa_old_permalink', true);
        $current_permalink = get_permalink($post_id);
        
        // Handle WP_Error for current permalink
        if (is_wp_error($current_permalink)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Could not get current permalink for post ' . $post_id . ': ' . $current_permalink->get_error_message());
            }
            return;
        }
        
        // Debug logging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA REDIRECT: Page ' . $post_id . ' - Old: ' . $old_permalink . ' | Current: ' . $current_permalink);
        }
        
        // If we have an old permalink and it's different from current, create redirect
        if (!empty($old_permalink) && $old_permalink !== $current_permalink) {
            $this->create_redirect_api_call($old_permalink, $current_permalink, 'page', $post_id);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Created redirect for page ' . $post_id . ' from ' . $old_permalink . ' to ' . $current_permalink);
            }
        }
        
        // Always update the stored permalink for next time
        update_post_meta($post_id, '_aysa_old_permalink', $current_permalink);
    }
    
    /**
     * ✅ NEW: Make API call to create redirect
     */
    private function create_redirect_api_call($old_url, $new_url, $entity_type, $entity_id) {
        // ✅ Use centralized API configuration
        if (!class_exists('Aysa_API_Config')) {
            require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
        }
        
        $endpoint = Aysa_API_Config::get_endpoint_url('create_redirect');
        
        if (!$endpoint) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT API: create_redirect endpoint not found in configuration');
            }
            return false;
        }
        
        // Get project ID
        $project_id = get_option('aysa_project_id', 0);
        if (!$project_id) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT API: No project ID configured');
            }
            return false;
        }
        
        // Prepare redirect data according to API DTO
        $redirect_data = array(
            'SourceUrl' => $old_url,
            'DestinationUrl' => $new_url,
            'Status' => 'Auto', // Auto-generated redirect
            'TotalBacklinks' => 0, // TODO: Could be enhanced with actual backlink count from SEO tools
            'ProjectId' => $project_id
        );
        
        // Prepare headers
        $headers = array(
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
            'Origin' => get_site_url()
        );
        
        // Prepare body
        $body = json_encode($redirect_data);
        
        // Debug logging with more details
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA REDIRECT API: Calling endpoint: ' . $endpoint);
            aysa_debug_log('AYSA REDIRECT API: Creating redirect from ' . $old_url . ' to ' . $new_url);
            aysa_debug_log('AYSA REDIRECT API: Request payload: ' . $body);
        }
        
        // Make HTTP request
        $response = wp_remote_post($endpoint, array(
            'headers' => $headers,
            'body' => $body,
            'timeout' => 30,
            'sslverify' => false // For development
        ));
        
        // Handle response
        if (is_wp_error($response)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT API: HTTP Error: ' . $response->get_error_message());
            }
            return false;
        }
        
        $response_code = wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);
        
        // Accept both 200 (OK) and 201 (Created) as success
        if (!in_array($response_code, [200, 201])) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT API: HTTP ' . $response_code . ' - ' . $response_body);
            }
            return false;
        }
        
        $decoded_response = json_decode($response_body, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT API: JSON Decode Error: ' . json_last_error_msg());
            }
            return false;
        }
        
        // Store successful redirect creation
        $redirect_meta = array(
            'source_url' => $old_url,
            'destination_url' => $new_url,
            'status' => 'Auto',
            'entity_type' => $entity_type,
            'entity_id' => $entity_id,
            'api_response' => $decoded_response,
            'created_at' => current_time('mysql')
        );
        
        if ($entity_type === 'category') {
            update_term_meta($entity_id, '_aysa_last_redirect', $redirect_meta);
        } else {
            update_post_meta($entity_id, '_aysa_last_redirect', $redirect_meta);
        }
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA REDIRECT API: Successfully created redirect');
        }
        
        // ✅ NEW: Also add redirect to .htaccess file
        $this->add_redirect_to_htaccess($old_url, $new_url);
        
        return true;
    }
    
    /**
     * ✅ NEW: Add redirect to .htaccess file
     */
    private function add_redirect_to_htaccess($old_url, $new_url) {
        // Get relative paths for .htaccess
        $site_url = get_site_url();
        $source_relative = str_replace($site_url, '', $old_url);
        $dest_relative = str_replace($site_url, '', $new_url);
        
        // Clean up leading slashes
        $source_relative = ltrim($source_relative, '/');
        $dest_relative = ltrim($dest_relative, '/');
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA HTACCESS: Adding redirect from ' . $source_relative . ' to ' . $dest_relative);
        }
        
        // Get .htaccess file path
        $htaccess_file = ABSPATH . '.htaccess';
        
        // Check if .htaccess exists and is writable
        if (!file_exists($htaccess_file)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA HTACCESS: .htaccess file does not exist at ' . $htaccess_file);
            }
            return false;
        }
        
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable -- Admin-authorized WordPress-root file operations retain a fail-closed writability preflight before the guarded write.
        if (!is_writable($htaccess_file)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA HTACCESS: .htaccess file is not writable');
            }
            return false;
        }
        
        // Read current .htaccess content
        $htaccess_content = file_get_contents($htaccess_file);
        
        // Create redirect rule
        $redirect_rule = "Redirect 301 /{$source_relative} /{$dest_relative}";
        
        // Check if redirect already exists
        if (strpos($htaccess_content, $redirect_rule) !== false) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA HTACCESS: Redirect rule already exists');
            }
            return true;
        }
        
        // Find WordPress section or add at the beginning
        $aysa_section_start = "# BEGIN AYSA Redirects\n";
        $aysa_section_end = "# END AYSA Redirects\n";
        
        if (strpos($htaccess_content, $aysa_section_start) !== false) {
            // AYSA section exists, add to it
            $pattern = '/(' . preg_quote($aysa_section_start, '/') . ')(.*?)(' . preg_quote($aysa_section_end, '/') . ')/s';
            
            $replacement = function($matches) use ($redirect_rule) {
                return $matches[1] . $matches[2] . $redirect_rule . "\n" . $matches[3];
            };
            
            $htaccess_content = preg_replace_callback($pattern, $replacement, $htaccess_content);
        } else {
            // Create new AYSA section at the beginning
            $aysa_section = $aysa_section_start . $redirect_rule . "\n" . $aysa_section_end . "\n";
            $htaccess_content = $aysa_section . $htaccess_content;
        }
        
        // Write back to .htaccess
        // phpcs:ignore PluginCheck.CodeAnalysis.WriteFile.ABSPATHDetected -- Admin-authorized redirect operation intentionally updates site-root .htaccess after nonce, capability, path and writability checks.
        $result = file_put_contents($htaccess_file, $htaccess_content);
        
        if ($result !== false) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA HTACCESS: Successfully added redirect rule');
            }
            return true;
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA HTACCESS: Failed to write to .htaccess file');
            }
            return false;
        }
    }
    
    /**
     * ✅ NEW: AJAX handler for adding redirects to .htaccess
     */
    public function handle_add_htaccess_redirect() {
        // Verify nonce if provided
        if (isset($_POST['nonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(['message' => 'Security check failed']);
            return;
        }
        
        // Check user capabilities
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Insufficient permissions']);
            return;
        }
        
        $source = isset($_POST['source']) ? sanitize_text_field(wp_unslash($_POST['source'])) : '';
        $destination = isset($_POST['destination']) ? sanitize_text_field(wp_unslash($_POST['destination'])) : '';
        
        if (empty($source) || empty($destination)) {
            wp_send_json_error(['message' => 'Source and destination are required']);
            return;
        }
        
        // Build full URLs
        $site_url = get_site_url();
        $source_url = $site_url . $source;
        $dest_url = $site_url . $destination;
        
        // Add to .htaccess
        $result = $this->add_redirect_to_htaccess($source_url, $dest_url);
        
        if ($result) {
            wp_send_json_success(['message' => 'Redirect added to .htaccess successfully']);
        } else {
            wp_send_json_error(['message' => 'Failed to add redirect to .htaccess']);
        }
    }
    
    /**
     * ✅ NEW: Automatically upsert category to API when SEO data is saved
     */
    public function auto_upsert_category_to_api($term_id, $keyword_data, $term) {
        // Only proceed if we have sufficient data
        if (empty($keyword_data['keyword']) && empty($keyword_data['meta_title'])) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AUTO UPSERT: Skipping API call - no meaningful SEO data for term ' . $term_id);
            }
            return;
        }
        
        // Get API key and project ID
        $api_key = get_option('aysa_api_key', '');
        $project_id = get_option('aysa_project_id', 0);
        
        if (empty($api_key) || !$project_id) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AUTO UPSERT: Missing API key or project ID for term ' . $term_id);
            }
            return;
        }
        
        // Check if we should make the API call (avoid duplicate calls)
        $last_upsert = get_term_meta($term_id, '_aysa_last_upsert', true);
        $current_time = current_time('timestamp');
        
        // Allow API calls (removed 60 second restriction)
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA AUTO UPSERT: Starting automatic API upsert for category ' . $term_id . ' (' . $term->name . ')');
        }
        
        // Prepare upsert data
        $upsert_data = array(
            'Id' => null, // New association
            'IdIntern' => $term_id,
            'AssociatedOn' => 'Category',
            'Value' => $term->name,
            'Link' => get_term_link($term_id),
            'Keyword' => isset($keyword_data['keyword']) ? $keyword_data['keyword'] : '',
            'ProjectId' => $project_id,
            'IsMain' => true,
            
            // SEO fields
            'MetaTitle' => isset($keyword_data['meta_title']) ? $keyword_data['meta_title'] : '',
            'MetaDescription' => isset($keyword_data['meta_description']) ? $keyword_data['meta_description'] : '',
            'MetaKeywords' => isset($keyword_data['meta_keywords']) ? $keyword_data['meta_keywords'] : '',
            
            // Secondary keywords
            'SecondKeyword' => isset($keyword_data['second_keyword']) ? $keyword_data['second_keyword'] : '',
            
            // Tracking
            'IsTracked' => isset($keyword_data['track']) ? (bool)$keyword_data['track'] : false
        );
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA AUTO UPSERT: Data: ' . aysa_debug_export($upsert_data, true));
        }
        
        // Make API call
        $api_result = $this->make_upsert_api_call($upsert_data, $api_key);
        
        if ($api_result['success']) {
            // Store successful API response
            update_term_meta($term_id, '_aysa_last_upsert', array(
                'timestamp' => current_time('timestamp'),
                'data' => $upsert_data,
                'response' => $api_result['data'],
                'source' => 'auto_upsert'
            ));
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AUTO UPSERT: SUCCESS for category ' . $term_id . ' - API response: ' . aysa_debug_export($api_result['data'], true));
            }
            
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA AUTO UPSERT: FAILED for category ' . $term_id . ' - Error: ' . $api_result['error']);
            }
        }
    }

    /**
     * ✅ NEW: Store initial permalink for new categories
     */
    public function store_initial_category_permalink($term_id, $tt_id) {
        // Get the permalink for the new category
        $permalink = get_term_link($term_id);
        
        if (!is_wp_error($permalink)) {
            update_term_meta($term_id, '_aysa_old_permalink', $permalink);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Stored initial permalink for new category ' . $term_id . ': ' . $permalink);
            }
        }
    }
    
    /**
     * ✅ NEW: Store initial permalink for new posts/products/pages
     */
    public function store_initial_post_permalink($post_id, $post, $update) {
        // Only handle products and pages
        if (!in_array($post->post_type, ['product', 'page'])) {
            return;
        }
        
        // Only for new posts (not updates)
        if ($update) {
            return;
        }
        
        // Only for published posts
        if ($post->post_status !== 'publish') {
            return;
        }
        
        // Get the permalink for the new post
        $permalink = get_permalink($post_id);
        
        if (!is_wp_error($permalink)) {
            update_post_meta($post_id, '_aysa_old_permalink', $permalink);
            
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA REDIRECT: Stored initial permalink for new ' . $post->post_type . ' ' . $post_id . ': ' . $permalink);
            }
        }
    }
    
    /**
     * ✅ NEW: AJAX handler for getting entity details (name and URL)
     */
    public function handle_get_entity_details() {
        // Verify nonce if provided
        if (isset($_POST['nonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error(['message' => 'Security check failed']);
            return;
        }
        
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field(wp_unslash($_POST['entity_type'])) : '';
        $entity_id = isset($_POST['entity_id']) ? intval(wp_unslash($_POST['entity_id'])) : 0;
        
        if (empty($entity_type) || !$entity_id) {
            wp_send_json_error(['message' => 'Entity type and ID are required']);
            return;
        }
        
        $entity_details = $this->get_entity_details($entity_type, $entity_id);
        
        if ($entity_details) {
            wp_send_json_success($entity_details);
        } else {
            wp_send_json_error(['message' => 'Entity not found or invalid']);
        }
    }

    /**
     * Return WordPress entities inventory so UI can show "entities without keywords".
     */
    public function handle_get_entities_inventory() {
        check_ajax_referer('aysa_metabox_nonce', 'nonce');

        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Insufficient permissions'));
            return;
        }

        $inventory = $this->collect_entities_inventory();
        wp_send_json_success(array('inventory' => $inventory));
    }

    /**
     * Build inventory grouped for on-page UX tabs: product/category/article/other.
     */
    private function collect_entities_inventory() {
        $inventory = array(
            'product' => array(),
            'category' => array(),
            'article' => array(),
            'other' => array()
        );

        // Products
        $product_ids = get_posts(array(
            'post_type' => 'product',
            'post_status' => array('publish'),
            'posts_per_page' => -1,
            'fields' => 'ids',
            'orderby' => 'ID',
            'order' => 'ASC',
            'no_found_rows' => true
        ));
        foreach ($product_ids as $product_id) {
            $post_obj = get_post($product_id);
            $quality = $this->get_or_update_entity_quality_cache('post', (int) $product_id, '', array(
                'name' => get_the_title($product_id),
                'type' => 'Product',
                'id' => (int) $product_id
            ));
            $terms = get_the_terms($product_id, 'product_cat');
            $cat_names = array();
            if (is_array($terms)) {
                foreach ($terms as $term) {
                    if (!empty($term->name)) {
                        $cat_names[] = $term->name;
                    }
                }
            }
            $inventory['product'][] = array(
                'id' => (int) $product_id,
                'name' => get_the_title($product_id),
                'url' => get_permalink($product_id),
                'edit_url' => get_edit_post_link($product_id),
                'source_type' => 'product',
                'slug' => $post_obj ? $post_obj->post_name : '',
                'excerpt' => $post_obj ? wp_strip_all_tags((string) $post_obj->post_excerpt) : '',
                'content' => $post_obj ? wp_strip_all_tags((string) $post_obj->post_content) : '',
                'categories' => $cat_names,
                'seo_score' => isset($quality['seo_score']) ? (int) $quality['seo_score'] : null,
                'seo_score_updated_at' => $quality['seo_score_updated_at'] ?? null,
                'content_source' => $quality['content_source'] ?? 'unknown'
            );
        }

        // Product categories + normal categories
        $taxonomies = array('product_cat', 'category');
        foreach ($taxonomies as $taxonomy) {
            $terms = get_terms(array(
                'taxonomy' => $taxonomy,
                'hide_empty' => false
            ));

            if (is_wp_error($terms) || empty($terms)) {
                continue;
            }

            foreach ($terms as $term) {
                $term_url = get_term_link($term);
                if (is_wp_error($term_url)) {
                    $term_url = '';
                }
                $quality = $this->get_or_update_entity_quality_cache('term', (int) $term->term_id, (string) $taxonomy, array(
                    'name' => $term->name,
                    'type' => 'Category',
                    'id' => (int) $term->term_id
                ));
                $inventory['category'][] = array(
                    'id' => (int) $term->term_id,
                    'name' => $term->name,
                    'url' => $term_url,
                    'edit_url' => get_edit_term_link($term->term_id, $taxonomy),
                    'source_type' => $taxonomy,
                    'slug' => (string) $term->slug,
                    'excerpt' => wp_strip_all_tags((string) $term->description),
                    'content' => wp_strip_all_tags((string) $term->description),
                    'categories' => array(),
                    'seo_score' => isset($quality['seo_score']) ? (int) $quality['seo_score'] : null,
                    'seo_score_updated_at' => $quality['seo_score_updated_at'] ?? null,
                    'content_source' => $quality['content_source'] ?? 'unknown'
                );
            }
        }

        // Blog posts (articles)
        $article_ids = get_posts(array(
            'post_type' => 'post',
            'post_status' => array('publish'),
            'posts_per_page' => -1,
            'fields' => 'ids',
            'orderby' => 'ID',
            'order' => 'ASC',
            'no_found_rows' => true
        ));
        foreach ($article_ids as $article_id) {
            $post_obj = get_post($article_id);
            $quality = $this->get_or_update_entity_quality_cache('post', (int) $article_id, '', array(
                'name' => get_the_title($article_id),
                'type' => 'Post',
                'id' => (int) $article_id
            ));
            $terms = get_the_terms($article_id, 'category');
            $cat_names = array();
            if (is_array($terms)) {
                foreach ($terms as $term) {
                    if (!empty($term->name)) {
                        $cat_names[] = $term->name;
                    }
                }
            }
            $inventory['article'][] = array(
                'id' => (int) $article_id,
                'name' => get_the_title($article_id),
                'url' => get_permalink($article_id),
                'edit_url' => get_edit_post_link($article_id),
                'source_type' => 'post',
                'slug' => $post_obj ? $post_obj->post_name : '',
                'excerpt' => $post_obj ? wp_strip_all_tags((string) $post_obj->post_excerpt) : '',
                'content' => $post_obj ? wp_strip_all_tags((string) $post_obj->post_content) : '',
                'categories' => $cat_names,
                'seo_score' => isset($quality['seo_score']) ? (int) $quality['seo_score'] : null,
                'seo_score_updated_at' => $quality['seo_score_updated_at'] ?? null,
                'content_source' => $quality['content_source'] ?? 'unknown'
            );
        }

        // "Other entities": pages + custom public post types (except system)
        $other_post_types = get_post_types(array('public' => true), 'names');
        $excluded_types = array('product', 'post', 'attachment', 'revision', 'nav_menu_item');
        foreach ($other_post_types as $post_type) {
            if (in_array($post_type, $excluded_types, true)) {
                continue;
            }

            $post_ids = get_posts(array(
                'post_type' => $post_type,
                'post_status' => array('publish'),
                'posts_per_page' => -1,
                'fields' => 'ids',
                'orderby' => 'ID',
                'order' => 'ASC',
                'no_found_rows' => true
            ));

            foreach ($post_ids as $post_id) {
                $post_obj = get_post($post_id);
                $quality = $this->get_or_update_entity_quality_cache('post', (int) $post_id, '', array(
                    'name' => get_the_title($post_id),
                    'type' => (string) $post_type,
                    'id' => (int) $post_id
                ));
                $inventory['other'][] = array(
                    'id' => (int) $post_id,
                    'name' => get_the_title($post_id),
                    'url' => get_permalink($post_id),
                    'edit_url' => get_edit_post_link($post_id),
                    'source_type' => $post_type,
                    'slug' => $post_obj ? $post_obj->post_name : '',
                    'excerpt' => $post_obj ? wp_strip_all_tags((string) $post_obj->post_excerpt) : '',
                    'content' => $post_obj ? wp_strip_all_tags((string) $post_obj->post_content) : '',
                    'categories' => array(),
                    'seo_score' => isset($quality['seo_score']) ? (int) $quality['seo_score'] : null,
                    'seo_score_updated_at' => $quality['seo_score_updated_at'] ?? null,
                    'content_source' => $quality['content_source'] ?? 'unknown'
                );
            }
        }

        return $inventory;
    }
    
    /**
     * ✅ NEW: Get entity details (name, URL, edit URL) based on type and ID
     */
    private function get_entity_details($entity_type, $entity_id) {
        $details = array(
            'name' => '',
            'url' => '',
            'edit_url' => '',
            'type' => $entity_type,
            'id' => $entity_id,
            'seo_score' => null,
            'seo_score_updated_at' => null,
            'content_source' => 'unknown'
        );
        $meta_target = array('storage' => '', 'id' => 0, 'taxonomy' => '');
        
        switch (strtolower($entity_type)) {
            case 'product':
                $product = wc_get_product($entity_id);
                if ($product) {
                    $details['name'] = $product->get_name();
                    $details['url'] = $product->get_permalink();
                    $details['edit_url'] = get_edit_post_link($entity_id);
                    $meta_target = array('storage' => 'post', 'id' => (int) $entity_id, 'taxonomy' => '');
                } else {
                    return false;
                }
                break;
                
            case 'category':
                $term = get_term($entity_id, 'product_cat');
                $taxonomy = 'product_cat';
                if (!$term || is_wp_error($term)) {
                    $term = get_term($entity_id, 'category');
                    $taxonomy = 'category';
                }
                if ($term && !is_wp_error($term)) {
                    $details['name'] = $term->name;
                    $details['url'] = get_term_link($term);
                    $details['edit_url'] = get_edit_term_link($entity_id, $taxonomy);
                    $meta_target = array('storage' => 'term', 'id' => (int) $entity_id, 'taxonomy' => $taxonomy);
                } else {
                    return false;
                }
                break;

            case 'article':
                $post = get_post($entity_id);
                if ($post && $post->post_type === 'post') {
                    $details['name'] = $post->post_title;
                    $details['url'] = get_permalink($entity_id);
                    $details['edit_url'] = get_edit_post_link($entity_id);
                    $meta_target = array('storage' => 'post', 'id' => (int) $entity_id, 'taxonomy' => '');
                } else {
                    return false;
                }
                break;

            case 'other':
                $post = get_post($entity_id);
                if ($post && !in_array($post->post_type, array('attachment', 'revision', 'nav_menu_item'), true)) {
                    $details['name'] = $post->post_title;
                    $details['url'] = get_permalink($entity_id);
                    $details['edit_url'] = get_edit_post_link($entity_id);
                    $meta_target = array('storage' => 'post', 'id' => (int) $entity_id, 'taxonomy' => '');
                } else {
                    return false;
                }
                break;
                
            case 'page':
                $page = get_post($entity_id);
                if ($page && $page->post_type === 'page') {
                    $details['name'] = $page->post_title;
                    $details['url'] = get_permalink($entity_id);
                    $details['edit_url'] = get_edit_post_link($entity_id);
                    $meta_target = array('storage' => 'post', 'id' => (int) $entity_id, 'taxonomy' => '');
                } else {
                    return false;
                }
                break;
                
            case 'post':
                $post = get_post($entity_id);
                if ($post && $post->post_type === 'post') {
                    $details['name'] = $post->post_title;
                    $details['url'] = get_permalink($entity_id);
                    $details['edit_url'] = get_edit_post_link($entity_id);
                    $meta_target = array('storage' => 'post', 'id' => (int) $entity_id, 'taxonomy' => '');
                } else {
                    return false;
                }
                break;
                
            default:
                // Fallback for legacy/variant entity type naming from API payloads.
                $post = get_post($entity_id);
                if ($post && !in_array($post->post_type, array('attachment', 'revision', 'nav_menu_item'), true)) {
                    $details['name'] = $post->post_title;
                    $details['url'] = get_permalink($entity_id);
                    $details['edit_url'] = get_edit_post_link($entity_id);
                    $meta_target = array('storage' => 'post', 'id' => (int) $entity_id, 'taxonomy' => '');
                    break;
                }
                $term = get_term($entity_id, 'product_cat');
                $taxonomy = 'product_cat';
                if (!$term || is_wp_error($term)) {
                    $term = get_term($entity_id, 'category');
                    $taxonomy = 'category';
                }
                if ($term && !is_wp_error($term)) {
                    $details['name'] = $term->name;
                    $details['url'] = get_term_link($term);
                    $details['edit_url'] = get_edit_term_link($entity_id, $taxonomy);
                    $meta_target = array('storage' => 'term', 'id' => (int) $entity_id, 'taxonomy' => $taxonomy);
                    break;
                }
                return false;
        }

        if (!empty($meta_target['storage']) && !empty($meta_target['id'])) {
            $quality = $this->get_or_update_entity_quality_cache($meta_target['storage'], (int) $meta_target['id'], (string) ($meta_target['taxonomy'] ?? ''), $details);
            if (is_array($quality)) {
                $details['seo_score'] = isset($quality['seo_score']) ? (int) $quality['seo_score'] : null;
                $details['seo_score_updated_at'] = $quality['seo_score_updated_at'] ?? null;
                $details['content_source'] = $quality['content_source'] ?? 'unknown';
            }
        }

        return $details;
    }

    /**
     * Returns cached entity quality data; refreshes every 12h.
     */
    private function get_or_update_entity_quality_cache($storage, $entity_id, $taxonomy, $entity_details = array()) {
        $cache_ttl = 12 * HOUR_IN_SECONDS;
        $cache_ts = intval($this->get_entity_meta_value($storage, $entity_id, '_aysa_entity_seo_score_cached_at', $taxonomy));
        $cache_source = (string) $this->get_entity_meta_value($storage, $entity_id, '_aysa_entity_content_source_cache', $taxonomy);
        $cache_score = $this->get_entity_meta_value($storage, $entity_id, '_aysa_entity_seo_score_cache', $taxonomy);
        $cache_score = is_numeric($cache_score) ? (int) round(floatval($cache_score)) : null;
        $now = time();

        if ($cache_ts > 0 && ($now - $cache_ts) < $cache_ttl && $cache_score !== null && $cache_source !== '') {
            // Fast correction path: if cache says original, verify history in case older logic misclassified.
            if ($cache_source === 'original') {
                // If AYSA metabox fields exist, force generated source even when legacy cache says original.
                if ($this->has_aysa_metabox_signal($storage, $entity_id, $taxonomy)) {
                    $cache_source = 'aysa_generated';
                    $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_content_source_cache', $cache_source, $taxonomy);
                }

                $history_signal = $this->get_entity_history_generation_signal($storage, $entity_id, $taxonomy, $entity_details);
                if ($history_signal === 'aysa_generated') {
                    $cache_source = 'aysa_generated';
                    $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_content_source_cache', $cache_source, $taxonomy);
                }
            }

            // Reverse correction path: legacy logic could mark entities as generated only because
            // keyword association fields existed. If that signal is no longer valid, recompute.
            if ($cache_source === 'aysa_generated') {
                $has_strong_signal = $this->has_aysa_metabox_signal($storage, $entity_id, $taxonomy);
                $history_signal = $this->get_entity_history_generation_signal($storage, $entity_id, $taxonomy, $entity_details);
                if (!$has_strong_signal && $history_signal !== 'aysa_generated') {
                    $computed = $this->compute_entity_quality_metrics($storage, $entity_id, $taxonomy, $entity_details);
                    $cache_source = isset($computed['content_source']) ? (string) $computed['content_source'] : 'unknown';
                    $cache_score = isset($computed['seo_score']) ? (int) $computed['seo_score'] : $cache_score;
                    $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_content_source_cache', $cache_source, $taxonomy);
                    $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_seo_score_cache', $cache_score, $taxonomy);
                    $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_seo_score_cached_at', $now, $taxonomy);
                    $cache_ts = $now;
                }
            }
            return array(
                'seo_score' => $cache_score,
                'seo_score_updated_at' => gmdate('c', $cache_ts),
                'content_source' => $cache_source
            );
        }

        $computed = $this->compute_entity_quality_metrics($storage, $entity_id, $taxonomy, $entity_details);
        $new_score = isset($computed['seo_score']) ? (int) $computed['seo_score'] : 0;
        $new_source = isset($computed['content_source']) ? (string) $computed['content_source'] : 'unknown';

        $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_seo_score_cache', $new_score, $taxonomy);
        $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_content_source_cache', $new_source, $taxonomy);
        $this->set_entity_meta_value($storage, $entity_id, '_aysa_entity_seo_score_cached_at', $now, $taxonomy);

        return array(
            'seo_score' => $new_score,
            'seo_score_updated_at' => gmdate('c', $now),
            'content_source' => $new_source
        );
    }

    private function compute_entity_quality_metrics($storage, $entity_id, $taxonomy, $entity_details = array()) {
        $title = '';
        $content = '';
        $meta_title = '';
        $meta_description = '';
        $keywords = '';

        if ($storage === 'term') {
            $term = get_term($entity_id, $taxonomy ?: '');
            if ($term && !is_wp_error($term)) {
                $title = (string) $term->name;
                $content = (string) $term->description;
            }
            $meta_title = (string) get_term_meta($entity_id, '_aysa_seo_title', true);
            if ($meta_title === '') {
                $meta_title = (string) get_term_meta($entity_id, '_aysa_meta_title', true);
            }
            $meta_description = (string) get_term_meta($entity_id, '_aysa_seo_description', true);
            if ($meta_description === '') {
                $meta_description = (string) get_term_meta($entity_id, '_aysa_meta_description', true);
            }
            $keywords = (string) get_term_meta($entity_id, '_aysa_seo_keywords', true);
            if ($keywords === '') {
                $keywords = (string) get_term_meta($entity_id, '_aysa_meta_keywords', true);
            }
        } else {
            $post = get_post($entity_id);
            if ($post) {
                $title = (string) $post->post_title;
                $content = (string) $post->post_content;
            }
            $meta_title = (string) get_post_meta($entity_id, '_aysa_seo_title', true);
            if ($meta_title === '') {
                $meta_title = (string) get_post_meta($entity_id, '_aysa_meta_title', true);
            }
            $meta_description = (string) get_post_meta($entity_id, '_aysa_seo_description', true);
            if ($meta_description === '') {
                $meta_description = (string) get_post_meta($entity_id, '_aysa_meta_description', true);
            }
            $keywords = (string) get_post_meta($entity_id, '_aysa_seo_keywords', true);
            if ($keywords === '') {
                $keywords = (string) get_post_meta($entity_id, '_aysa_meta_keywords', true);
            }
        }

        $title = wp_strip_all_tags((string) $title);
        $content_plain = wp_strip_all_tags((string) $content);
        $content_len = function_exists('mb_strlen') ? mb_strlen($content_plain) : strlen($content_plain);
        $meta_title_len = function_exists('mb_strlen') ? mb_strlen((string) $meta_title) : strlen((string) $meta_title);
        $meta_desc_len = function_exists('mb_strlen') ? mb_strlen((string) $meta_description) : strlen((string) $meta_description);
        $main_keyword = '';
        if ($keywords !== '') {
            $parts = array_filter(array_map('trim', explode(',', $keywords)));
            $main_keyword = !empty($parts) ? (string) $parts[0] : '';
        }

        $title_lower = function_exists('mb_strtolower') ? mb_strtolower($title) : strtolower($title);
        $content_lower = function_exists('mb_strtolower') ? mb_strtolower($content_plain) : strtolower($content_plain);
        $main_kw_lower = function_exists('mb_strtolower') ? mb_strtolower($main_keyword) : strtolower($main_keyword);

        $score = 0;
        if ($meta_title_len > 0) $score += 20;
        if ($meta_title_len >= 30 && $meta_title_len <= 65) $score += 10;
        if ($meta_desc_len > 0) $score += 20;
        if ($meta_desc_len >= 120 && $meta_desc_len <= 165) $score += 10;
        if ($content_len >= 300) $score += 15;
        if ($content_len >= 900) $score += 10;
        if ($main_kw_lower !== '' && strpos($title_lower, $main_kw_lower) !== false) $score += 10;
        if ($main_kw_lower !== '' && strpos($content_lower, $main_kw_lower) !== false) $score += 5;

        $score = max(0, min(100, (int) round($score)));
        $content_source = $this->detect_entity_content_source($storage, $entity_id, $content, $meta_title, $meta_description, $taxonomy, $entity_details);

        return array(
            'seo_score' => $score,
            'content_source' => $content_source
        );
    }

    private function detect_entity_content_source($storage, $entity_id, $content, $meta_title, $meta_description, $taxonomy = '', $entity_details = array()) {
        // 1) Highest-priority signal: AYSA metabox payload exists.
        // Requested behavior: if AYSA box has content, mark as AYSA generated.
        if ($this->has_aysa_metabox_signal($storage, $entity_id, $taxonomy, $meta_title, $meta_description)) {
            return 'aysa_generated';
        }

        // 2) Primary signal: AYSA history entries with generated/custom output (applied preferred).
        $history_signal = $this->get_entity_history_generation_signal($storage, $entity_id, $taxonomy, $entity_details);
        if ($history_signal === 'aysa_generated') {
            return 'aysa_generated';
        }

        // 3) Secondary signal: explicit content/meta markers.
        $content_text = (string) $content;
        $marker_found = stripos($content_text, 'AYSA GENERATED CONTENT') !== false ||
            stripos($content_text, '<!-- AYSA') !== false ||
            stripos($content_text, 'data-aysa') !== false;

        if ($storage === 'term') {
            $generated_meta = get_term_meta($entity_id, '_aysa_generated_content', true);
            $seo_content_meta = get_term_meta($entity_id, '_aysa_seo_content', true);
        } else {
            $generated_meta = get_post_meta($entity_id, '_aysa_generated_content', true);
            $seo_content_meta = get_post_meta($entity_id, '_aysa_seo_content', true);
        }

        $has_generated_flag = !empty($generated_meta) || !empty($seo_content_meta);
        if ($has_generated_flag || $marker_found) {
            return 'aysa_generated';
        }

        // 4) If history has only original/manual entries, trust that.
        if ($history_signal === 'original') {
            return 'original';
        }

        $has_any_seo = ((string) $meta_title !== '') || ((string) $meta_description !== '');
        if ($has_any_seo || trim(wp_strip_all_tags($content_text)) !== '') {
            return 'original';
        }

        return 'unknown';
    }

    private function has_aysa_metabox_signal($storage, $entity_id, $taxonomy = '', $meta_title = '', $meta_description = '') {
        $get_meta = function($key) use ($storage, $entity_id) {
            if ($storage === 'term') {
                return get_term_meta($entity_id, $key, true);
            }
            return get_post_meta($entity_id, $key, true);
        };

        // Strong AYSA-generation signals persisted by metabox.
        // Important: keyword association fields are intentionally excluded, because
        // keyword save != generated content.
        $meta_keys = array(
            '_aysa_generated_content',
            '_aysa_seo_content',
            '_aysa_generated_h1',
            '_aysa_generated_meta_title',
            '_aysa_generated_meta_description',
            'blog_content_outline',
            'blog_content_tldr',
            'blog_content_introduction',
            'blog_content_extended',
            'blog_content_faq',
            'blog_content_conclusion'
        );

        foreach ($meta_keys as $meta_key) {
            $value = $get_meta($meta_key);
            if (is_array($value)) {
                if (!empty(array_filter($value, function($v) { return trim((string) $v) !== ''; }))) {
                    return true;
                }
                continue;
            }
            if (trim((string) $value) !== '') {
                return true;
            }
        }

        // Explicit generated marker (if set by content pipeline).
        $generated_flag = $get_meta('_aysa_is_generated');
        if ((string) $generated_flag === '1') {
            return true;
        }

        return false;
    }

    /**
     * Inspect AYSA SEO history table to infer generated vs original content source.
     * Returns: 'aysa_generated' | 'original' | 'unknown'
     */
    private function get_entity_history_generation_signal($storage, $entity_id, $taxonomy = '', $entity_details = array()) {
        global $wpdb;
        if (!isset($wpdb) || !is_object($wpdb)) {
            return 'unknown';
        }

        $table_name = esc_sql($wpdb->prefix . 'aysa_seo_history');
        $like_table = $wpdb->esc_like($table_name);
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $like_table));
        if ($table_exists !== $table_name) {
            return 'unknown';
        }

        $entity_type_variants = array();
        $raw_type = isset($entity_details['type']) ? (string) $entity_details['type'] : '';
        if ($raw_type !== '') {
            $entity_type_variants[] = $raw_type;
            $entity_type_variants[] = strtolower($raw_type);
            $entity_type_variants[] = ucfirst(strtolower($raw_type));
        }

        if ($storage === 'term') {
            $entity_type_variants[] = 'Category';
            $entity_type_variants[] = 'category';
            if (!empty($taxonomy)) {
                $entity_type_variants[] = (string) $taxonomy;
            }
            if ($taxonomy === 'product_cat') {
                $entity_type_variants[] = 'product_category';
                $entity_type_variants[] = 'ProductCategory';
            }
        } else {
            $post_obj = get_post($entity_id);
            if ($post_obj && !empty($post_obj->post_type)) {
                $post_type = (string) $post_obj->post_type;
                $entity_type_variants[] = $post_type;
                $entity_type_variants[] = ucfirst($post_type);
                if ($post_type === 'post') {
                    $entity_type_variants[] = 'Article';
                    $entity_type_variants[] = 'article';
                }
            }
            $entity_type_variants[] = 'Post';
            $entity_type_variants[] = 'post';
            $entity_type_variants[] = 'Page';
            $entity_type_variants[] = 'page';
            $entity_type_variants[] = 'Product';
            $entity_type_variants[] = 'product';
            $entity_type_variants[] = 'Other';
            $entity_type_variants[] = 'other';
        }

        $entity_type_variants = array_values(array_unique(array_filter(array_map('strval', $entity_type_variants))));
        if (empty($entity_type_variants)) {
            return 'unknown';
        }

        $placeholders = implode(',', array_fill(0, count($entity_type_variants), '%s'));
        $sql = "SELECT generator_type, is_applied
                FROM {$table_name}
                WHERE entity_id = %d
                  AND entity_type IN ({$placeholders})
                ORDER BY is_applied DESC, id DESC
                LIMIT 5";

        $prepare_args = array_merge(array((int) $entity_id), $entity_type_variants);
        $prepared = call_user_func_array(array($wpdb, 'prepare'), array_merge(array($sql), $prepare_args));
        if (empty($prepared)) {
            return 'unknown';
        }

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query: table identifiers and structure are internal, and request-derived values are prepared before execution. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $rows = $wpdb->get_results($prepared);
        if (empty($rows) || !is_array($rows)) {
            return 'unknown';
        }

        $has_original = false;
        foreach ($rows as $row) {
            $generator = strtolower((string) ($row->generator_type ?? ''));
            if (in_array($generator, array('new', 'custom', 'ai', 'automated'), true)) {
                return 'aysa_generated';
            }
            if ($generator === 'original') {
                $has_original = true;
            }
        }

        return $has_original ? 'original' : 'unknown';
    }

    private function get_entity_meta_value($storage, $entity_id, $key, $taxonomy = '') {
        if ($storage === 'term') {
            return get_term_meta($entity_id, $key, true);
        }
        return get_post_meta($entity_id, $key, true);
    }

    private function set_entity_meta_value($storage, $entity_id, $key, $value, $taxonomy = '') {
        if ($storage === 'term') {
            return update_term_meta($entity_id, $key, $value);
        }
        return update_post_meta($entity_id, $key, $value);
    }

    private function invalidate_entity_quality_cache($storage, $entity_id, $taxonomy = '') {
        if ($storage === 'term') {
            delete_term_meta($entity_id, '_aysa_entity_seo_score_cache');
            delete_term_meta($entity_id, '_aysa_entity_content_source_cache');
            delete_term_meta($entity_id, '_aysa_entity_seo_score_cached_at');
            return;
        }
        delete_post_meta($entity_id, '_aysa_entity_seo_score_cache');
        delete_post_meta($entity_id, '_aysa_entity_content_source_cache');
        delete_post_meta($entity_id, '_aysa_entity_seo_score_cached_at');
    }

    /**
     * Add metabox for normal WordPress categories - edit form (existing categories)
     */
    public function add_normal_cat_metabox($term)
    {
        // Get the category ID
        $term_id = $term->term_id;
        
        // Get stored keyword data
        $keyword_data = get_term_meta($term_id, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }
        
        // Get project ID from options
        $project_id = get_option('aysa_project_id', 0);
        
        // Enqueue and localize script with safe entityUrl
        wp_enqueue_script('aysa_product_metabox_admin-js');
        $term_link = get_term_link($term_id, 'category');
        if ( is_wp_error( $term_link ) ) {
            $term_link = '';
        }
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'termId'     => $term_id,
                'entityType' => 'term',
                'entityUrl'  => $term_link,
                'project_id' => $project_id,
                'nonce'      => wp_create_nonce('aysa_metabox_nonce'),
                'ajax_url'   => admin_url('admin-ajax.php'),
                'api_base_url' => AYSA_API_BASE_URL
            )
        );
        echo '<div id="custom_category_meta_box" class="postbox category_box">
                <div class="postbox-header"><h2>AYSA SEO</h2></div>
                <div class="inside">';
        
        // Include the metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-category-metabox.php';
        
        echo '</div></div>';
        
        // Add nonce field for security
        wp_nonce_field('aysa_category_save', 'aysa_category_nonce');
    }
    
    /**
     * Add metabox for normal WordPress categories - add form (new categories)
     */
    public function add_normal_cat_metabox_new()
    {
        // Enqueue and localize script for new category (no URL yet)
        wp_enqueue_script('aysa_product_metabox_admin-js');
        wp_localize_script(
            'aysa_product_metabox_admin-js',
            'aysaParams',
            array(
                'termId'     => 0,
                'entityType' => 'term',
                'entityUrl'  => '',
                'nonce'      => wp_create_nonce('aysa_category_metabox')
            )
        );
        echo '<div id="custom_category_meta_box_new" class="form-field aysa-category-metabox">
                <h2>AYSA SEO</h2>
                <div class="inside">';
        
        // Include the metabox template
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-category-metabox.php';
        
        echo '</div></div>';
        
        // Add nonce field for security
        wp_nonce_field('aysa_category_save', 'aysa_category_nonce');
    }

    /**
     * Save normal WordPress category SEO data
     */
    public function save_normal_category_seo_data($term_id)
    {
        // Basic validation first
        if (!$term_id || !is_numeric($term_id)) {
            return;
        }
        
        // Get the term to validate it exists
        $term = get_term($term_id);
        if (is_wp_error($term) || !$term) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: Invalid term_id or term not found: ' . $term_id);
            }
            return;
        }
        
        // Check user capabilities
        if (!current_user_can('manage_categories')) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: User does not have permission to edit categories');
            }
            return;
        }
        
        // Enhanced data collection and validation
        $has_aysa_data = false;
        $aysa_data_sources = array();
        
        // Check for AYSA nonce and data in POST
        if (isset($_POST['aysa_category_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aysa_category_nonce'])), 'aysa_category_save')) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'form_post';
        }
        
        // Check for direct POST data (without nonce - for WordPress core saves)
        $has_post_data = isset($_POST['aysa_meta_title']) || isset($_POST['aysa_meta_description']) || 
                        isset($_POST['aysa_meta_keywords']) || isset($_POST['keyword']) || isset($_POST['second_keyword']);
        
        if ($has_post_data) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'direct_post';
        }
        
        // Check for AYSA data in term meta (from AJAX saves)
        $temp_meta = get_term_meta($term_id, '_aysa_temp_save_data', true);
        if (!empty($temp_meta) && is_array($temp_meta)) {
            $has_aysa_data = true;
            $aysa_data_sources[] = 'temp_meta';
        }
        
        // Enhanced logging with POST data analysis
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA SEO: Normal category save triggered - Term ID: ' . $term_id . ' (' . $term->name . ')');
            aysa_debug_log('AYSA SEO: Term taxonomy: ' . $term->taxonomy);
            aysa_debug_log('AYSA SEO: Has AYSA data: ' . ($has_aysa_data ? 'yes' : 'no'));
            aysa_debug_log('AYSA SEO: Data sources: ' . implode(', ', $aysa_data_sources));
        }
        
        // If we don't have AYSA data, skip
        if (!$has_aysa_data) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA SEO: No AYSA data found for normal category, skipping save');
            }
            return;
        }
        
        // Call the main category save method (reuse existing logic)
        $this->save_category_seo_data($term_id);
    }


    /**
     * Prepare data for blog content generation
     */
    private function prepare_blog_content_data($post_id, $article_topic, $primary_keyword, $local_specifics) {
        // Get general information from options
        $brand_name = get_option('aysa_brand_name', get_bloginfo('name'));
        $phone_number = get_option('aysa_phone_number', '');
        $email_address = get_option('aysa_email_address', get_option('admin_email'));
        $client_info = trim($phone_number . ' ' . $email_address); // Combine phone and email for client info
        $domain = get_option('aysa_domain', home_url());
        $publication_location = get_option('aysa_publication_location', get_bloginfo('name'));
        $language = get_option('aysa_language', 'ro');
        $city = get_option('aysa_city', '');
        
        // Get secondary keywords from keyword clustering API
        $project_id = get_option('aysa_project_id', 0);
        $secondary_keywords = $this->get_secondary_keywords_from_clustering($primary_keyword, $language, $project_id);
        
        // If no keywords from clustering, try from post meta
        if (empty($secondary_keywords)) {
            $keyword_data = get_post_meta($post_id, 'aysa_keyword_data', true);
            if (is_array($keyword_data) && !empty($keyword_data['second_keyword'])) {
                $secondary_keywords = explode(',', $keyword_data['second_keyword']);
                $secondary_keywords = array_map('trim', $secondary_keywords);
            }
        }
        
        // Prepare website context
        $website_context = $this->get_website_context();
        
        return [
            'article_topic' => $article_topic,
            'brand_name' => $brand_name,
            'client_info' => $client_info,
            'domain' => $domain,
            'publication_location' => $publication_location,
            'language' => $language,
            'local_specifics' => $local_specifics,
            'city' => $city,
            'target_audience' => get_option('aysa_target_audience', 'general public'),
            'audience_level' => get_option('aysa_audience_level', ''),
            'target_word_count' => '2000',
            'primary_keyword' => $primary_keyword,
            'secondary_keywords' => $secondary_keywords,
            'related_keywords' => $secondary_keywords, // For now, same as secondary
            'tone' => get_option('aysa_tone', 'profesionist'),
            'website_context' => $website_context,
            'phone_number' => $phone_number,
            'email_address' => $email_address,
            'project_id' => get_option('aysa_project_id', 0)
        ];
    }

    /**
     * Get website context for blog content generation
     */
    private function get_website_context() {
        $site_description = get_bloginfo('description');
        $recent_posts = get_posts(['numberposts' => 3, 'post_status' => 'publish']);
        $context = $site_description;
        
        if (!empty($recent_posts)) {
            $context .= ' Recent topics: ';
            $titles = array_map(function($post) { return $post->post_title; }, $recent_posts);
            $context .= implode(', ', $titles);
        }
        
        return $context;
    }

    /**
     * Get secondary keywords for a post
     */
    private function get_post_secondary_keywords($post_id) {
        $keyword_data = get_post_meta($post_id, 'aysa_keyword_data', true);
        $secondary_keywords = [];
        
        if (is_array($keyword_data) && !empty($keyword_data['second_keyword'])) {
            $secondary_keywords = explode(',', $keyword_data['second_keyword']);
            $secondary_keywords = array_map('trim', $secondary_keywords);
        }
        
        return $secondary_keywords;
    }

    /**
     * Get blog content API base URL
     */
    private function get_blog_content_api_base_url() {
        // Check if there's a constant defined in wp-config.php
        if (defined('AYSA_BLOG_CONTENT_API_URL')) {
            return rtrim(AYSA_BLOG_CONTENT_API_URL, '/');
        }
        
        // Check if there's a custom endpoint defined in WordPress options
        $custom_endpoint = get_option('aysa_blog_content_api_endpoint', '');
        
        if (!empty($custom_endpoint)) {
            return rtrim($custom_endpoint, '/');
        }
        
        // Default to primary AYSA API for blog content generation
        if (defined('AYSA_API_BASE_URL') && is_string(AYSA_API_BASE_URL) && trim(AYSA_API_BASE_URL) !== '') {
            return rtrim((string) AYSA_API_BASE_URL, '/');
        }
        if (class_exists('Aysa_API_Config')) {
            return rtrim((string) Aysa_API_Config::get_base_url(), '/');
        }
        return 'https://api.aysa.ai';
    }

    /**
     * Call keyword clustering API to get secondary keywords
     */
    private function get_secondary_keywords_from_clustering($primary_keyword, $language, $project_id) {
        $api_base_url = defined('AYSA_API_BASE_URL') ? AYSA_API_BASE_URL : (class_exists('Aysa_API_Config') ? Aysa_API_Config::get_base_url() : 'https://api.aysa.ai/');
        $api_url = rtrim($api_base_url, '/') . '/api/ChatGPT/keyword-clustering';
        
        $query_params = array(
            'primary_keyword' => $primary_keyword,
            'language' => $language,
            'projectId' => $project_id
        );
        
        $url = $api_url . '?' . http_build_query($query_params);
        
        $response = wp_remote_get($url, array(
            'timeout' => 30,
            'headers' => array(
                'Accept' => 'application/json'
            )
        ));
        
        if (is_wp_error($response)) {
            aysa_debug_log('AYSA: Keyword clustering API error: ' . $response->get_error_message());
            return array();
        }
        
        $response_code = wp_remote_retrieve_response_code($response);
        if ($response_code !== 200) {
            aysa_debug_log('AYSA: Keyword clustering API returned status: ' . $response_code);
            return array();
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (!$data || !is_array($data)) {
            aysa_debug_log('AYSA: Invalid keyword clustering response format');
            return array();
        }
        
        // Extract keywords from all categories
        $all_keywords = array();
        $keyword_types = array('short_tail_keywords', 'mid_tail_keywords', 'long_tail_keywords', 'related_keywords', 'lsi_keywords');
        
        foreach ($keyword_types as $type) {
            if (isset($data[$type]) && is_array($data[$type])) {
                foreach ($data[$type] as $keyword) {
                    if (is_string($keyword) && !empty(trim($keyword))) {
                        $all_keywords[] = trim($keyword);
                    }
                }
            }
        }
        
        // Remove duplicates and filter out the primary keyword
        $all_keywords = array_unique($all_keywords);
        $all_keywords = array_filter($all_keywords, function($keyword) use ($primary_keyword) {
            return strcasecmp($keyword, $primary_keyword) !== 0;
        });
        
        return array_values($all_keywords);
    }
    
    /**
     * Call blog content API endpoint
     */
    private function call_blog_content_api($endpoint, $data) {
        $api_url = $this->get_blog_content_api_base_url() . '/blog-content/' . $endpoint;
        
        // Configure request options
        $request_options = [
            'headers' => [
                'Content-Type' => 'application/json',
            ],
            'body' => wp_json_encode($data),
            'timeout' => 120, // 2 minutes for content generation
        ];
        
        // Log the request for debugging (only in debug mode)
        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA Blog Content API Request: ' . $api_url);
            aysa_debug_log('AYSA Blog Content API Data: ' . wp_json_encode($data));
        }
        
        $response = wp_remote_post($api_url, $request_options);

        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA Blog Content API Error: ' . $error_message);
            }
            return ['success' => false, 'message' => $error_message];
        }

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

        if (defined('WP_DEBUG') && WP_DEBUG) {
            aysa_debug_log('AYSA Blog Content API Response Code: ' . $response_code);
            aysa_debug_log('AYSA Blog Content API Response Body: ' . $body);
        }

        if ($response_code === 200) {
            return ['success' => true, 'data' => $result];
        } else {
            $error_message = $result['message'] ?? 'API call failed';
            if (defined('WP_DEBUG') && WP_DEBUG) {
                aysa_debug_log('AYSA Blog Content API Failed: ' . $error_message);
            }
            return ['success' => false, 'message' => $error_message];
        }
    }

    /**
     * Generate complete blog content by calling all endpoints in sequence
     */
    private function generate_complete_blog_content($data) {
        $results = [];
        
        // Step 1: Generate document outline
        $outline_result = $this->call_blog_content_api('35-document-outline', $data);
        if (!$outline_result['success']) {
            return $outline_result;
        }
        $results['outline'] = $outline_result['data']['outline'] ?? '';
        $data['document_outline'] = $results['outline'];
        
        // Extract H1 title from outline for next steps
        $lines = explode("\n", $results['outline']);
        $h1_title = '';
        foreach ($lines as $line) {
            if (strpos(trim($line), 'H1') === 0) {
                $h1_title = trim(str_replace('H1:', '', $line));
                break;
            }
        }
        $data['title_h1'] = $h1_title;
        
        // Step 2: Generate TL;DR and introduction
        $tldr_result = $this->call_blog_content_api('36-tldr-introduction', $data);
        if ($tldr_result['success']) {
            $results['tldr_content'] = $tldr_result['data']['tldr_content'] ?? '';
            $results['introduction_content'] = $tldr_result['data']['introduction_content'] ?? '';
        }
        
        // Step 3: Generate extended content
        $extended_result = $this->call_blog_content_api('37-extended-content', $data);
        if ($extended_result['success']) {
            $results['extended_content'] = $extended_result['data']['extended_content'] ?? '';
        }
        
        // Step 4: Generate FAQ
        $faq_result = $this->call_blog_content_api('38-faq-microdata', $data);
        if ($faq_result['success']) {
            $results['faq_html'] = $faq_result['data']['faq_html'] ?? '';
        }
        
        // Step 5: Generate conclusion with CTA
        $conclusion_result = $this->call_blog_content_api('39-conclusion-cta', $data);
        if ($conclusion_result['success']) {
            $results['conclusion_content'] = $conclusion_result['data']['conclusion_content'] ?? '';
        }
        
        // Step 5.5: Generate featured image
        $image_result = $this->call_blog_content_api('40-image-generation', $data);
        if ($image_result['success']) {
            $results['featured_image_url'] = $image_result['data']['imageUrl'] ?? $image_result['data']['ImageUrl'] ?? $image_result['data']['featured_image_url'] ?? '';
        }
        
        // Step 6: Generate meta SEO elements
        $meta_result = $this->call_blog_content_api('41-meta-seo-elements', $data);
        if ($meta_result['success']) {
            $results['meta_title'] = $meta_result['data']['meta_title'] ?? '';
            $results['meta_description'] = $meta_result['data']['meta_description'] ?? '';
            $results['url_recomandat'] = $meta_result['data']['url_recomandat'] ?? '';
        }
        
        return ['success' => true, 'data' => $results];
    }

    /**
     * Save blog content to post meta
     */
    private function save_blog_content_to_meta($post_id, $content_data) {
        $meta_mapping = [
            'outline' => 'aysa_blog_content_outline',
            'tldr_content' => 'aysa_blog_content_tldr',
            'introduction_content' => 'aysa_blog_content_introduction',
            'extended_content' => 'aysa_blog_content_extended',
            'faq_html' => 'aysa_blog_content_faq',
            'conclusion_content' => 'aysa_blog_content_conclusion',
            'meta_title' => 'aysa_meta_title',
            'meta_description' => 'aysa_meta_description',
            'url_recomandat' => 'aysa_url_recomandat',
            'featured_image_url' => 'aysa_featured_image_url'
        ];
        
        foreach ($meta_mapping as $key => $meta_key) {
            if (!empty($content_data[$key])) {
                update_post_meta($post_id, $meta_key, $content_data[$key]);
            }
        }
        
        // Update the combined keyword data
        $keyword_data = get_post_meta($post_id, 'aysa_keyword_data', true);
        if (!is_array($keyword_data)) {
            $keyword_data = [];
        }
        
        foreach ($meta_mapping as $key => $meta_key) {
            if (!empty($content_data[$key])) {
                $keyword_data[$key] = $content_data[$key];
            }
        }
        
        update_post_meta($post_id, 'aysa_keyword_data', $keyword_data);
    }
    
    /**
     * Handle AJAX request to set featured image from URL
     */
    public function handle_set_featured_image() {
        // Verify nonce
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'aysa_metabox_nonce')) {
            wp_die('Security check failed');
        }
        
        // Get parameters
        $post_id = intval(wp_unslash($_POST['post_id'] ?? 0));
        $image_url = esc_url_raw(wp_unslash($_POST['image_url'] ?? ''));
        
        // Validate parameters
        if (!$post_id || !$image_url) {
            wp_send_json_error('Missing required parameters');
            return;
        }
        
        // Check if user can edit the post
        if (!current_user_can('edit_post', $post_id)) {
            wp_send_json_error('You do not have permission to edit this post');
            return;
        }
        
        // Download and set featured image
        $result = $this->download_and_set_featured_image($post_id, $image_url);
        
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
        } else {
            wp_send_json_success($result);
        }
    }

    /**
     * Generate article featured image via api.aysa.ai broker (no DeepSeek path).
     */
    public function handle_generate_article_image() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_metabox_nonce')) {
            wp_send_json_error('Security check failed');
            return;
        }

        $request = $this->prepare_article_image_request($_POST);
        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $result = $this->execute_article_image_request($request);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        wp_send_json_success($result);
    }
    
    /**
     * Download image from URL and set as featured image
     * 
     * @param int $post_id Post ID
     * @param string $image_url Image URL
     * @return array|WP_Error Success data or error
     */
    private function download_and_set_featured_image($post_id, $image_url) {
        // Include necessary WordPress files
        if (!function_exists('media_handle_sideload')) {
            require_once(ABSPATH . 'wp-admin/includes/media.php');
            require_once(ABSPATH . 'wp-admin/includes/file.php');
            require_once(ABSPATH . 'wp-admin/includes/image.php');
        }
        
        // Get the image
        $response = wp_remote_get($image_url, [
            'timeout' => 30,
            'headers' => [
                'User-Agent' => 'AYSA WordPress Plugin'
            ]
        ]);
        
        if (is_wp_error($response)) {
            return new WP_Error('download_failed', 'Failed to download image: ' . $response->get_error_message());
        }
        
        $response_code = wp_remote_retrieve_response_code($response);
        if ($response_code !== 200) {
            return new WP_Error('download_failed', 'Failed to download image. HTTP status: ' . $response_code);
        }
        
        $image_data = wp_remote_retrieve_body($response);
        if (empty($image_data)) {
            return new WP_Error('download_failed', 'Downloaded image is empty');
        }
        
        // Get image info from URL
        $image_info = pathinfo(wp_parse_url($image_url, PHP_URL_PATH));
        $filename = !empty($image_info['filename']) ? $image_info['filename'] : 'aysa-featured-image';
        $extension = !empty($image_info['extension']) ? $image_info['extension'] : 'jpg';
        
        // Create temporary file
        $upload_dir = wp_upload_dir();
        $temp_file = $upload_dir['path'] . '/' . $filename . '.' . $extension;
        
        // Save image data to temporary file
        $saved = file_put_contents($temp_file, $image_data);
        if ($saved === false) {
            return new WP_Error('save_failed', 'Failed to save image to temporary file');
        }
        
        // Prepare file array for media_handle_sideload
        $file_array = [
            'name' => $filename . '.' . $extension,
            'tmp_name' => $temp_file,
            'size' => filesize($temp_file),
            'type' => wp_check_filetype($temp_file)['type']
        ];
        
        // Upload the image
        $attachment_id = media_handle_sideload($file_array, $post_id, 'AYSA Generated Featured Image');
        
        // Clean up temporary file
        if (file_exists($temp_file)) {
            wp_delete_file($temp_file);
        }
        
        if (is_wp_error($attachment_id)) {
            return new WP_Error('upload_failed', 'Failed to upload image: ' . $attachment_id->get_error_message());
        }
        
        // Set as featured image
        $set_result = set_post_thumbnail($post_id, $attachment_id);
        
        if (!$set_result) {
            return new WP_Error('set_failed', 'Failed to set image as featured image');
        }
        
        // Get thumbnail HTML for UI update
        $thumbnail_html = get_the_post_thumbnail($post_id, 'medium');
        
        return [
            'attachment_id' => $attachment_id,
            'thumbnail_html' => $thumbnail_html,
            'message' => 'Featured image set successfully'
        ];
    }

    private function get_broker_config() {
        $base_url = defined('AYSA_OAUTH_BROKER_BASE_URL')
            ? AYSA_OAUTH_BROKER_BASE_URL
            : (defined('AYSA_API_BASE_URL') ? AYSA_API_BASE_URL : 'https://api.aysa.ai');
        $key_id = defined('AYSA_OAUTH_BROKER_KEY_ID') ? AYSA_OAUTH_BROKER_KEY_ID : 'wp_plugin';
        $key_secret = defined('AYSA_OAUTH_BROKER_KEY_SECRET') ? AYSA_OAUTH_BROKER_KEY_SECRET : '';

        $base_url = rtrim((string) $base_url, '/');
        $key_id = trim((string) $key_id);
        $key_secret = trim((string) $key_secret);

        if ($base_url === '' || $key_id === '' || $key_secret === '') {
            return new WP_Error('broker_config_missing', 'OAuth broker is not configured on this site.');
        }

        return array(
            'base_url' => $base_url,
            'key_id' => $key_id,
            'key_secret' => $key_secret,
        );
    }

    private function broker_request($method, $path, $payload = array(), $timeout = 30) {
        $cfg = $this->get_broker_config();
        if (is_wp_error($cfg)) {
            return $cfg;
        }

        $method = strtoupper((string) $method);
        $path = '/' . ltrim((string) $path, '/');
        $url = $cfg['base_url'] . $path;
        $body = ($method === 'POST') ? wp_json_encode($payload) : '';
        if ($body === false) {
            $body = '{}';
        }

        $timestamp = (string) time();
        try {
            $nonce = bin2hex(random_bytes(16));
        } catch (Exception $e) {
            $nonce = wp_generate_password(32, false, false);
        }

        $body_hash = hash('sha256', $body);
        $canonical = implode("\n", array($method, $path, $body_hash, $timestamp, $nonce));
        $signature = hash_hmac('sha256', $canonical, $cfg['key_secret']);

        $headers = array(
            'Content-Type' => 'application/json',
            'X-Aysa-Key' => $cfg['key_id'],
            'X-Aysa-Timestamp' => $timestamp,
            'X-Aysa-Nonce' => $nonce,
            'X-Aysa-Signature' => $signature,
        );

        $args = array(
            'timeout' => max(10, min(120, intval($timeout))),
            'method' => $method,
            'headers' => $headers,
        );
        if ($method === 'POST') {
            $args['body'] = $body;
        }

        $resp = wp_remote_request($url, $args);
        if (is_wp_error($resp)) {
            return new WP_Error('broker_http_failed', 'Broker request failed: ' . $resp->get_error_message());
        }

        $code = wp_remote_retrieve_response_code($resp);
        $data = json_decode((string) wp_remote_retrieve_body($resp), true);
        if (!is_array($data)) {
            return new WP_Error('broker_invalid_response', 'Broker returned invalid response.');
        }
        if ($code < 200 || $code >= 300 || (isset($data['ok']) && !$data['ok'])) {
            $msg = isset($data['error']) ? (string) $data['error'] : 'Broker returned error.';
            return new WP_Error('broker_error', $msg . ' [code=' . intval($code) . ']');
        }

        return $data;
    }

    private function get_project_id_for_wallet() {
        return $this->resolve_bound_project_id(0);
    }

    private function track_wallet_usage($action_key, $reference, $fallback_cost_cents, $meta = array(), $endpoint = '') {
        if (!function_exists('aysa_get_wallet_manager')) {
            return;
        }

        $project_id = $this->get_project_id_for_wallet();
        if ($project_id <= 0) {
            return;
        }

        $wallet = aysa_get_wallet_manager();
        if (!$wallet || !method_exists($wallet, 'track_action_usage')) {
            return;
        }

        $wallet->track_action_usage($project_id, $action_key, $reference, array(
            'meta' => is_array($meta) ? $meta : array(),
            'fallback_cost_cents' => max(0, intval($fallback_cost_cents)),
            'endpoint' => $endpoint,
            'billing_authority' => 'plugin_wallet',
        ));
    }
    
    /**
     * AJAX handler for getting credits
     */
    public function handle_get_credits() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_credits_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions'));
            return;
        }
        
        try {
            if (!class_exists('Aysa_Wallet_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-wallet-manager.php';
            }
            $project_id = intval(get_option('aysa_project_id', 0));
            if ($project_id <= 0) {
                wp_send_json_success(array('totalCredits' => 0));
                return;
            }
            $snapshot = Aysa_Wallet_Manager::get_instance()->get_wallet_snapshot($project_id, array('allow_remote' => false));
            wp_send_json_success(array(
                'totalCredits' => intval($snapshot['balance_cents'] ?? 0),
                'credits' => intval($snapshot['balance_cents'] ?? 0),
                'spendableCredits' => intval($snapshot['spendable_cents'] ?? 0),
            ));
        } catch (Exception $e) {
            wp_send_json_error(array('message' => 'Error: ' . $e->getMessage()));
        }
    }
    
    /**
     * AJAX handler for buying credits
     */
    public function handle_buy_credits() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aysa_credits_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions'));
            return;
        }
        
        try {
            if (!class_exists('Aysa_Wallet_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-wallet-manager.php';
            }
            $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
            $credits_cents = isset($_POST['credits_cents']) ? intval(wp_unslash($_POST['credits_cents'])) : 0;
            if ($project_id <= 0 || $credits_cents <= 0) {
                wp_send_json_error(array('message' => 'Invalid project or credits amount.'));
                return;
            }
            $order = Aysa_Wallet_Manager::get_instance()->create_topup_order($project_id, $credits_cents, get_current_user_id());
            if (is_wp_error($order)) {
                wp_send_json_error(array('message' => $order->get_error_message()));
                return;
            }
            wp_send_json_success(array(
                'message' => 'Credit order created.',
                'order' => $order,
                'payment_link' => (string) ($order['payment_link'] ?? ''),
            ));
        } catch (Exception $e) {
            wp_send_json_error(array('message' => $e->getMessage()));
        }
    }
}
