<?php
/**
 * Plugin Name: Instant Indexing & Check for Google SEO
 * Description: Index Google fast with Instant Indexing. Force Google indexing for your SEO content via Google API or Premium Force Index. Check indexation status.
 * Version: 1.7.4
 * Author: Arthur SEO
 * Author URI: https://wp-instant-indexing.com
 * License: GPLv2 or later
 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
 */

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

// --- CONFIGURATION ---
define('IIC_CHECK_VERSION', '1.7.4');
define('IIC_CHECK_SALES_URL', 'https://wp-instant-indexing.com');
define('IIC_CHECK_API_URL', 'https://wp-instant-indexing.com/check_license.php');
define('IIC_CHECK_RELAY_URL', 'https://wp-instant-indexing.com/relay_ralfy.php');
define('IIC_CHECK_SPEEDY_RELAY_URL', 'https://wp-instant-indexing.com/relay_speedy.php');
define('IIC_CHECK_INSTANT_RELAY_URL', 'https://wp-instant-indexing.com/relay_ralfy.php'); // instant flag sent via 'instant'=1
define('IIC_CHECK_UPDATE_URL', 'https://wp-instant-indexing.com/metadata.json');

// --- INSTANT INDEXING : interrupteur ---
// Piloté à distance depuis le panel admin (wp-instant-indexing.com), synchronisé
// via check_license.php. Cette constante n'est qu'un REPLI utilisé tant qu'aucune
// valeur n'a été reçue du serveur (false = coupé par sécurité).
define('IIC_CHECK_INSTANT_ENABLED', false);

/**
 * Retrouve l'ID d'un contenu à partir de son URL, de façon fiable.
 *
 * url_to_postid() échoue dans de nombreux cas réels (slash final, types de
 * contenu personnalisés, sites multilingues, page d'accueil...). Quand ça
 * échouait, le statut n'était jamais enregistré : l'interface affichait le
 * résultat, mais il disparaissait au rechargement.
 *
 * On privilégie donc l'ID envoyé par l'interface (elle le connaît déjà), avec
 * plusieurs replis sur l'URL.
 *
 * @param string   $url        URL du contenu.
 * @param int|null $hinted_id  ID transmis par l'interface (prioritaire).
 * @return int 0 si introuvable.
 */
function iic_check_resolve_post_id($url, $hinted_id = null)
{
    // 1. ID fourni par l'interface : on vérifie qu'il existe vraiment.
    $hinted_id = (int) $hinted_id;
    if ($hinted_id > 0 && get_post_status($hinted_id) !== false) {
        return $hinted_id;
    }

    if (empty($url)) {
        return 0;
    }

    // 2. Résolution standard.
    $id = url_to_postid($url);
    if ($id) {
        return $id;
    }

    // 3. Variantes avec / sans slash final.
    $id = url_to_postid(untrailingslashit($url));
    if ($id) {
        return $id;
    }
    $id = url_to_postid(trailingslashit($url));
    if ($id) {
        return $id;
    }

    // 4. Dernier recours : recherche par nom de page (gère certains CPT).
    $id = (int) attachment_url_to_postid($url);
    if ($id) {
        return $id;
    }

    return 0;
}

/**
 * Marque un contenu comme « en attente d'indexation » et horodate l'envoi,
 * pour pouvoir afficher « en attente depuis X » dans le tableau de bord.
 */
function iic_check_mark_waiting($post_id)
{
    if (!$post_id) {
        return;
    }
    update_post_meta($post_id, '_sip_index_status', 'waiting');
    update_post_meta($post_id, '_sip_index_date', current_time('mysql'));
}

/**
 * Depuis combien d'heures ce contenu attend-il ? null si non pertinent.
 */
function iic_check_waiting_hours($post_id, $status)
{
    if ($status !== 'waiting' || !$post_id) {
        return null;
    }
    $sent = get_post_meta($post_id, '_sip_index_date', true);
    if (empty($sent)) {
        return null;
    }
    $diff = current_time('timestamp') - strtotime($sent);
    return $diff < 0 ? 0 : (int) floor($diff / 3600);
}

// --- INTÉGRATION WOOCOMMERCE ---

/** WooCommerce est-il installé et actif sur ce site ? */
function iic_check_woo_active()
{
    return class_exists('WooCommerce') || post_type_exists('product');
}

/** L'intégration WooCommerce est-elle activée ? (activée par défaut si Woo est présent) */
function iic_check_woo_enabled()
{
    if (!iic_check_woo_active()) {
        return false;
    }
    return get_option('iic_check_woo_enabled', '1') === '1';
}

/** Types de contenu à indexer (articles, pages + produits si Woo activé). */
function iic_check_post_types()
{
    $types = ['post', 'page'];
    if (iic_check_woo_enabled()) {
        $types[] = 'product';
    }
    return $types;
}

/** Même liste, prête à être injectée dans un IN (...) SQL. */
function iic_check_post_types_sql()
{
    $types = array_map('esc_sql', iic_check_post_types());
    return "'" . implode("','", $types) . "'";
}

/** Taxonomies WooCommerce (catégories, étiquettes, marques) à exclure si Woo est désactivé. */
function iic_check_woo_taxonomies()
{
    return ['product_cat', 'product_tag', 'product_brand', 'pwb-brand', 'pa_brand', 'berocket_brand', 'yith_product_brand'];
}

/**
 * Instant Indexing est-il actif ? Valeur pilotée par le serveur (panel admin),
 * avec repli sur la constante si aucune synchro n'a encore eu lieu.
 */
function iic_check_instant_enabled()
{
    $opt = get_option('iic_check_instant_enabled', null);
    if ($opt === null || $opt === '' || $opt === false) {
        return IIC_CHECK_INSTANT_ENABLED;
    }
    return ($opt === '1' || $opt === 1 || $opt === true);
}

if (file_exists(plugin_dir_path(__FILE__) . 'vendor/autoload.php')) {
    require_once plugin_dir_path(__FILE__) . 'vendor/autoload.php';
}

// --- INIT AUTO-UPDATE ---
// Auto-update removed for WordPress.org compliance
// add_action('init', function () { ... });

add_action('admin_menu', function () {
    add_menu_page('Instant Index', 'Instant Index', 'manage_options', 'instant-indexing-check', function () {
        echo '<div id="sip-root"></div>';
    }, 'dashicons-google', 30);
});

// --- NATIVE WP POSTS FILTER ---
add_action('restrict_manage_posts', function ($post_type) {
    if (in_array($post_type, iic_check_post_types(), true)) {
        $selected = isset($_GET['sip_index_filter']) ? sanitize_text_field($_GET['sip_index_filter']) : '';
        echo '<select name="sip_index_filter" id="sip_index_filter">';
        echo '<option value="">🚀 Instant Indexing (Toutes)</option>';
        echo '<option value="not_indexed" ' . selected($selected, 'not_indexed', false) . '>❌ Non Indexées</option>';
        echo '<option value="indexed" ' . selected($selected, 'indexed', false) . '>✅ Indexées</option>';
        echo '<option value="waiting" ' . selected($selected, 'waiting', false) . '>En attente</option>';
        echo '</select>';
    }
});

add_filter('parse_query', function ($query) {
    global $pagenow;
    if (is_admin() && $pagenow === 'edit.php' && isset($_GET['sip_index_filter']) && $_GET['sip_index_filter'] !== '' && $query->is_main_query()) {
        $status = sanitize_text_field($_GET['sip_index_filter']);
        $meta_query = $query->get('meta_query');
        if (!is_array($meta_query)) {
            $meta_query = [];
        }
        $meta_query[] = [
            'key' => '_sip_index_status',
            'value' => $status,
            'compare' => '='
        ];
        $query->set('meta_query', $meta_query);
    }
});

