<?php
/**
 * AI Recommend upsell: API fetch, install, cron, and storefront takeover.
 */

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

if (!defined('ASKAGENTO_RECOMMEND_UPSELL_META_KEY')) {
    define('ASKAGENTO_RECOMMEND_UPSELL_META_KEY', '_askagento_ai_upsell_ids');
}

if (!defined('ASKAGENTO_OPTION_UPSELL_PAYLOAD')) {
    define('ASKAGENTO_OPTION_UPSELL_PAYLOAD', 'askagento_last_upsell_recommendations_payload');
}

if (!defined('ASKAGENTO_OPTION_UPSELL_SITE_ID')) {
    define('ASKAGENTO_OPTION_UPSELL_SITE_ID', 'askagento_last_upsell_recommendations_site_id');
}

/**
 * Product-id map from an upsell API payload ({@see GET /recommend/upsell}).
 *
 * @param array $payload Decoded JSON body.
 * @return array<string, array>
 */
function askagento_recommend_upsell_payload_map(array $payload): array {
    if (isset($payload['upsells']) && is_array($payload['upsells'])) {
        return $payload['upsells'];
    }
    if (isset($payload['recommendations']) && is_array($payload['recommendations'])) {
        return $payload['recommendations'];
    }

    return [];
}

/**
 * @return string Raw JSON stored for upsell, or empty.
 */
function askagento_recommend_upsell_get_stored_payload_raw(): string {
    return (string) get_option(ASKAGENTO_OPTION_UPSELL_PAYLOAD, '');
}

/**
 * @param array $payload Upsell recommendations payload.
 */
function askagento_recommend_upsell_store_payload(array $payload, string $site_id): void {
    update_option(ASKAGENTO_OPTION_UPSELL_PAYLOAD, wp_json_encode($payload), false);
    update_option(ASKAGENTO_OPTION_UPSELL_SITE_ID, sanitize_text_field($site_id), false);
}

/**
 * Clear autonomous recommendation run history / cached payloads (new activation or catalogue registration).
 */
function askagento_recommend_upsell_reset_activity_options() {
    delete_option('askagento_ai_recommend_auto_last_run');
    delete_option('askagento_ai_recommend_auto_last_error');
    delete_option(ASKAGENTO_OPTION_UPSELL_PAYLOAD);
    delete_option(ASKAGENTO_OPTION_UPSELL_SITE_ID);
    delete_option('askagento_last_recommendations_api_fetch_day');
    delete_option('askagento_ai_recommend_installed_at');
    if (function_exists('askagento_recommend_cross_sell_reset_activity_options')) {
        askagento_recommend_cross_sell_reset_activity_options();
    }
}

/**
 * Stored recommendation bundle, or fetch from API (respecting daily cap for cron/autonomous).
 *
 * @param string $site_id Installation site identifier.
 * @return array|WP_Error
 */
function askagento_recommend_upsell_resolve_payload_for_pipeline($site_id) {
    $result = askagento_recommend_upsell_fetch_if_daily_quota($site_id);
    if (!is_wp_error($result)) {
        return $result;
    }
    if ($result->get_error_code() !== 'daily_cap') {
        return $result;
    }

    $raw = askagento_recommend_upsell_get_stored_payload_raw();
    if ($raw === '') {
        return $result;
    }

    $decoded = json_decode($raw, true);
    if (!is_array($decoded) || askagento_recommend_upsell_payload_map($decoded) === []) {
        return $result;
    }

    return $decoded;
}

/**
 * Fetch upsell recommendations via API (GET /recommend/upsell).
 *
 * @param string $site_id Installation site identifier.
 * @return array|WP_Error
 */
function askagento_recommend_upsell_fetch($site_id) {
    $settings = askagento_get_install_settings();

    if (!askagento_install_api_is_enabled()) {
        return new WP_Error('not_enabled', __('Installation API not enabled', 'askagento'));
    }

    if (empty($site_id)) {
        return new WP_Error('missing_installation_id', __('No installation ID found', 'askagento'));
    }

    if (function_exists('askagento_feature_is_enabled') && !askagento_feature_is_enabled('ai_recommend_upsell')) {
        return new WP_Error('feature_not_available', __('AI Upsell Recommendations is not available on your current plan.', 'askagento'));
    }

    $api_key = isset($settings['api_key']) ? trim((string) $settings['api_key']) : '';

    if ($api_key === '') {
        return new WP_Error('missing_api_key', __('Discovery API key is missing; cannot load upsell recommendations.', 'askagento'));
    }

    $args = [
        'method'  => 'GET',
        'timeout' => 90,
        'headers' => [
            'Content-Type'          => 'application/json',
            'X-Askagento-Site-Id'   => (string) $site_id,
            'X-Askagento-Api-Key'   => $api_key,
        ],
    ];

    $base = rtrim((string) $settings['api_url'], '/');
    $url = $base . '/recommend/upsell';
    $response = wp_remote_get($url, $args);
    if (is_wp_error($response)) {
        return $response;
    }

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

    if ($code >= 200 && $code < 300 && is_array($body)) {
        if (askagento_recommend_upsell_payload_map($body) === []) {
            return new WP_Error(
                'invalid_payload',
                __('Upsell recommendations payload is empty. Re-run catalogue sync on AskAgento or wait for sync to finish.', 'askagento')
            );
        }

        return $body;
    }

    return askagento_install_api_response_error(
        $response,
        __('Failed to load upsell recommendations', 'askagento'),
        __('Upsell recommendations are not available yet. Wait for catalogue sync to finish on AskAgento, or run Finish installation again from Settings.', 'askagento')
    );
}