// --- ENQUEUE SCRIPTS & DATA ---
add_action('admin_enqueue_scripts', function ($hook) {
    if ($hook != 'toplevel_page_instant-indexing-check')
        return;

    // Versionner les assets sur la version du plugin permet au navigateur de les
    // mettre en cache (le fichier JS fait ~215 Ko). Bumper IIC_CHECK_VERSION à
    // chaque release invalide proprement le cache.
    $ver = IIC_CHECK_VERSION;

    wp_enqueue_style('iic-style', plugin_dir_url(__FILE__) . 'admin-style.css', [], $ver);
    wp_enqueue_script('iic-app', plugin_dir_url(__FILE__) . 'admin-app.js', [], $ver, true);

    $google_key = get_option('iic_check_google_json_key');
    $auto_mode = get_option('iic_check_auto_index_mode', 'disabled');

    // Données chargées : Historique et Crédits
    $history = get_option('iic_check_premium_history', []);
    $credits = get_option('iic_check_user_credits', 0);

    wp_localize_script('iic-app', 'iic_check_config', [
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('iic_check_security_token'),
        'hasKey' => (
            get_option('iic_check_setup_done', false) ||
            (!empty($google_key) && !empty(get_option('iic_check_gsc_site_url')))
        ),
        'autoMode' => $auto_mode,
        'siteUrl' => site_url(),
        'status' => get_option('iic_check_saas_status', false),
        'credits' => $credits,
        'pluginUrl' => plugin_dir_url(__FILE__),
        'salesUrl' => IIC_CHECK_SALES_URL,
        'history' => $history,
        'locale' => get_option('iic_check_locale', get_locale()),
        // Indique si l'utilisateur a explicitement choisi une langue dans le plugin.
        // Sinon, le JS se basera sur la langue du navigateur (navigator.language).
        'localeExplicit' => (get_option('iic_check_locale', false) !== false),
        'apiMode' => get_option('iic_check_api_mode', 'google'),
        // WooCommerce : '1'/'0' (jamais de booléen, wp_localize_script les convertit en chaînes)
        'wooActive'  => iic_check_woo_active() ? '1' : '0',
        'wooEnabled' => iic_check_woo_enabled() ? '1' : '0',
        // Permet au JS de masquer les boutons Instant Index quand le service est coupé.
        // '1'/'0' explicites : wp_localize_script convertit les booléens en chaînes
        // (false devient ""), ce qui rendait toute comparaison stricte peu fiable.
        'instantEnabled' => iic_check_instant_enabled() ? '1' : '0',
        'pendingTask' => get_option('iic_check_pending_task', null),
        'expiresAt' => get_option('iic_check_expires_at', ''),
        'isExpired' => (function() {
            $status   = get_option('iic_check_saas_status', 'free');
            $expires  = get_option('iic_check_expires_at', '');
            $hadPrem  = !empty($expires) && $expires > '2020-01-01';
            $isNowFree = ($status !== 'premium');
            return ($hadPrem && $isNowFree && strtotime($expires) < time()) ? true : false;
        })(),
    ]);
});

// --- AJAX : SAUVEGARDER UNE TÂCHE EN COURS ---
add_action('wp_ajax_iic_check_save_pending_task', function () {
    if (!current_user_can('manage_options')) wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    $task_id = isset($_POST['task_id']) ? sanitize_text_field(wp_unslash($_POST['task_id'])) : '';
    $urls    = isset($_POST['urls'])    ? json_decode(wp_unslash($_POST['urls']), true)    : [];
    if (empty($task_id)) { iic_check_send_json(['msg' => 'task_id manquant'], false); return; }
    update_option('iic_check_pending_task', [
        'task_id'    => $task_id,
        'urls'       => (array)$urls,
        'created_at' => time(),
    ]);
    iic_check_send_json('OK');
});

// --- AJAX : EFFACER UNE TÂCHE EN COURS ---
add_action('wp_ajax_iic_check_clear_pending_task', function () {
    if (!current_user_can('manage_options')) wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    delete_option('iic_check_pending_task');
    iic_check_send_json('OK');
});

function iic_check_send_json($data, $success = true)
{
    // Clean buffer before sending JSON to avoid invalid response
    while (ob_get_level()) {
        ob_end_clean();
    }

    // La réponse part : plus besoin du filet de sécurité anti-réponse-vide.
    $GLOBALS['iic_check_json_sent'] = true;

    if ($success)
        wp_send_json_success($data);
    else
        wp_send_json_error($data);
}

/**
 * Filet de sécurité : garantit qu'une requête AJAX du plugin ne renvoie JAMAIS
 * une réponse vide.
 *
 * Une erreur fatale PHP (ou un process coupé par l'hébergeur pendant l'appel au
 * relais) produit un corps vide, que le navigateur signalait par un
 * « JSON.parse: unexpected end of data » affiché tel quel à l'utilisateur.
 * On renvoie à la place un JSON explicite.
 */
add_action('init', function () {
    if (!wp_doing_ajax() || empty($_REQUEST['action'])) {
        return;
    }
    if (strpos((string) $_REQUEST['action'], 'iic_check_') !== 0) {
        return;
    }

    $GLOBALS['iic_check_json_sent'] = false;

    register_shutdown_function(function () {
        if (!empty($GLOBALS['iic_check_json_sent'])) {
            return; // réponse déjà envoyée normalement
        }
        $err = error_get_last();
        $fatal = $err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR], true);

        while (ob_get_level()) {
            ob_end_clean();
        }
        if (!headers_sent()) {
            header('Content-Type: application/json; charset=utf-8');
        }
        echo wp_json_encode([
            'success' => false,
            'data'    => [
                'msg'       => $fatal
                    ? 'Erreur serveur interrompue (PHP). Réessayez ; si l\'action a déjà été envoyée, le statut restera en attente.'
                    : 'Réponse serveur interrompue (délai dépassé). L\'action a peut-être été prise en compte : vérifiez le statut dans quelques minutes.',
                'retryable' => true,
            ],
        ]);
    });
}, 1);

// --- APPEL API DISTANT (RELAY) ---
function iic_check_call_premium_relay($url)
{
    $license_key = get_option('iic_check_saas_license_key');
    if (empty($license_key))
        return ['status' => 'error', 'message' => 'Licence manquante'];

    $response = wp_remote_post(IIC_CHECK_RELAY_URL, [
        'body' => ['license_key' => $license_key, 'url' => esc_url_raw($url)],
        'timeout' => 20
    ]);

    if (is_wp_error($response))
        return ['status' => 'error', 'message' => 'Erreur Relais : ' . $response->get_error_message()];
    return json_decode(wp_remote_retrieve_body($response), true);
}

function iic_check_call_premium_relay_bulk($urls_array)
{
    $license_key = get_option('iic_check_saas_license_key');
    if (empty($license_key))
        return ['status' => 'error', 'message' => 'Licence manquante'];

    $response = wp_remote_post(IIC_CHECK_RELAY_URL, [
        'body' => [
            'license_key' => $license_key, 
            'urls' => json_encode($urls_array)
        ],
        'timeout' => 45 // Wait longer for bulk
    ]);

    if (is_wp_error($response))
        return ['status' => 'error', 'message' => 'Erreur Relais : ' . $response->get_error_message()];
    return json_decode(wp_remote_retrieve_body($response), true);
}

// --- APPEL RELAY INSTANT INDEXING (10 crédits/URL) ---
function iic_check_call_instant_relay($url)
{
    $license_key = get_option('iic_check_saas_license_key');
    if (empty($license_key))
        return ['status' => 'error', 'message' => 'Licence manquante'];

    $response = wp_remote_post(IIC_CHECK_INSTANT_RELAY_URL, [
        'body' => [
            'license_key' => $license_key,
            'url'         => esc_url_raw($url),
            'instant'     => '1',
        ],
        'timeout' => 25
    ]);

    if (is_wp_error($response))
        return ['status' => 'error', 'message' => 'Erreur Relay Instant : ' . $response->get_error_message()];
    return json_decode(wp_remote_retrieve_body($response), true);
}

function iic_check_call_instant_relay_bulk($urls_array)
{
    $license_key = get_option('iic_check_saas_license_key');
    if (empty($license_key))
        return ['status' => 'error', 'message' => 'Licence manquante'];

    $response = wp_remote_post(IIC_CHECK_INSTANT_RELAY_URL, [
        'body' => [
            'license_key' => $license_key,
            'urls'        => json_encode($urls_array),
            'instant'     => '1',
        ],
        'timeout' => 60
    ]);

    if (is_wp_error($response))
        return ['status' => 'error', 'message' => 'Erreur Relay Instant Bulk : ' . $response->get_error_message()];
    return json_decode(wp_remote_retrieve_body($response), true);
}

// --- GESTION HISTORIQUE ---
function iic_check_add_history($url)
{
    $history = get_option('iic_check_premium_history', []);
    $history[$url] = time();
    // Garde max 2000 entrées pour ne pas surcharger
    if (count($history) > 2000) {
        array_shift($history);
    }
    update_option('iic_check_premium_history', $history);
}

// --- AJAX : MARQUER COMME ENVOYÉ ---
add_action('wp_ajax_iic_check_mark_premium', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    // Sanitization issue fixed
    if (!isset($_POST['url'])) {
        iic_check_send_json('Missing URL', false);
        return;
    }
    $url = esc_url_raw(wp_unslash($_POST['url']));
    iic_check_add_history($url);
    iic_check_send_json('OK');
});