/**
 * Whether a successful recommendation API fetch already ran today (site-local calendar day).
 */
function askagento_recommend_upsell_api_fetched_today() {
    $last = (string) get_option('askagento_last_recommendations_api_fetch_day', '');
    if ($last === '') {
        return false;
    }

    return $last === current_time('Y-m-d');
}

/**
 * Record that a successful recommendation API fetch ran today (for at-most-once-per-day cap).
 */
function askagento_recommend_upsell_mark_api_fetched_today() {
    update_option('askagento_last_recommendations_api_fetch_day', current_time('Y-m-d'), false);
}

/**
 * Fetch recommendations from the API only if the daily download quota allows (max once per calendar day).
 * Used for autonomous/cron paths only (max one successful API fetch per calendar day). Admin manual Download is unlimited and uses {@see askagento_recommend_upsell_fetch} directly.
 *
 * @param string $site_id Installation site identifier.
 * @return array|WP_Error
 */
function askagento_recommend_upsell_fetch_if_daily_quota($site_id) {
    if (apply_filters('askagento_recommend_upsell_bypass_daily_fetch_cap', false)) {
        $result = askagento_recommend_upsell_fetch($site_id);
        if (!is_wp_error($result)) {
            askagento_recommend_upsell_mark_api_fetched_today();
        }

        return $result;
    }

    if (askagento_recommend_upsell_api_fetched_today()) {
        return new WP_Error(
            'daily_cap',
            __('Recommendations were already downloaded today (limit: once per calendar day). Try again tomorrow.', 'askagento')
        );
    }

    $result = askagento_recommend_upsell_fetch($site_id);
    if (!is_wp_error($result)) {
        askagento_recommend_upsell_mark_api_fetched_today();
    }

    return $result;
}

/**
 * Products per AJAX batch when writing recommendation links (admin manual install).
 */
function askagento_recommend_install_batch_size(): int {
    $size = (int) apply_filters('askagento_recommend_install_batch_size', 25);

    return max(1, min(200, $size));
}

/**
 * Decode a stored recommendation JSON bundle once per install run (cached briefly).
 *
 * @param string $cache_group e.g. upsell or cross_sell.
 * @param string $raw_payload Raw JSON from wp_options.
 * @return array|WP_Error
 */
function askagento_recommend_decode_install_payload(string $cache_group, string $raw_payload) {
    if ($raw_payload === '') {
        return new WP_Error('empty_payload', __('No recommendation bundle stored. Download first.', 'askagento'));
    }

    $cache_key = 'askagento_rec_inst_' . sanitize_key($cache_group) . '_' . md5($raw_payload);
    $cached = get_transient($cache_key);
    if (is_array($cached)) {
        return $cached;
    }

    $decoded = json_decode($raw_payload, true);
    if (!is_array($decoded)) {
        return new WP_Error('invalid_payload', __('Stored recommendations are invalid. Download again.', 'askagento'));
    }

    set_transient($cache_key, $decoded, 20 * MINUTE_IN_SECONDS);

    return $decoded;
}

/**
 * Reduce WP/WC overhead while applying many product link updates.
 */
function askagento_recommend_begin_bulk_install(): void {
    if (function_exists('wp_defer_term_counting')) {
        wp_defer_term_counting(true);
    }
    if (function_exists('wp_defer_comment_counting')) {
        wp_defer_comment_counting(true);
    }
    if (function_exists('wp_suspend_cache_addition')) {
        wp_suspend_cache_addition(true);
    }
    if (function_exists('wp_suspend_cache_invalidation')) {
        wp_suspend_cache_invalidation(true);
    }
}

/**
 * @see askagento_recommend_begin_bulk_install()
 */
function askagento_recommend_end_bulk_install(): void {
    if (function_exists('wp_suspend_cache_invalidation')) {
        wp_suspend_cache_invalidation(false);
    }
    if (function_exists('wp_suspend_cache_addition')) {
        wp_suspend_cache_addition(false);
    }
    if (function_exists('wp_defer_comment_counting')) {
        wp_defer_comment_counting(false);
    }
    if (function_exists('wp_defer_term_counting')) {
        wp_defer_term_counting(false);
    }
}