// --- AUTO-INDEX ---
add_action('transition_post_status', 'iic_check_auto_index_logic', 10, 3);
function iic_check_auto_index_logic($new, $old, $post)
{
    // Uniquement lors d'une première publication.
    if ($new !== 'publish' || $old === 'publish')
        return;
    if (get_option('iic_check_saas_status') !== 'premium')
        return;

    $mode = get_option('iic_check_auto_index_mode', 'disabled');
    if ($mode === 'disabled')
        return;

    // --- GARDE-FOUS : n'indexer que du contenu public réellement publié ---

    // Révisions, sauvegardes automatiques, brouillons auto : jamais.
    if (wp_is_post_revision($post->ID) || wp_is_post_autosave($post->ID))
        return;

    // Uniquement les types gérés par le plugin (articles, pages, produits Woo).
    // Écarte les types internes créés par d'autres extensions (wp_sync_storage,
    // commandes, champs ACF...) dont l'URL n'est pas une page publique.
    if (!in_array($post->post_type, iic_check_post_types(), true))
        return;

    // Le type de contenu doit être public.
    $pt_obj = get_post_type_object($post->post_type);
    if (!$pt_obj || empty($pt_obj->public))
        return;

    // Ni contenu privé, ni protégé par mot de passe.
    if ($post->post_status !== 'publish' || !empty($post->post_password))
        return;

    $url = get_permalink($post->ID);
    if (empty($url))
        return;

    // URL non définitive : soit un permalien interne (?post_type=...), soit un
    // ?p=123 alors que le site utilise des permaliens jolis (le slug final n'est
    // pas encore établi). Envoyer ça à Google indexerait une mauvaise URL.
    $has_pretty_permalinks = (bool) get_option('permalink_structure');
    if (strpos($url, '?post_type=') !== false
        || ($has_pretty_permalinks && preg_match('/[?&]p=\d+/', $url))) {
        iic_check_log("[AUTO] Ignoré : URL non définitive ($url)", 'warning');
        return;
    }

    if ($mode === 'google') {
        $json = get_option('iic_check_google_json_key');
        if (empty($json))
            return;
        try {
            $client = new Google_Client();
            $client->setAuthConfig($json);
            $client->addScope('https://www.googleapis.com/auth/indexing');
            $service = new Google_Service_Indexing($client);
            $body = new Google_Service_Indexing_UrlNotification();
            $body->setUrl($url);
            $body->setType('URL_UPDATED');
            $service->urlNotifications->publish($body);
            iic_check_log("[AUTO-GOOGLE] Envoyé : $url", 'success');
            iic_check_log_auth_action('free_index');
        } catch (Exception $e) {
            iic_check_log("[AUTO-GOOGLE] Erreur", 'error');
        }
    } elseif ($mode === 'premium') {
        $res = iic_check_call_premium_relay($url);
        if (isset($res['status']) && $res['status'] === 'ok') {
            iic_check_add_history($url);
            if (isset($res['new_credits'])) {
                update_option('iic_check_user_credits', $res['new_credits']);
            }
            iic_check_log("[AUTO-PREMIUM] 🚀 Envoyé : $url", 'success');
            iic_check_log_auth_action('premium_index');
        } else {
            iic_check_log("[AUTO-PREMIUM] Erreur", 'error');
        }
    } elseif ($mode === 'instant') {
        if (!iic_check_instant_enabled()) {
            iic_check_log("[AUTO-TURBO] Ignoré : Instant Indexing temporairement indisponible.", 'warning');
            return;
        }
        $res = iic_check_call_instant_relay($url);
        if (isset($res['status']) && $res['status'] === 'ok') {
            iic_check_add_history($url);
            if (isset($res['new_credits'])) {
                update_option('iic_check_user_credits', $res['new_credits']);
            }
            iic_check_log("[AUTO-TURBO] ⚡ Envoyé : $url", 'success');
            iic_check_log_auth_action('instant_index');
        } else {
            iic_check_log("[AUTO-TURBO] Erreur", 'error');
        }
    }
}

// --- AJAX HANDLERS ---

add_action('wp_ajax_iic_check_fetch_urls', function () {
    if (!current_user_can('manage_options'))
        wp_die();

    // Unclosed ob_start issue fixed - using it just to catch unexpected output, then clean
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $filter = isset($_GET['filter']) ? sanitize_key(wp_unslash($_GET['filter'])) : '30days';
    $statusFilter = isset($_GET['statusFilter']) ? sanitize_key(wp_unslash($_GET['statusFilter'])) : 'all';

    $is_premium = (get_option('iic_check_saas_status') === 'premium');

    // Free users cannot view all-time, 90days, or custom URLs — lock to 30days
    if (!$is_premium && ($filter === 'all' || $filter === '90days' || $filter === 'custom')) {
        ob_end_clean();
        iic_check_send_json(['premium_required' => true, 'items' => [], 'total' => 0, 'pages' => 0, 'current_page' => 1, 'stats' => []]);
        return;
    }

    // Les gros sites demandent toutes leurs URLs d'un coup (sélection globale).
    // Sans marge, PHP dépasse sa limite de temps ou de mémoire et renvoie une
    // réponse VIDE, que le navigateur signalait par « JSON.parse: unexpected end
    // of data ». On desserre les limites pour cette requête de lecture.
    @set_time_limit(0);
    if (function_exists('wp_raise_memory_limit')) {
        wp_raise_memory_limit('admin');
    }

    $paged = isset($_GET['paged']) ? intval($_GET['paged']) : 1;
    $limit = isset($_GET['limit']) ? intval($_GET['limit']) : 20;

    $args = [
        'post_type' => iic_check_post_types(),
        'post_status' => 'publish',
        'posts_per_page' => $limit,
        'paged' => $paged,
        'orderby' => 'modified',
        'order' => 'DESC'
    ];

    global $wpdb;
    $date_sql = "";

    if ($filter === 'custom') {
        $dateFrom = isset($_GET['dateFrom']) ? sanitize_text_field(wp_unslash($_GET['dateFrom'])) : '';
        $dateTo   = isset($_GET['dateTo'])   ? sanitize_text_field(wp_unslash($_GET['dateTo']))   : '';
        // Validate format YYYY-MM-DD
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateFrom) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateTo)) {
            $args['date_query'] = [[
                'column'    => 'post_modified_gmt',
                'after'     => $dateFrom . ' 00:00:00',
                'before'    => $dateTo . ' 23:59:59',
                'inclusive' => true,
            ]];
            $date_sql = $wpdb->prepare(
                " AND p.post_modified_gmt >= %s AND p.post_modified_gmt <= %s",
                $dateFrom . ' 00:00:00',
                $dateTo . ' 23:59:59'
            );
        }
    } elseif ($filter !== 'all') {
        $map = ['7days' => '7 days ago', '30days' => '30 days ago', '90days' => '90 days ago'];
        if (isset($map[$filter])) {
            $args['date_query'] = [['column' => 'post_modified_gmt', 'after' => $map[$filter]]];
        }

        $days_map = ['7days' => '7', '30days' => '30', '90days' => '90'];
        if (isset($days_map[$filter])) {
            $days = $days_map[$filter];
            $date_sql = " AND p.post_modified_gmt > DATE_SUB(NOW(), INTERVAL $days DAY)";
        }
    }

    // --- Search filter ---
    $search = isset($_GET['search']) ? sanitize_text_field(wp_unslash($_GET['search'])) : '';
    if (!empty($search)) {
        $args['s'] = $search;
    }

    // --- Compute Global Stats for the date range ---
    $count_sql = "
        SELECT 
            COALESCE(NULLIF(pm.meta_value, ''), 'unknown') as status, 
            COUNT(DISTINCT p.ID) as count
        FROM {$wpdb->posts} p
        LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_sip_index_status'
        WHERE p.post_type IN (" . iic_check_post_types_sql() . ") AND p.post_status = 'publish' $date_sql
        GROUP BY status
    ";
    
    $results = $wpdb->get_results($count_sql);
    $stats = ['indexed' => 0, 'not_indexed' => 0, 'waiting' => 0, 'unknown' => 0, 'checking' => 0, 'error' => 0];
    $total_posts = 0;
    if ($results) {
        foreach ($results as $row) {
            $status = empty($row->status) ? 'unknown' : $row->status;
            if (!isset($stats[$status])) $stats[$status] = 0;
            $stats[$status] += (int) $row->count;
            $total_posts += (int) $row->count;
        }
    }

    // --- Status Filtering logic ---
    if ($statusFilter !== 'all') {
        if ($statusFilter === 'unknown') {
            $args['meta_query'] = [
                'relation' => 'OR',
                [
                    'key' => '_sip_index_status',
                    'compare' => 'NOT EXISTS'
                ],
                [
                    'key' => '_sip_index_status',
                    'value' => '',
                    'compare' => '='
                ],
                [
                    'key' => '_sip_index_status',
                    'value' => 'unknown',
                    'compare' => '='
                ]
            ];
        } else {
            $args['meta_query'] = [
                [
                    'key' => '_sip_index_status',
                    'value' => $statusFilter,
                    'compare' => '='
                ]
            ];
        }
    }

    $data = [];
    
    if ($limit > 500) {
        // Plafond volontairement raisonnable : construire 100 000 lignes (avec un
        // get_permalink() par article) épuisait la mémoire PHP et faisait renvoyer
        // une réponse vide au lieu d'un JSON.
        $limit_val = ($limit >= 999999) ? 20000 : $limit;
        
        $status_filter_sql = "";
        if ($statusFilter !== 'all') {
            if ($statusFilter === 'unknown') {
                $status_filter_sql = " AND (pm.meta_value IS NULL OR pm.meta_value = '' OR pm.meta_value = 'unknown')";
            } else {
                $status_filter_sql = $wpdb->prepare(" AND pm.meta_value = %s", $statusFilter);
            }
        }

        $sql = "
            SELECT p.ID, p.post_title, p.post_modified, pm.meta_value as status
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_sip_index_status'
            WHERE p.post_type IN (" . iic_check_post_types_sql() . ") AND p.post_status = 'publish'
            $date_sql $status_filter_sql
            ORDER BY p.post_modified DESC LIMIT %d
        ";
        
        $raw_posts = $wpdb->get_results($wpdb->prepare($sql, $limit_val));
        
        if ($raw_posts) {
            foreach ($raw_posts as $p) {
                $status = empty($p->status) ? 'unknown' : $p->status;
                $data[] = [
                    'id' => $p->ID,
                    'title' => $p->post_title,
                    'url' => get_permalink($p->ID),
                    'date' => date('d/m/Y', strtotime($p->post_modified)),
                    'saved_status' => $status,
                    'waiting_hours' => iic_check_waiting_hours($p->ID, $status)
                ];
                // Prevent memory leak on large loops
                clean_post_cache($p->ID);
            }
        }
        // Compute accurate total count that also applies the status filter
        if ($statusFilter === 'all') {
            $found_posts = $total_posts;
        } else {
            $count_filtered_sql = "
                SELECT COUNT(DISTINCT p.ID)
                FROM {$wpdb->posts} p
                LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_sip_index_status'
                WHERE p.post_type IN (" . iic_check_post_types_sql() . ") AND p.post_status = 'publish'
                $date_sql $status_filter_sql
            ";
            $found_posts = (int) $wpdb->get_var($count_filtered_sql);
        }
        $max_num_pages = 1;
    } else {
        $q = new WP_Query($args);
        if ($q->have_posts()) {
            while ($q->have_posts()) {
                $q->the_post();
                $post_id = get_the_ID();
                $status = get_post_meta($post_id, '_sip_index_status', true);
                if (empty($status)) {
                    $status = 'unknown';
                }
                $data[] = [
                    'id' => $post_id,
                    'title' => get_the_title(),
                    'url' => get_permalink(),
                    'date' => get_the_modified_date('d/m/Y'),
                    'saved_status' => $status,
                    'waiting_hours' => iic_check_waiting_hours($post_id, $status)
                ];
            }
        }
        wp_reset_postdata();
        $found_posts = $q->found_posts;
        $max_num_pages = $q->max_num_pages;
    }

    // Clean any accidental output before sending JSON
    ob_end_clean();

    iic_check_send_json([
        'items' => $data,
        'total' => (int) $found_posts,
        'pages' => (int) $max_num_pages,
        'current_page' => $paged,
        'stats' => $stats,
        'stats_total' => $total_posts
    ]);
});

add_action('wp_ajax_iic_check_reset_status', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $raw_ids = isset($_POST['ids']) ? wp_unslash($_POST['ids']) : '[]';
    $ids = json_decode(sanitize_text_field($raw_ids), true);

    if (!is_array($ids) || empty($ids)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'No IDs provided'], false);
        return;
    }

    $reset_count = 0;
    foreach ($ids as $id) {
        $id = intval($id);
        if ($id > 0) {
            delete_post_meta($id, '_sip_index_status');
            $reset_count++;
        }
    }

    iic_check_log("\u21ba Status reset for {$reset_count} URL(s)", 'info');
    ob_end_clean();
    iic_check_send_json(['reset_count' => $reset_count]);
});

add_action('wp_ajax_iic_check_exclude_status', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $raw_ids = isset($_POST['ids']) ? wp_unslash($_POST['ids']) : '[]';
    $ids = json_decode(sanitize_text_field($raw_ids), true);

    if (!is_array($ids) || empty($ids)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'No IDs provided'], false);
        return;
    }

    $exclude_count = 0;
    foreach ($ids as $id) {
        $id = intval($id);
        if ($id > 0) {
            update_post_meta($id, '_sip_index_status', 'excluded');
            $exclude_count++;
        }
    }

    iic_check_log("🚫 Status excluded for {$exclude_count} URL(s)", 'info');
    ob_end_clean();
    iic_check_send_json(['exclude_count' => $exclude_count]);
});

add_action('wp_ajax_iic_check_activate_license', function () {

    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');

    // Sanitization added
    $key = isset($_POST['key']) ? sanitize_text_field(wp_unslash($_POST['key'])) : '';
    $site_url = get_site_url();

    $r = wp_remote_post(IIC_CHECK_API_URL, ['body' => ['license_key' => $key, 'site_url' => $site_url]]);

    if (is_wp_error($r)) {
        iic_check_send_json(['msg' => 'Erreur API'], false);
        return;
    }

    $b = json_decode(wp_remote_retrieve_body($r), true);

    if (!is_array($b) || !isset($b['status'])) {
        iic_check_log("Réponse API illisible lors de l'activation", 'error');
        iic_check_send_json(['msg' => 'Réponse serveur invalide.'], false);
        return;
    }

    // Synchronise l'état d'Instant Indexing piloté depuis le panel admin.
    if (array_key_exists('instant_enabled', $b)) {
        update_option('iic_check_instant_enabled', !empty($b['instant_enabled']) ? '1' : '0');
    }

    if ($b['status'] === 'active') {
        update_option('iic_check_saas_license_key', $key);
        // Use server-side premium status instead of key prefix
        $isPrem = !empty($b['is_premium']);
        
        if ($isPrem) {
            update_option('iic_check_saas_status', 'premium');
            update_option('iic_check_auto_index_enabled', true);
        } else {
            // User has a valid key but is not premium (e.g. CRED- key with credits only)
            $hasCredits = isset($b['credits']) && (int)$b['credits'] > 0;
            update_option('iic_check_saas_status', $hasCredits ? 'credits_only' : 'free');
        }

        // Sauvegarde des crédits récupérés
        if (isset($b['credits'])) {
            update_option('iic_check_user_credits', $b['credits']);
        }

        iic_check_send_json([
            'status' => $isPrem ? 'premium' : (isset($b['credits']) && (int)$b['credits'] > 0 ? 'credits_only' : 'free'),
            'credits' => isset($b['credits']) ? (int)$b['credits'] : 0
        ]);

    } elseif ($b['status'] === 'expired') {
        update_option('iic_check_saas_status', 'free');
        update_option('iic_check_saas_license_key', ''); // On retire la clé pour forcer une ré-activation ou passage au gratuit
        iic_check_log("Licence expirée", 'warning');
        iic_check_send_json(['msg' => 'Licence expirée.'], false);
    } else {
        iic_check_log("Erreur activation licence (Clé invalide)", 'error');
        iic_check_send_json(['msg' => 'Clé invalide ou erreur réseau.'], false);
    }
});

add_action('wp_ajax_iic_check_refresh_credits', function() {
    if (!current_user_can('manage_options')) wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $key = get_option('iic_check_saas_license_key');
    if (empty($key)) {
        iic_check_send_json(['credits' => 0]);
        return;
    }

    $site_url = get_site_url();
    $r = wp_remote_post(IIC_CHECK_API_URL, ['body' => ['license_key' => $key, 'site_url' => $site_url], 'timeout' => 5]);

    if (is_wp_error($r)) {
        iic_check_send_json(['credits' => get_option('iic_check_user_credits', 0)]);
        return;
    }

    $b = json_decode(wp_remote_retrieve_body($r), true);
    // Synchronise l'état d'Instant Indexing piloté depuis le panel admin.
    if (is_array($b) && array_key_exists('instant_enabled', $b)) {
        update_option('iic_check_instant_enabled', !empty($b['instant_enabled']) ? '1' : '0');
    }
    if (isset($b['credits'])) {
        update_option('iic_check_user_credits', $b['credits']);
        iic_check_log("Crédits synchronisés en direct ({$b['credits']})", 'info');
        iic_check_send_json(['credits' => (int)$b['credits'], 'instant_enabled' => iic_check_instant_enabled()]);
    } else {
        iic_check_send_json(['credits' => get_option('iic_check_user_credits', 0)]);
    }
});

add_action('wp_ajax_iic_check_go_free', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
    if (empty($email)) {
        iic_check_send_json(['msg' => 'Email requis'], false);
        return;
    }
    update_option('iic_check_saas_status', 'free');
    wp_remote_post(IIC_CHECK_API_URL, [
        'body' => ['license_key' => 'FREE', 'site_url' => get_site_url(), 'email' => $email],
        'timeout' => 5,
        'blocking' => true
    ]);
    iic_check_log("Mode Gratuit activé", 'info');
    iic_check_send_json('OK');
});