/**
 * @param int[] $ids Product post IDs.
 * @return array<int, true> Set of IDs that exist as product posts.
 */
function askagento_recommend_valid_product_id_set(array $ids): array {
    $ids = array_values(array_unique(array_filter(array_map('intval', $ids))));
    if ($ids === []) {
        return [];
    }

    $set = [];
    foreach (array_chunk($ids, 200) as $chunk) {
        $found = get_posts([
            'post_type' => 'product',
            'post_status' => 'any',
            'post__in' => $chunk,
            'posts_per_page' => -1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
        ]);

        foreach ($found as $product_id) {
            $product_id = (int) $product_id;
            if ($product_id > 0) {
                $set[$product_id] = true;
            }
        }
    }

    return $set;
}

/**
 * Write WooCommerce upsell IDs without a full product save (faster for bulk install).
 *
 * @param int   $product_id Source product ID.
 * @param int[] $linked_ids Linked upsell product IDs.
 */
function askagento_recommend_upsell_write_product_links(int $product_id, array $linked_ids): void {
    update_post_meta($product_id, '_upsell_ids', $linked_ids);
    update_post_meta($product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY, $linked_ids);

    if (function_exists('wc_delete_product_transients')) {
        wc_delete_product_transients($product_id);
    }
    clean_post_cache($product_id);
}

/**
 * Apply AI recommendations to WooCommerce upsell IDs only.
 *
 * @param array  $payload Recommendations payload.
 * @param string $mode    'replace_all' - update every product in the payload; 'fill_missing' - only products with no stored AI upsell links yet.
 * @param array  $batch   Optional keys: offset (int), limit (int). When limit > 0, only that slice of the bundle is processed (for admin AJAX).
 * @return array|WP_Error
 */
function askagento_recommend_upsell_apply_to_shop($payload, $mode = 'replace_all', array $batch = []) {
    if (!function_exists('wc_get_product')) {
        return new WP_Error('woocommerce_missing', __('WooCommerce is required to install recommendation links.', 'askagento'));
    }

    $recommendations = is_array($payload) ? askagento_recommend_upsell_payload_map($payload) : [];
    if ($recommendations === []) {
        return new WP_Error('invalid_payload', __('Invalid upsell recommendations payload', 'askagento'));
    }

    $product_ids = array_values(array_map('intval', array_keys($recommendations)));
    $product_ids = array_values(array_filter($product_ids, static function ($id) {
        return $id > 0;
    }));
    $batch_total = count($product_ids);

    $batch_offset = max(0, (int) ($batch['offset'] ?? 0));
    $batch_limit = isset($batch['limit']) ? max(0, (int) $batch['limit']) : 0;
    $use_batching = $batch_limit > 0;

    if ($use_batching) {
        if ($batch_offset >= $batch_total) {
            return [
                'updated_products' => 0,
                'linked_rows' => 0,
                'skipped_products' => 0,
                'mode' => ($mode === 'fill_missing') ? 'fill_missing' : 'replace_all',
                'batch_offset' => $batch_offset,
                'batch_total' => $batch_total,
                'batch_processed' => 0,
                'batch_done' => true,
                'next_offset' => null,
            ];
        }
        $product_ids = array_slice($product_ids, $batch_offset, $batch_limit);
    }

    $fill_missing_only = ($mode === 'fill_missing');
    $updated_products = 0;
    $linked_rows = 0;
    $skipped_products = 0;

    $linked_candidates = [];
    foreach ($product_ids as $source_id) {
        $rows = $recommendations[(string) $source_id] ?? $recommendations[$source_id] ?? [];
        if (!is_array($rows)) {
            continue;
        }
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $linked_id = isset($row['product_id']) ? (int) $row['product_id'] : 0;
            if ($linked_id > 0) {
                $linked_candidates[] = $linked_id;
            }
        }
    }
    $valid_ids = askagento_recommend_valid_product_id_set(array_merge($product_ids, $linked_candidates));

    askagento_recommend_begin_bulk_install();

    foreach ($product_ids as $product_id) {
        if (!isset($valid_ids[$product_id])) {
            continue;
        }

        if ($fill_missing_only && askagento_recommend_upsell_product_has_installed_links($product_id)) {
            $skipped_products++;
            continue;
        }

        $rows = $recommendations[(string) $product_id] ?? $recommendations[$product_id] ?? [];
        $linked_ids = [];
        if (is_array($rows)) {
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $linked_id = isset($row['product_id']) ? (int) $row['product_id'] : 0;
                if ($linked_id <= 0 || $linked_id === $product_id || !isset($valid_ids[$linked_id])) {
                    continue;
                }
                $linked_ids[] = $linked_id;
            }
        }

        $linked_ids = array_values(array_unique($linked_ids));
        if ($linked_ids === []) {
            continue;
        }

        // Keep exact API order from hosted recommendations (upsells only).
        askagento_recommend_upsell_write_product_links($product_id, $linked_ids);

        $updated_products++;
        $linked_rows += count($linked_ids);
    }

    askagento_recommend_end_bulk_install();

    $batch_processed = count($product_ids);
    $batch_done = !$use_batching || ($batch_offset + $batch_processed >= $batch_total);

    if ($batch_done) {
        update_option('askagento_ai_recommend_installed_at', current_time('mysql'), false);
    }

    $result = [
        'updated_products' => $updated_products,
        'linked_rows' => $linked_rows,
        'skipped_products' => $skipped_products,
        'mode' => $fill_missing_only ? 'fill_missing' : 'replace_all',
    ];

    if ($use_batching) {
        $result['batch_offset'] = $batch_offset;
        $result['batch_total'] = $batch_total;
        $result['batch_processed'] = $batch_processed;
        $result['batch_done'] = $batch_done;
        $result['next_offset'] = $batch_done ? null : ($batch_offset + $batch_processed);
    }

    return $result;
}