add_action('wp_ajax_iic_check_request_indexing', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');
    // ID transmis par l'interface : bien plus fiable que de deviner depuis l'URL.
    $hinted_post_id = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;
    try {
        $client = new Google_Client();
        $client->setAuthConfig(get_option('iic_check_google_json_key'));
        $client->addScope('https://www.googleapis.com/auth/indexing');
        $service = new Google_Service_Indexing($client);
        if (!isset($_POST['url'])) {
            throw new Exception('Missing URL');
        }
        $url = esc_url_raw(wp_unslash($_POST['url']));

        $body = new Google_Service_Indexing_UrlNotification();
        $body->setUrl($url);
        $body->setType('URL_UPDATED');
        $service->urlNotifications->publish($body);

        $post_id = iic_check_resolve_post_id($url, $hinted_post_id ?? null);
        if ($post_id) {
            iic_check_mark_waiting($post_id);
        }

        iic_check_log("Google Standard : {$url}", 'success');
        iic_check_log_auth_action('free_index');
        ob_end_clean();
        iic_check_send_json('OK');
    } catch (Exception $e) {
        ob_end_clean();
        $msg = $e->getMessage();
        iic_check_log("Google Std Error: " . $msg, 'error');
        iic_check_send_json(['msg' => $msg], false);
    }
});

add_action('wp_ajax_iic_check_premium_force', function () {
    if (!current_user_can('manage_options'))
        wp_die();

    // Fix output buffering
    ob_start();

    check_ajax_referer('iic_check_security_token', 'nonce');
    // ID transmis par l'interface : bien plus fiable que de deviner depuis l'URL.
    $hinted_post_id = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;

    if (get_option('iic_check_saas_status') !== 'premium') {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Premium Requis'], false);
        return;
    }

    if (!isset($_POST['url'])) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'URL manquante'], false);
        return;
    }
    $url = esc_url_raw(wp_unslash($_POST['url']));
    $res = iic_check_call_premium_relay($url);

    // Clean buffer
    ob_end_clean();

    // 'pending' : la demande est partie mais le service n'a pas confirmé à temps.
    // Elle est très probablement prise en compte (et déjà facturée) : on traite
    // ce cas comme un envoi réussi, l'URL reste « en attente ».
    $relay_status = $res['status'] ?? '';
    if (in_array($relay_status, ['ok', 'pending'], true)) {
        iic_check_add_history($url);

        // Mise à jour locale des crédits
        if (isset($res['new_credits'])) {
            update_option('iic_check_user_credits', $res['new_credits']);
        }

        $post_id = iic_check_resolve_post_id($url, $hinted_post_id ?? null);
        if ($post_id) {
            iic_check_mark_waiting($post_id);
        }

        if ($relay_status === 'pending') {
            iic_check_log("💎 Premium Force (confirmation en attente) : " . $url, 'info');
        } else {
            iic_check_log("💎 Premium Force : " . $url, 'success');
        }
        iic_check_log_auth_action('premium_index');
        // On renvoie les nouveaux crédits au JS
        iic_check_send_json(['status' => 'ok', 'new_credits' => $res['new_credits'] ?? null]);
    } else {
        $msg = $res['message'] ?? 'Erreur Inconnue';
        iic_check_log("Erreur Premium : " . $msg, 'error');
        iic_check_send_json(['msg' => $msg], false);
    }
});

// --- AJAX : INSTANT INDEXING (1 URL — 10 crédits) ---
add_action('wp_ajax_iic_check_instant_index', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');
    // ID transmis par l'interface : bien plus fiable que de deviner depuis l'URL.
    $hinted_post_id = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;

    if (!iic_check_instant_enabled()) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Instant Indexing est temporairement indisponible. Aucun crédit n\'a été consommé.'], false);
        return;
    }

    if (get_option('iic_check_saas_status') !== 'premium') {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Premium Requis'], false);
        return;
    }

    $credits = (float) get_option('iic_check_user_credits', 0);
    if ($credits < 10) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Crédits insuffisants (10 requis pour Instant Indexing)'], false);
        return;
    }

    if (!isset($_POST['url'])) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'URL manquante'], false);
        return;
    }

    $url = esc_url_raw(wp_unslash($_POST['url']));
    $res = iic_check_call_instant_relay($url);
    ob_end_clean();

    if (isset($res['status']) && $res['status'] === 'ok') {
        iic_check_add_history($url);
        if (isset($res['new_credits'])) {
            update_option('iic_check_user_credits', $res['new_credits']);
        }
        $post_id = iic_check_resolve_post_id($url, $hinted_post_id ?? null);
        if ($post_id) {
            iic_check_mark_waiting($post_id);
        }
        iic_check_log("⚡ Instant Index : " . $url, 'success');
        iic_check_log_auth_action('instant_index');
        iic_check_send_json(['status' => 'ok', 'new_credits' => $res['new_credits'] ?? null]);
    } else {
        $msg = $res['message'] ?? 'Erreur Inconnue';
        iic_check_log("Erreur Instant Index : " . $msg, 'error');
        iic_check_send_json(['msg' => $msg], false);
    }
});

// --- AJAX : INSTANT INDEXING BULK (10 crédits/URL) ---
add_action('wp_ajax_iic_check_instant_index_bulk', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    if (!iic_check_instant_enabled()) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Instant Indexing est temporairement indisponible. Aucun crédit n\'a été consommé.'], false);
        return;
    }

    if (get_option('iic_check_saas_status') !== 'premium') {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Premium Requis'], false);
        return;
    }

    $urls_raw = isset($_POST['urls']) ? wp_unslash($_POST['urls']) : '[]';
    $urls_array = json_decode($urls_raw, true);
    if (!is_array($urls_array) || empty($urls_array)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Aucune URL fournie'], false);
        return;
    }
    $urls_array = array_map('esc_url_raw', $urls_array);
    $count = count($urls_array);

    $credits = (float) get_option('iic_check_user_credits', 0);
    $cost = $count * 10;
    if ($credits < $cost) {
        ob_end_clean();
        iic_check_send_json(['msg' => "Crédits insuffisants : {$cost} crédits requis, vous en avez {$credits}"], false);
        return;
    }

    $res = iic_check_call_instant_relay_bulk($urls_array);
    ob_end_clean();

    if (isset($res['status']) && $res['status'] === 'ok') {
        if (isset($res['new_credits'])) {
            update_option('iic_check_user_credits', $res['new_credits']);
        }
        foreach ($urls_array as $url) {
            iic_check_add_history($url);
            $post_id = iic_check_resolve_post_id($url, $hinted_post_id ?? null);
            if ($post_id) {
                iic_check_mark_waiting($post_id);
            }
        }
        iic_check_log("⚡ Instant Index Bulk : {$count} URLs soumises", 'success');
        iic_check_log_auth_action('instant_index', $count);
        iic_check_send_json([
            'status'        => 'ok',
            'success_count' => $count,
            'credits_used'  => $res['creditsUsed'] ?? ($count * 10),
            'new_credits'   => $res['new_credits'] ?? null,
        ]);
    } else {
        $msg = $res['message'] ?? 'Erreur Inconnue';
        iic_check_log("Erreur Instant Index Bulk : " . $msg, 'error');
        iic_check_send_json(['msg' => $msg], false);
    }
});

add_action('wp_ajax_iic_check_save_auto_mode', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    if (get_option('iic_check_saas_status') !== 'premium') {
        iic_check_send_json(['msg' => 'Premium Only'], false);
        return;
    }
    $mode = isset($_POST['mode']) ? sanitize_text_field(wp_unslash($_POST['mode'])) : 'disabled';
    update_option('iic_check_auto_index_mode', $mode);

    $license_key = get_option('iic_check_saas_license_key', 'unknown_or_free');
    $site_url = get_site_url();
    wp_remote_post('https://wp-instant-indexing.com/log_action.php', [
        'body' => [
            'license_key' => $license_key,
            'action_type' => 'set_api_mode',
            'site_url' => $site_url,
            'api_mode' => $mode
        ],
        'blocking' => false,
        'timeout' => 2
    ]);

    iic_check_log("Auto Mode modifié : " . strtoupper($mode), 'info');
    iic_check_send_json('OK');
});

add_action('wp_ajax_iic_check_save_sys_config', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    $locale = isset($_POST['locale']) ? sanitize_text_field(wp_unslash($_POST['locale'])) : 'en_US';
    update_option('iic_check_locale', $locale);
    iic_check_send_json('OK');
});