/**
 * Whether this product already has AI upsell links installed.
 *
 * @param int $product_id WooCommerce product ID.
 */
function askagento_recommend_upsell_product_has_installed_links($product_id) {
    $product_id = (int) $product_id;
    if ($product_id <= 0) {
        return false;
    }

    if (!metadata_exists('post', $product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY)) {
        return false;
    }

    $existing_meta = get_post_meta($product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY, true);
    $existing_ids = is_array($existing_meta) ? array_values(array_filter(array_map('intval', $existing_meta))) : [];

    return !empty($existing_ids);
}

/**
 * Clear AI upsell links from products previously installed by AskAgento.
 *
 * @return array
 */
function askagento_recommend_upsell_clear_links() {
    $query = new WP_Query([
        'post_type' => 'product',
        'post_status' => 'any',
        'posts_per_page' => -1,
        'fields' => 'ids',
    ]);

    $updated_products = 0;

    foreach ($query->posts as $product_id) {
        $product_id = (int) $product_id;
        if ($product_id <= 0) {
            continue;
        }

        $had_meta = metadata_exists('post', $product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY);
        if (!$had_meta) {
            continue;
        }

        $product = wc_get_product($product_id);
        if (!$product) {
            continue;
        }

        $product->set_upsell_ids([]);
        $product->save();
        wc_delete_product_transients($product_id);

        delete_post_meta($product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY);

        $updated_products++;
    }

    update_option('askagento_ai_recommend_installed_at', '', false);

    return [
        'updated_products' => $updated_products,
    ];
}

/**
 * WooCommerce product image URL for admin preview (bounded small size via registered sizes).
 *
 * @param int $product_id WooCommerce product ID.
 * @return string URL or empty string.
 */
function askagento_recommend_upsell_product_preview_thumb_url(int $product_id): string {
    if ($product_id <= 0 || ! function_exists('wc_get_product')) {
        return '';
    }
    $product = wc_get_product($product_id);
    if (! $product) {
        return '';
    }
    $image_id = (int) $product->get_image_id();
    if ($image_id <= 0) {
        return '';
    }
    foreach (['woocommerce_gallery_thumbnail', 'woocommerce_thumbnail', 'thumbnail'] as $size) {
        $url = wp_get_attachment_image_url($image_id, $size);
        if (is_string($url) && $url !== '') {
            return esc_url_raw($url);
        }
    }

    return '';
}

/**
 * Admin preview: three randomly chosen query products, each with up to three closest matches (one row per query).
 *
 * The query products are sampled at random from the recommendation bundle so repeated
 * test runs surface different products; each product's matches stay its genuine top three.
 *
 * @param array $payload Recommendations payload (recommendations map).
 * @return array<int, array{source_id:int, source_title:string, source_image_url:string, matches:array<int, array{target_id:int, target_title:string, target_image_url:string}>}>
 */
function askagento_recommend_upsell_build_preview_first_three(array $payload): array {
    $out = [];
    $recs = askagento_recommend_upsell_payload_map($payload);
    // Randomly sample up to three query products for the preview instead of always
    // showing the first three in bundle order.
    $source_ids = array_keys($recs);
    shuffle($source_ids);
    foreach ($source_ids as $source_id_str) {
        if (count($out) >= 3) {
            break;
        }
        $rows = $recs[$source_id_str] ?? null;
        $source_id = (int) $source_id_str;
        if ($source_id <= 0 || ! is_array($rows) || $rows === []) {
            continue;
        }
        $matches = [];
        foreach ($rows as $row) {
            if (count($matches) >= 3) {
                break;
            }
            if (! is_array($row)) {
                continue;
            }
            $target_id = isset($row['product_id']) ? (int) $row['product_id'] : 0;
            if ($target_id <= 0) {
                continue;
            }
            $target_title = '';
            if (! empty($row['title']) && is_string($row['title'])) {
                $target_title = wp_strip_all_tags($row['title']);
            }
            if ($target_title === '') {
                $p_tgt = function_exists('wc_get_product') ? wc_get_product($target_id) : null;
                $target_title = (is_object($p_tgt) && is_callable([$p_tgt, 'get_name'])) ? $p_tgt->get_name() : '';
            }
            $matches[] = [
                'target_id' => $target_id,
                'target_title' => $target_title,
                'target_image_url' => askagento_recommend_upsell_product_preview_thumb_url($target_id),
            ];
        }
        if ($matches === []) {
            continue;
        }

        $p_src = function_exists('wc_get_product') ? wc_get_product($source_id) : null;
        $source_title = (is_object($p_src) && is_callable([$p_src, 'get_name'])) ? $p_src->get_name() : '';

        $out[] = [
            'source_id' => $source_id,
            'source_title' => $source_title,
            'source_image_url' => askagento_recommend_upsell_product_preview_thumb_url($source_id),
            'matches' => $matches,
        ];
    }

    return $out;
}

/**
 * AJAX: Download recommendations payload.
 */
add_action('wp_ajax_askagento_recommend_upsell_download', function() {
    if (!check_ajax_referer('askagento_admin', 'nonce', false)) {
        wp_send_json_error(['message' => __('Security check failed. Please reload the page.', 'askagento')], 403);
    }

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied', 'askagento')]);
    }

    if (function_exists('wp_raise_memory_limit')) {
        wp_raise_memory_limit('admin');
    }

    $site_id = get_option('askagento_install_site_id', '');
    if (empty($site_id)) {
        wp_send_json_error(['message' => __('No installation ID found', 'askagento')]);
    }

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

    askagento_recommend_upsell_store_payload($result, $site_id);

    wp_send_json_success([
        'filename' => sanitize_file_name($site_id . '-upsell.json'),
        'payload' => $result,
        'preview_first_three' => askagento_recommend_upsell_build_preview_first_three($result),
    ]);
});

/**
 * AJAX: Return stored recommendations as pretty-printed JSON for browser download (optional backup).
 */
add_action('wp_ajax_askagento_recommend_upsell_export_stored_json', function() {
    if (!check_ajax_referer('askagento_admin', 'nonce', false)) {
        wp_send_json_error(['message' => __('Security check failed. Please reload the page.', 'askagento')], 403);
    }

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied', 'askagento')]);
    }

    $raw = askagento_recommend_upsell_get_stored_payload_raw();
    if ($raw === '') {
        wp_send_json_error(['message' => __('No upsell recommendations stored on this shop yet. Download recommendations first.', 'askagento')]);
    }

    $decoded = json_decode($raw, true);
    if (!is_array($decoded)) {
        wp_send_json_error(['message' => __('Stored recommendations are invalid. Download again.', 'askagento')]);
    }

    $pretty = wp_json_encode(
        $decoded,
        JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
    );
    if (!is_string($pretty) || $pretty === '') {
        wp_send_json_error(['message' => __('Could not encode recommendations JSON.', 'askagento')]);
    }

    $site_id = sanitize_file_name(get_option(ASKAGENTO_OPTION_UPSELL_SITE_ID, get_option('askagento_install_site_id', 'site')));
    if ($site_id === '') {
        $site_id = 'recommendations';
    }
    $filename = sanitize_file_name($site_id . '-upsell-recommendations.json');
    if ($filename === '') {
        $filename = 'recommendations.json';
    }

    wp_send_json_success([
        'filename' => $filename,
        'body' => $pretty,
    ]);
});

/**
 * AJAX: Install recommendations to WooCommerce products.
 */
add_action('wp_ajax_askagento_recommend_upsell_install', function() {
    if (!check_ajax_referer('askagento_admin', 'nonce', false)) {
        wp_send_json_error(['message' => __('Security check failed. Please reload the page.', 'askagento')], 403);
    }

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied', 'askagento')]);
    }

    if (function_exists('wp_raise_memory_limit')) {
        wp_raise_memory_limit('admin');
    }

    $raw_payload = askagento_recommend_upsell_get_stored_payload_raw();
    if (empty($raw_payload)) {
        wp_send_json_error(['message' => __('No downloaded upsell recommendations found. Download first.', 'askagento')]);
    }

    $payload = askagento_recommend_decode_install_payload('upsell', $raw_payload);
    if (is_wp_error($payload)) {
        wp_send_json_error(['message' => $payload->get_error_message()]);
    }

    $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash($_POST['mode'])) : 'replace_all';
    if ($mode !== 'fill_missing') {
        $mode = 'replace_all';
    }

    $offset = isset($_POST['offset']) ? absint(wp_unslash($_POST['offset'])) : 0;

    try {
        $result = askagento_recommend_upsell_apply_to_shop($payload, $mode, [
            'offset' => $offset,
            'limit' => askagento_recommend_install_batch_size(),
        ]);
    } catch (Throwable $e) {
        wp_send_json_error(['message' => $e->getMessage()]);
    }

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

    wp_send_json_success($result);
});