add_action('wp_ajax_iic_check_save_key', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    $raw = isset($_POST['json_key']) ? wp_unslash($_POST['json_key']) : '';
    if (!empty(trim($raw))) {
        $dec = json_decode($raw, true);

        // Strict Validation & Sanitization against Google Service Account structure
        // Reviewer Note: We validate structure and sanitize individual fields instead of trusting raw json_decode
        if (is_array($dec) && isset($dec['type'], $dec['project_id'], $dec['private_key'], $dec['client_email'])) {
            $clean_key = [
                'type' => sanitize_text_field($dec['type']),
                'project_id' => sanitize_text_field($dec['project_id']),
                'private_key_id' => isset($dec['private_key_id']) ? sanitize_text_field($dec['private_key_id']) : '',
                'private_key' => $dec['private_key'], // Keep raw for RSA internal newlines (required for openssl)
                'client_email' => sanitize_email($dec['client_email']),
                'client_id' => isset($dec['client_id']) ? sanitize_text_field($dec['client_id']) : '',
                'auth_uri' => isset($dec['auth_uri']) ? esc_url_raw($dec['auth_uri']) : '',
                'token_uri' => isset($dec['token_uri']) ? esc_url_raw($dec['token_uri']) : '',
                'auth_provider_x509_cert_url' => isset($dec['auth_provider_x509_cert_url']) ? esc_url_raw($dec['auth_provider_x509_cert_url']) : '',
                'client_x509_cert_url' => isset($dec['client_x509_cert_url']) ? esc_url_raw($dec['client_x509_cert_url']) : ''
            ];
            update_option('iic_check_google_json_key', $clean_key);
        } else {
            iic_check_send_json(['msg' => 'Invalid JSON Structure (Missing Service Account fields)'], false);
            return;
        }
    }
    $raw_gsc = isset($_POST['gsc_url']) ? wp_unslash($_POST['gsc_url']) : '';
    $gsc_url = trim($raw_gsc);
    if ($gsc_url !== '') {
        if (preg_match('#^https?://#i', $gsc_url)) {
            $gsc_url = esc_url_raw($gsc_url);
        } elseif (preg_match('#^sc-domain:[a-z0-9.-]+$#i', $gsc_url)) {
            $gsc_url = sanitize_text_field($gsc_url);
        } elseif (preg_match('#^[a-z0-9.-]+/?$#i', $gsc_url)) {
            $gsc_url = rtrim(sanitize_text_field($gsc_url), '/');
            $gsc_url = 'sc-domain:' . $gsc_url;
        } else {
            $gsc_url = sanitize_text_field($gsc_url);
        }
    }
    $api_mode = isset($_POST['api_mode']) && in_array($_POST['api_mode'], ['google', 'proxy_premium']) ? $_POST['api_mode'] : 'google';

    update_option('iic_check_gsc_site_url', $gsc_url);
    update_option('iic_check_api_mode', $api_mode);
    update_option('iic_check_setup_done', true);

    // Intégration WooCommerce (produits, catégories, étiquettes, marques)
    if (isset($_POST['woo_enabled'])) {
        update_option('iic_check_woo_enabled', $_POST['woo_enabled'] === '1' ? '1' : '0');
    }

    iic_check_send_json('OK');
});

// --- PREMIUM CHECK (SPEEDY INDEXER) ---
add_action('wp_ajax_iic_check_premium_check_url', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    if (get_option('iic_check_saas_status') !== 'premium') {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Premium Requis'], false);
        return;
    }

    $credits = (float) get_option('iic_check_user_credits', 0);
    if ($credits < 0.2) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Crédits insuffisants (0.2 requis)'], false);
        return;
    }

    $url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : '';
    if (empty($url)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'URL manquante'], false);
        return;
    }

    $license_key = get_option('iic_check_saas_license_key');
    $response = wp_remote_post(IIC_CHECK_SPEEDY_RELAY_URL, [
        'body'    => ['license_key' => $license_key, 'action' => 'create', 'url' => $url],
        'timeout' => 30
    ]);

    ob_end_clean();

    if (is_wp_error($response)) {
        iic_check_send_json(['msg' => 'Erreur de connexion au service Premium Check'], false);
        return;
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);
    if (isset($body['status']) && $body['status'] === 'ok' && isset($body['task_id'])) {
        iic_check_send_json(['task_id' => $body['task_id']]);
    } else {
        $msg = $body['message'] ?? 'Erreur du service Premium Check';
        iic_check_send_json(['msg' => $msg], false);
    }
});

add_action('wp_ajax_iic_check_premium_check_status', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');
    // ID transmis par l'interface : bien plus fiable que de deviner depuis l'URL.
    $hinted_post_id = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;

    $task_id = isset($_POST['task_id']) ? sanitize_text_field(wp_unslash($_POST['task_id'])) : '';
    $url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : '';

    if (empty($task_id) || empty($url)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Paramètres manquants'], false);
        return;
    }

    $license_key = get_option('iic_check_saas_license_key');
    $response = wp_remote_post(IIC_CHECK_SPEEDY_RELAY_URL, [
        'body'    => ['license_key' => $license_key, 'action' => 'status', 'task_id' => $task_id],
        'timeout' => 60
    ]);

    ob_end_clean();

    if (is_wp_error($response)) {
        iic_check_send_json(['msg' => 'Erreur de connexion Status (relay)'], false);
        return;
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);
    if (!isset($body['status'])) {
        iic_check_send_json(['msg' => 'Erreur de lecture du statut (relay)'], false);
        return;
    }

    // Adapter la structure relay vers la structure attendue par la suite
    if ($body['status'] === 'error') {
        iic_check_send_json(['msg' => $body['message'] ?? 'Erreur relay status'], false);
        return;
    }

    // Simuler la structure result pour la suite du code
    $body['result'] = [
        'is_completed' => ($body['status'] === 'completed') ? true : false,
    ];

    $result = $body['result'];
    if (empty($result['is_completed'])) {
        // Not done yet — on transmet le temps estimé (eta_minutes) si dispo
        iic_check_send_json(['status' => 'checking', 'eta_minutes' => $body['eta_minutes'] ?? null]);
        return;
    }

    // Task is completed. Deduct credits.
    $credits = (float) get_option('iic_check_user_credits', 0);
    $new_credits = max(0, $credits - 0.2);
    update_option('iic_check_user_credits', $new_credits);

    // Sync new credit count with remote server if possible, or just local. 
    // We update local, and the background sync check will sync it up/down depending on auth truth.
    // For safety, let's also pass the new balance to front-end.
    
    // Check if it's indexed
    $is_indexed = (isset($result['indexed_count']) && $result['indexed_count'] > 0);
    $status = $is_indexed ? 'indexed' : 'not_indexed';

    $post_id = iic_check_resolve_post_id($url, $hinted_post_id ?? null);
    if ($post_id) {
        update_post_meta($post_id, '_sip_index_status', $status);
    }

    iic_check_log("Premium Check terminé : {$url} -> ". strtoupper($status), $is_indexed ? 'success' : 'warning');
    iic_check_log_auth_action('premium_check');
    iic_check_send_json(['status' => $status, 'new_credits' => number_format((float)$new_credits, 1, '.', '')]);
});

// --- PREMIUM CHECK (SPEEDY INDEXER) - BULK ---
add_action('wp_ajax_iic_check_premium_check_bulk', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    if (get_option('iic_check_saas_status') !== 'premium') {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Premium Requis'], false);
        return;
    }

    $raw_urls = isset($_POST['urls']) ? json_decode(wp_unslash($_POST['urls']), true) : [];
    if (empty($raw_urls) || !is_array($raw_urls)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'URLs manquantes ou invalides'], false);
        return;
    }

    $urls = array_filter(array_map('esc_url_raw', $raw_urls));
    if (empty($urls)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Aucune URL valide fournie'], false);
        return;
    }

    // Increase execution time limit — no hard cap on URL count since JS polling has no timeout
    @set_time_limit(0);

    $cost = count($urls) * 0.2;
    $credits = (float) get_option('iic_check_user_credits', 0);

    if ($credits < $cost) {
        ob_end_clean();
        iic_check_send_json(['msg' => "Crédits insuffisants. Requis : " . number_format($cost, 1) . ". Actuels : " . number_format($credits, 1)], false);
        return;
    }

    $license_key = get_option('iic_check_saas_license_key');
    $response = wp_remote_post(IIC_CHECK_SPEEDY_RELAY_URL, [
        'body'    => [
            'license_key' => $license_key,
            'action'      => 'create',
            'urls'        => json_encode(array_values($urls))
        ],
        'timeout' => 30
    ]);

    ob_end_clean();

    if (is_wp_error($response)) {
        iic_check_send_json(['msg' => 'Erreur de connexion au service Premium Check'], false);
        return;
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);
    if (isset($body['status']) && $body['status'] === 'ok' && isset($body['task_id'])) {
        // Persister la tâche en DB pour survie au rechargement de page
        update_option('iic_check_pending_task', [
            'task_id'    => $body['task_id'],
            'urls'       => array_values($urls),
            'created_at' => time(),
        ]);
        iic_check_send_json(['task_id' => $body['task_id']]);
    } else {
        $msg = $body['message'] ?? 'Erreur du service Premium Check';
        iic_check_send_json(['msg' => $msg], false);
    }
});

add_action('wp_ajax_iic_check_premium_check_status_bulk', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

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

    if (empty($task_id)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Paramètre task_id manquant'], false);
        return;
    }

    // STATUS CHECK via relay
    $license_key = get_option('iic_check_saas_license_key');
    $status_response = wp_remote_post(IIC_CHECK_SPEEDY_RELAY_URL, [
        'body'    => ['license_key' => $license_key, 'action' => 'status', 'task_id' => $task_id],
        'timeout' => 60
    ]);

    if (is_wp_error($status_response)) {
        ob_end_clean();
        // Return a retryable error — JS will silently retry without alerting the user
        iic_check_send_json(['msg' => 'relay_timeout', 'retryable' => true], false);
        return;
    }

    $status_body = json_decode(wp_remote_retrieve_body($status_response), true);

    if (!isset($status_body['status'])) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Réponse relay invalide'], false);
        return;
    }

    if ($status_body['status'] === 'error') {
        ob_end_clean();
        iic_check_send_json(['msg' => $status_body['message'] ?? 'Erreur relay status'], false);
        return;
    }

    if ($status_body['status'] === 'checking') {
        ob_end_clean();
        iic_check_send_json(['status' => 'checking', 'processed' => $status_body['processed'] ?? 0, 'size' => $status_body['size'] ?? 0, 'eta_minutes' => $status_body['eta_minutes'] ?? null]);
        return;
    }

    // status === 'completed' : on récupère le rapport
    $report_response = wp_remote_post(IIC_CHECK_SPEEDY_RELAY_URL, [
        'body'    => ['license_key' => $license_key, 'action' => 'report', 'task_id' => $task_id],
        'timeout' => 60
    ]);

    ob_end_clean();

    if (is_wp_error($report_response)) {
        iic_check_send_json(['msg' => 'Erreur de connexion Report (relay)'], false);
        return;
    }

    $report_body = json_decode(wp_remote_retrieve_body($report_response), true);
    if (!isset($report_body['status']) || $report_body['status'] !== 'ok') {
        $msg = $report_body['message'] ?? 'Erreur de lecture du rapport (relay)';
        iic_check_send_json(['msg' => $msg], false);
        return;
    }

    $indexed_links   = (array)($report_body['indexed_links']   ?? []);
    $unindexed_links = (array)($report_body['unindexed_links'] ?? []);
    $new_credits     = $report_body['new_credits'] ?? get_option('iic_check_user_credits', 0);

    // Mise à jour locale du solde
    update_option('iic_check_user_credits', $new_credits);

    $results = [];
    $normalize_url = function($u) {
        return untrailingslashit(esc_url_raw($u));
    };

    foreach ($indexed_links as $url) {
        $clean_url = $normalize_url($url);
        $post_id = iic_check_resolve_post_id($clean_url) ?: iic_check_resolve_post_id($url);
        if ($post_id) {
            update_post_meta($post_id, '_sip_index_status', 'indexed');
        }
        $results[$clean_url] = 'indexed';
        $results[$url]       = 'indexed';
    }

    foreach ($unindexed_links as $url) {
        $clean_url = $normalize_url($url);
        $post_id = iic_check_resolve_post_id($clean_url) ?: iic_check_resolve_post_id($url);
        if ($post_id) {
            update_post_meta($post_id, '_sip_index_status', 'not_indexed');
        }
        $results[$clean_url] = 'not_indexed';
        $results[$url]       = 'not_indexed';
    }

    $c_idx = count($indexed_links);
    $c_unidx = count($unindexed_links);
    // Tâche terminée : effacer le pending task de la DB
    delete_option('iic_check_pending_task');
    iic_check_log("Premium Check Bulk terminé : $c_idx indexée(s), $c_unidx non indexée(s).", 'info');
    iic_check_log_auth_action('premium_check', $c_idx + $c_unidx);
    iic_check_send_json([
        'status'      => 'completed',
        'results'     => $results,
        'new_credits' => number_format((float)$new_credits, 1, '.', '')
    ]);
});

// --- EXTERNAL INDEXING (PREMIUM ONLY) ---
add_action('wp_ajax_iic_check_external_index', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $is_premium = get_option('iic_check_saas_status') === 'premium';
    if (!$is_premium) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Fonction réservée aux utilisateurs Premium (ou Licence Invalide)'], false);
        return;
    }

    $raw_urls = isset($_POST['urls']) ? wp_unslash($_POST['urls']) : '';
    if (empty($raw_urls)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Aucune URL fournie'], false);
        return;
    }

    $urls = array_filter(array_map('trim', explode("\n", $raw_urls)));
    $urls = array_filter($urls, function ($u) { return filter_var($u, FILTER_VALIDATE_URL); });
    $urls = array_values(array_unique($urls));

    if (empty($urls)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Aucune URL valide trouvée'], false);
        return;
    }

    $credits = (float) get_option('iic_check_user_credits', 0);
    $cost = count($urls);

    if ($credits < $cost) {
        ob_end_clean();
        iic_check_send_json(['msg' => "Crédits insuffisants. Vous avez $credits crédits, et il en faut $cost."], false);
        return;
    }

    $results = [];
    $success_count = 0;

    foreach ($urls as $url) {
        // Envoi au relai Premium Indexer (qui s'occupe de déduire les crédits côté serveur distant)
        $res = iic_check_call_premium_relay($url);

        if (isset($res['status']) && $res['status'] === 'ok') {
            $results[] = ['url' => $url, 'status' => 'success'];
            $success_count++;
            
            // Mise à jour locale des crédits
            if (isset($res['new_credits'])) {
                update_option('iic_check_user_credits', $res['new_credits']);
            }
            
            // Add to index history (just in case they want to track it somewhere)
            $history = get_option('iic_check_index_history', []);
            $history[$url] = current_time('timestamp');
            update_option('iic_check_index_history', $history);
        } else {
            $msg = isset($res['message']) ? $res['message'] : (is_array($res) ? 'JSON Relais ('.json_encode($res).')' : 'Erreur Indexation (Réponse: '.print_r($res, true).')');
            $results[] = ['url' => $url, 'status' => 'error', 'msg' => $msg];
        }
    }

    ob_end_clean();
    $new_credits = get_option('iic_check_user_credits', 0); // Re-fetch as it was modified in the loop by iic_check_call_premium_relay

    if ($success_count > 0) {
        iic_check_log("💎 Premium Force (Externe) en Bulk ($success_count) OK", 'success');
        iic_check_log_auth_action('premium_index');
    }

    iic_check_send_json([
        'results' => $results,
        'success_count' => $success_count,
        'total' => count($urls),
        'new_credits' => number_format((float)$new_credits, 1, '.', '')
    ], true);
});

// --- EXTERNAL INDEXING (PREMIUM ONLY) - BULK ---
add_action('wp_ajax_iic_check_external_index_bulk', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $is_premium = get_option('iic_check_saas_status') === 'premium';
    if (!$is_premium) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Fonction réservée aux utilisateurs Premium (ou Licence Invalide)'], false);
        return;
    }

    $raw_urls = isset($_POST['urls']) ? json_decode(wp_unslash($_POST['urls']), true) : [];
    if (empty($raw_urls) || !is_array($raw_urls)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Aucune URL fournie'], false);
        return;
    }

    $urls = array_filter(array_map('esc_url_raw', $raw_urls));
    $urls = array_values(array_unique($urls));

    if (empty($urls)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Aucune URL valide trouvée'], false);
        return;
    }

    $credits = (float) get_option('iic_check_user_credits', 0);
    $cost = count($urls);

    if ($credits < $cost) {
        ob_end_clean();
        iic_check_send_json(['msg' => "Crédits insuffisants. Vous avez $credits crédits, et il en faut $cost."], false);
        return;
    }

    // Envoi au relai Premium Indexer en BULK
    $res = iic_check_call_premium_relay_bulk($urls);
    $results = [];

    if (isset($res['status']) && $res['status'] === 'ok') {
        foreach ($urls as $url) {
            $results[] = ['url' => $url, 'status' => 'success'];
            
            // Add to history
            $history = get_option('iic_check_index_history', []);
            $history[$url] = current_time('timestamp');
            update_option('iic_check_index_history', $history);
            
            // Met à jour la date d'indexation premium et le statut 'waiting' en BDD
            $clean_url = untrailingslashit(esc_url_raw($url));
            $post_id = iic_check_resolve_post_id($clean_url) ?: iic_check_resolve_post_id($url);
            if ($post_id) {
                iic_check_mark_waiting($post_id);
            }
        }
        $success_count = count($urls);
        
        // Mise à jour locale des crédits
        if (isset($res['new_credits'])) {
            update_option('iic_check_user_credits', $res['new_credits']);
        }
        
        iic_check_log("💎 Premium Indexation Bulk OK ($success_count sites envoyés)", 'success');
        iic_check_log_auth_action('premium_index', $success_count);
        
    } else {
        $msg = isset($res['message']) ? $res['message'] : (is_array($res) ? 'JSON Relais ('.json_encode($res).')' : 'Erreur Indexation (Réponse: '.print_r($res, true).')');
        iic_check_log("Erreur Bulk Premium : $msg", 'error');
        foreach ($urls as $url) {
            $results[] = ['url' => $url, 'status' => 'error', 'msg' => $msg];
        }
        $success_count = 0;
    }

    ob_end_clean();
    $new_credits = get_option('iic_check_user_credits', 0);

    iic_check_send_json([
        'results' => $results,
        'success_count' => $success_count,
        'total' => count($urls),
        'new_credits' => number_format((float)$new_credits, 1, '.', '')
    ], true);
});