/**
 * AJAX: Clear recommendations from WooCommerce products.
 */
add_action('wp_ajax_askagento_recommend_upsell_clear', function() {
    if (!check_ajax_referer('askagento_admin', 'nonce', false)) {
        wp_send_json_error(['message' => __('Security check failed. Please reload the page.', 'askagento')], 403);
    }

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied', 'askagento')]);
    }

    $result = askagento_recommend_upsell_clear_links();
    wp_send_json_success($result);
});

if (!defined('ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK')) {
    define('ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK', 'askagento_recommend_upsell_cron_event');
}

/**
 * AJAX: Combined status for AI Recommend admin (installation API + autonomous run info).
 */
add_action('wp_ajax_askagento_recommend_upsell_combined_admin_status', function () {
    if (!check_ajax_referer('askagento_admin', 'nonce', false)) {
        wp_send_json_error(['message' => __('Security check failed. Please reload the page.', 'askagento')], 403);
    }

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied', 'askagento')]);
    }

    $installation = [];
    $site_id = get_option('askagento_install_site_id', '');
    if ($site_id === '') {
        $installation['ok'] = false;
        $installation['message'] = __('No installation ID found', 'askagento');
    } else {
        $remote = askagento_check_install_status($site_id);
        if (is_wp_error($remote)) {
            $installation['ok'] = false;
            $installation['message'] = $remote->get_error_message();
        } else {
            $installation['ok'] = true;
            $installation['payload'] = $remote;
        }
    }

    $next_cron_unix = wp_next_scheduled(ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK);
    $last_run_raw = get_option('askagento_ai_recommend_auto_last_run', '');
    $last_err = get_option('askagento_ai_recommend_auto_last_error', []);
    $last_run = [];
    if (is_string($last_run_raw) && $last_run_raw !== '') {
        $decoded_run = json_decode($last_run_raw, true);
        $last_run = is_array($decoded_run) ? $decoded_run : [];
    }

    $autonomous = [
        'enabled' => (bool) get_option('askagento_ai_recommend_auto', false),
        'install_mode' => get_option('askagento_ai_recommend_auto_mode', 'replace_all') === 'fill_missing'
            ? 'fill_missing'
            : 'replace_all',
        'cron_next_unix' => $next_cron_unix ? (int) $next_cron_unix : null,
        'last_run' => $last_run,
        'last_error' => is_array($last_err) ? $last_err : [],
    ];

    wp_send_json_success(
        [
            'installation' => $installation,
            'autonomous' => $autonomous,
        ]
    );
});

/**
 * Schedule the daily cron that may fetch + install autonomous recommendations (if enabled).
 */
function askagento_recommend_upsell_schedule_cron() {
    if (wp_next_scheduled(ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK)) {
        return;
    }
    wp_schedule_event(time() + 180, 'daily', ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK);
}

/**
 * Clear autonomous recommendations cron hook.
 */
function askagento_recommend_upsell_unschedule_cron() {
    wp_clear_scheduled_hook(ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK);
}

/**
 * Ensure cron exists when autonomous mode is on (e.g. after plugin upgrades).
 */
function askagento_recommend_upsell_maybe_schedule_cron() {
    if (!(bool) get_option('askagento_ai_recommend_auto', false)) {
        return;
    }
    askagento_recommend_upsell_schedule_cron();
}

/**
 * Fetch latest recommendations from the API and install using the autonomous mode preference.
 *
 * Skips work when WooCommerce or the recommendation API isn’t usable.
 *
 * @param string $context Optional slug for troubleshooting (cron, upgrade).
 */
function askagento_recommend_upsell_run_autonomous_pipeline($context = '') {
    unset($context);

    if (!(bool) get_option('askagento_ai_recommend_auto', false)) {
        return;
    }

    if (!askagento_install_api_is_enabled()) {
        return;
    }

    if (!function_exists('wc_get_product')) {
        return;
    }

    $site_id = get_option('askagento_install_site_id', '');
    if ($site_id === '') {
        return;
    }

    $result = askagento_recommend_upsell_resolve_payload_for_pipeline($site_id);
    if (is_wp_error($result)) {
        if ($result->get_error_code() === 'daily_cap') {
            return;
        }
        update_option(
            'askagento_ai_recommend_auto_last_error',
            [
                'time' => current_time('mysql'),
                'message' => $result->get_error_message(),
            ],
            false
        );
        return;
    }

    delete_option('askagento_ai_recommend_auto_last_error');

    askagento_recommend_upsell_store_payload($result, $site_id);

    $mode_raw = get_option('askagento_ai_recommend_auto_mode', 'replace_all');
    $install_mode = ($mode_raw === 'fill_missing') ? 'fill_missing' : 'replace_all';

    $apply = askagento_recommend_upsell_apply_to_shop($result, $install_mode);
    if (is_wp_error($apply)) {
        update_option(
            'askagento_ai_recommend_auto_last_error',
            [
                'time' => current_time('mysql'),
                'message' => $apply->get_error_message(),
            ],
            false
        );
        return;
    }

    update_option(
        'askagento_ai_recommend_auto_last_run',
        wp_json_encode(
            [
                'time' => current_time('mysql'),
                'mode' => $install_mode,
                'updated_products' => $apply['updated_products'] ?? 0,
                'linked_rows' => $apply['linked_rows'] ?? 0,
                'skipped_products' => $apply['skipped_products'] ?? 0,
            ],
            JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
        ),
        false
    );
}

add_action(ASKAGENTO_RECOMMEND_UPSELL_CRON_HOOK, static function () {
    askagento_recommend_upsell_run_autonomous_pipeline('cron');
});

add_action(
    'plugins_loaded',
    static function () {
        askagento_recommend_upsell_maybe_schedule_cron();
    },
    20
);

add_action(
    'updated_option_askagento_ai_recommend_auto',
    static function ($old_val, $value) {
        if ((bool) $value) {
            askagento_recommend_upsell_schedule_cron();
        } else {
            askagento_recommend_upsell_unschedule_cron();
        }
    },
    10,
    2
);

/**
 * Ensure cron is queued the first time autonomous mode option is inserted (added_option branch).
 *
 * @param string $option Option name.
 * @param mixed  $value  Option value.
 */
add_action(
    'added_option',
    static function ($option, $value) {
        if ('askagento_ai_recommend_auto' !== $option || ! (bool) $value) {
            return;
        }
        askagento_recommend_upsell_schedule_cron();
    },
    10,
    2
);

add_action(
    'upgrader_process_complete',
    static function ($_upgrader, $options) {
        unset($_upgrader);
        if (empty($options['type']) || 'plugin' !== $options['type'] || empty($options['action']) || 'update' !== $options['action']) {
            return;
        }

        $slugs = [];
        if (! empty($options['plugins']) && is_array($options['plugins'])) {
            $slugs = $options['plugins'];
        }
        if (! empty($options['plugin']) && is_string($options['plugin'])) {
            $slugs[] = $options['plugin'];
        }

        if (! defined('ASKAGENTO_PLUGIN_FILE')) {
            return;
        }

        foreach (array_unique($slugs) as $slug) {
            if ($slug === plugin_basename(ASKAGENTO_PLUGIN_FILE)) {
                askagento_recommend_upsell_maybe_schedule_cron();
                askagento_recommend_upsell_run_autonomous_pipeline('plugin_update');
                return;
            }
        }
    },
    10,
    2
);

function askagento_recommend_upsell_normalize_product_ids($product_id, $ids) {
    if (!is_array($ids)) {
        return [];
    }
    $normalized = array_values(array_unique(array_map('intval', $ids)));
    $normalized = array_values(array_filter($normalized, function($id) use ($product_id) {
        return $id > 0 && $id !== (int)$product_id;
    }));
    return $normalized;
}

function askagento_recommend_upsell_get_related_ids($product_id) {
    $product_id = (int) $product_id;
    $ai_related = get_post_meta($product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY, true);

    return askagento_recommend_upsell_normalize_product_ids($product_id, $ai_related);
}

/**
 * True when this product has AI upsell install state (including cleared empty list).
 */
function askagento_recommend_upsell_takeover_active($product_id) {
    $product_id = (int) $product_id;

    return metadata_exists('post', $product_id, ASKAGENTO_RECOMMEND_UPSELL_META_KEY);
}

function askagento_recommend_upsell_render_products_section($ids, $section_class, $title_text) {
    $ids = array_values(array_unique(array_map('intval', $ids)));
    if (empty($ids)) {
        return '';
    }
    $ids_csv = implode(',', $ids);
    $shortcode = sprintf(
        '[products ids="%s" columns="4" orderby="post__in"]',
        esc_attr($ids_csv)
    );
    return '<section class="' . esc_attr($section_class) . '">'
        . '<h2>' . esc_html($title_text) . '</h2>'
        . do_shortcode($shortcode)
        . '</section>';
}

/**
 * Render only the "Related products" section from the backend-ordered ID list.
 */
function askagento_recommend_upsell_build_combined_html($product_id) {
    $ids = askagento_recommend_upsell_get_related_ids((int)$product_id);
    if (empty($ids)) {
        return '';
    }
    return askagento_recommend_upsell_render_products_section(
        $ids,
        'related products up-sells upsells',
        __('Related products', 'askagento')
    );
}

/**
 * Emit combined HTML once per request; subsequent native blocks are suppressed.
 *
 * @return string HTML or empty string.
 */