add_action('wp_ajax_iic_check_check_url', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');
    // ID transmis par l'interface : bien plus fiable que de deviner depuis l'URL.
    $hinted_post_id = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;
    try {
        if (!isset($_POST['url']))
            throw new Exception('URL Manquante');
        $url = esc_url_raw(wp_unslash($_POST['url']));

        $client = new Google_Client();
        $client->setAuthConfig(get_option('iic_check_google_json_key'));
        $client->addScope('https://www.googleapis.com/auth/webmasters.readonly');
        $service = new Google_Service_SearchConsole($client);
        $site_url = get_option('iic_check_gsc_site_url');
        $req = new Google_Service_SearchConsole_InspectUrlIndexRequest();
        $req->setInspectionUrl($url);
        $req->setSiteUrl($site_url);
        $res = $service->urlInspection_index->inspect($req);
        $v = $res->inspectionResult->indexStatusResult->verdict;

        ob_end_clean();

        $post_id = iic_check_resolve_post_id($url, $hinted_post_id ?? null);

        if (is_null($v)) {
            if ($post_id)
                update_post_meta($post_id, '_sip_index_status', 'not_indexed');
            iic_check_log("Google Check : {$url} -> NON INDEXÉE (Null)", 'warning');
            iic_check_send_json(['status' => 'not_indexed', 'msg' => 'Non Indexée (Null Verdict)']);
        } elseif ($v === 'PASS') {
            if ($post_id)
                update_post_meta($post_id, '_sip_index_status', 'indexed');
            iic_check_log("Google Check : {$url} -> INDEXÉE", 'success');
            iic_check_log_auth_action('free_check');
            iic_check_send_json(['status' => 'indexed']);
        } else {
            if ($post_id)
                update_post_meta($post_id, '_sip_index_status', 'not_indexed');
            iic_check_log("Google Check : {$url} -> NON INDEXÉE", 'warning');
            iic_check_log_auth_action('free_check');
            iic_check_send_json(['status' => 'not_indexed', 'msg' => 'Non Indexée']);
        }
    } catch (Exception $e) {
        ob_end_clean();
        $msg = $e->getMessage();
        iic_check_log("Check Status Error: " . $msg . " (siteUrl={$site_url}, inspectionUrl={$url})", 'error');
        iic_check_send_json(['status' => 'error', 'msg' => $msg], false);
    }
});

add_action('wp_ajax_iic_check_delete_not_indexed', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    if (get_option('iic_check_saas_status') !== 'premium') {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Premium Requis'], false);
        return;
    }

    $args = [
        'post_type' => iic_check_post_types(),
        'post_status' => 'publish',
        'posts_per_page' => -1,
        'fields' => 'ids',
        'meta_query' => [
            [
                'key' => '_sip_index_status',
                'value' => 'not_indexed',
                'compare' => '='
            ]
        ]
    ];
    $q = new WP_Query($args);
    $count = 0;
    if (!empty($q->posts)) {
        foreach ($q->posts as $pid) {
            wp_trash_post($pid);
            $count++;
        }
    }

    iic_check_log("Suppression de $count élément(s) non indexé(s) vers la corbeille.", 'info');
    ob_end_clean();
    iic_check_send_json(['count' => $count]);
});

add_action('wp_ajax_iic_check_logout', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    delete_option('iic_check_saas_status');
    delete_option('iic_check_saas_license_key');
    delete_option('iic_check_user_credits');
    iic_check_send_json('OK');
});

add_action('wp_ajax_iic_check_get_credits', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    check_ajax_referer('iic_check_security_token', 'nonce');
    
    $credits = get_option('iic_check_user_credits', 0);
    $status = get_option('iic_check_saas_status', false);
    
    iic_check_send_json([
        'credits' => (int) $credits,
        'status' => $status
    ]);
});

add_action('wp_ajax_iic_check_debug_api', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    iic_check_send_json(['logs' => ['✅ Test OK']]);
});

function iic_check_log($msg, $type)
{
    $l = get_option('iic_check_logs', []);
    if (!is_array($l))
        $l = [];
    array_unshift($l, ['date' => gmdate('d/m H:i'), 'msg' => $msg, 'type' => $type]);
    if (count($l) > 50)
        array_pop($l);
    update_option('iic_check_logs', $l);
}

function iic_check_log_auth_action($type, $amount = 1) {
    $license = get_option('iic_check_saas_license_key', 'unknown_or_free');
    wp_remote_post('https://wp-instant-indexing.com/log_action.php', [
        'body' => [
            'license_key' => $license,
            'action_type' => $type,
            'amount' => $amount
        ],
        'blocking' => false,
        'timeout' => 1
    ]);
}

add_action('wp_ajax_iic_check_get_logs', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    ob_start();
    $logs = get_option('iic_check_logs', []);
    if (!is_array($logs))
        $logs = [];
    ob_end_clean();
    iic_check_send_json($logs);
});
add_action('wp_ajax_iic_check_clear_logs', function () {
    if (!current_user_can('manage_options'))
        wp_die();
    update_option('iic_check_logs', []);
    iic_check_send_json('OK');
});

// --- AJAX: FETCH TAXONOMY/CATEGORY URLS ---
add_action('wp_ajax_iic_check_fetch_categories', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    // Gather all public, queryable taxonomies
    $taxonomy_names = get_taxonomies(['public' => true, 'show_ui' => true], 'names');

    // Si l'intégration WooCommerce est désactivée, on exclut ses taxonomies
    // (catégories produit, étiquettes, marques).
    if (!iic_check_woo_enabled()) {
        $taxonomy_names = array_diff($taxonomy_names, iic_check_woo_taxonomies());
    }

    $items = [];
    foreach ($taxonomy_names as $tax) {
        $terms = get_terms([
            'taxonomy'   => $tax,
            'hide_empty' => false,
            'number'     => 500, // cap to avoid memory issues
        ]);
        if (is_wp_error($terms) || empty($terms)) continue;

        $tax_obj = get_taxonomy($tax);
        $tax_label = $tax_obj ? $tax_obj->labels->name : ucfirst($tax);

        foreach ($terms as $term) {
            $url = get_term_link($term);
            if (is_wp_error($url) || empty($url)) continue;

            // Status stored in term meta: _sip_index_status
            $saved_status = get_term_meta($term->term_id, '_sip_index_status', true);
            if (empty($saved_status)) $saved_status = 'unknown';

            $items[] = [
                'id'           => $term->term_id,
                'taxonomy'     => $tax,
                'tax_label'    => $tax_label,
                'name'         => $term->name,
                'url'          => esc_url($url),
                'count'        => (int) $term->count,
                'saved_status' => $saved_status,
            ];
        }
    }

    ob_end_clean();
    iic_check_send_json(['items' => $items, 'total' => count($items)]);
});

// --- AJAX: SAVE CATEGORY INDEX STATUS ---
add_action('wp_ajax_iic_check_save_category_status', function () {
    if (!current_user_can('manage_options')) wp_die();
    ob_start();
    check_ajax_referer('iic_check_security_token', 'nonce');

    $term_id  = isset($_POST['term_id'])  ? intval($_POST['term_id'])                             : 0;
    $status   = isset($_POST['status'])   ? sanitize_text_field(wp_unslash($_POST['status']))    : '';
    $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field(wp_unslash($_POST['taxonomy']))  : 'category';

    if (!$term_id || !in_array($status, ['indexed', 'not_indexed', 'waiting', 'unknown', 'error'], true)) {
        ob_end_clean();
        iic_check_send_json(['msg' => 'Paramètres invalides'], false);
        return;
    }

    update_term_meta($term_id, '_sip_index_status', $status);
    ob_end_clean();
    iic_check_send_json(['ok' => true]);
});