function askagento_recommend_upsell_emit_combined_once($product_id) {
    global $askagento_recommend_upsell_combined_emitted;
    if (!empty($askagento_recommend_upsell_combined_emitted)) {
        return '';
    }
    if (!askagento_recommend_upsell_takeover_active($product_id)) {
        return '';
    }
    $askagento_recommend_upsell_combined_emitted = true;

    return askagento_recommend_upsell_build_combined_html((int)$product_id);
}

/**
 * Recognize WooCommerce blocks that render upsells / related on single product pages.
 */
function askagento_recommend_upsell_is_wc_merchandising_block($block) {
    if (!is_array($block) || empty($block['blockName'])) {
        return false;
    }
    $name = (string)$block['blockName'];
    $attrs = isset($block['attrs']) && is_array($block['attrs']) ? $block['attrs'] : [];
    $attrs_json = wp_json_encode($attrs);
    $attrs_json = is_string($attrs_json) ? strtolower($attrs_json) : '';

    $explicit = [
        'woocommerce/product-related-products',
        'woocommerce/related-products',
        'woocommerce/product-upsell-products',
        'woocommerce/product-upsells',
        'woocommerce/product-upsell',
    ];
    if (in_array($name, $explicit, true)) {
        return true;
    }

    if ($name === 'woocommerce/product-collection') {
        if (strpos($attrs_json, 'related') !== false) {
            return true;
        }
        if (strpos($attrs_json, 'upsell') !== false) {
            return true;
        }
        if (strpos($attrs_json, 'cross-sell') !== false || strpos($attrs_json, 'cross_sell') !== false) {
            return true;
        }
        if (!empty($attrs['collection']) && is_string($attrs['collection'])) {
            $c = strtolower($attrs['collection']);
            if (strpos($c, 'related') !== false || strpos($c, 'upsell') !== false) {
                return true;
            }
        }
    }

    return false;
}

/**
 * Remove native upsell / related sections when AskAgento owns recommendations for this product.
 */
add_action('template_redirect', function() {
    if (!function_exists('is_product') || !is_product()) {
        return;
    }
    $product_id = (int)get_queried_object_id();
    if ($product_id <= 0 || !askagento_recommend_upsell_takeover_active($product_id)) {
        return;
    }

    remove_action('woocommerce_after_single_product_summary', 'woocommerce_upsell_display', 15);
    remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20);
}, 99);

/**
 * Classic product templates: output both sections once in backend order.
 */
add_action('woocommerce_after_single_product_summary', function() {
    if (!function_exists('is_product') || !is_product()) {
        return;
    }
    $product_id = (int)get_queried_object_id();
    if ($product_id <= 0 || !askagento_recommend_upsell_takeover_active($product_id)) {
        return;
    }

    $html = askagento_recommend_upsell_emit_combined_once($product_id);
    if ($html !== '') {
        echo wp_kses_post($html);
    }
}, 14);

/**
 * Block / hybrid templates: first matching merch block emits both sections; all others suppressed.
 */
add_filter('render_block', function($block_content, $block) {
    if (!function_exists('is_product') || !is_product()) {
        return $block_content;
    }
    $product_id = (int)get_the_ID();
    if ($product_id <= 0 || !askagento_recommend_upsell_takeover_active($product_id)) {
        return $block_content;
    }
    if (!askagento_recommend_upsell_is_wc_merchandising_block($block)) {
        return $block_content;
    }

    return askagento_recommend_upsell_emit_combined_once($product_id);
}, 9999, 2);

/**
 * When not under takeover, keep related queries stable for default WooCommerce behavior.
 */
add_filter('woocommerce_product_related_posts_shuffle', '__return_false', 9999);
add_filter('woocommerce_get_related_product_ids', function($related_posts, $product_id, $args) {
    if (askagento_recommend_upsell_takeover_active((int)$product_id)) {
        return $related_posts;
    }
    $ai_related = askagento_recommend_upsell_get_related_ids((int)$product_id);
    if (!empty($ai_related)) {
        return $ai_related;
    }
    return $related_posts;
}, 9999, 3);

add_filter('woocommerce_related_products', function($related_posts, $product_id, $args) {
    if (askagento_recommend_upsell_takeover_active((int)$product_id)) {
        return $related_posts;
    }
    $ai_related = askagento_recommend_upsell_get_related_ids((int)$product_id);
    if (!empty($ai_related)) {
        return $ai_related;
    }
    return $related_posts;
}, 9999, 3);

add_filter('woocommerce_upsell_display_args', function($args) {
    $args['orderby'] = 'post__in';
    $args['order'] = 'ASC';
    return $args;
}, 9999, 1);

add_filter('woocommerce_output_related_products_args', function($args) {
    $args['orderby'] = 'post__in';
    $args['order'] = 'ASC';
    return $args;
}, 9999, 1);

