<?php
/**
 * Bizmart — Background import processor.
 *
 * @package Bizmart
 * @license GPL-2.0-or-later
 */
if (!defined('ABSPATH')) exit;

// Importer: job helpers, scheduler, processor, and AJAX handlers
// This file was extracted from bizmart-core.php to keep the core file small.

if (!function_exists('bizmart_import_job_get')) {
    function bizmart_import_job_get() {
        $job = get_option('bizmart_final_import_job', null);
        return $job ? $job : null;
    }
}

if (!function_exists('bizmart_import_job_update')) {
    function bizmart_import_job_update($data): void {
        update_option('bizmart_final_import_job', $data);
    }
}

if (!function_exists('bizmart_import_job_clear')) {
    function bizmart_import_job_clear(): void {
        delete_option('bizmart_final_import_job');
        wp_clear_scheduled_hook('bizmart_final_import_job_process');
    }
}

/**
 * Append an entry to the job audit log (max 400 entries, FIFO).
 */
if (!function_exists('bizmart_import_audit_log')) {
    function bizmart_import_audit_log(array &$job, array $entry): void {
        if (!isset($job['audit_log']) || !is_array($job['audit_log'])) {
            $job['audit_log'] = [];
        }
        $entry['ts'] = current_time('mysql');
        $job['audit_log'][] = $entry;
        if (count($job['audit_log']) > 400) {
            array_shift($job['audit_log']);
        }
    }
}

if (!function_exists('bizmart_import_job_create')) {
    function bizmart_import_job_create(string $meta_key, int $total, string $policy = 'prompt', int $batch = 50, bool $dry_run = false): array {
        $batch = max(1, (int) $batch);
        $policy = in_array($policy, ['prompt','replace','skip'], true) ? $policy : 'prompt';

        $job_id = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('bizmart_import_', true);

        $job = [
            'job_id' => $job_id,
            'meta_key' => $meta_key,
            // 'ids' omitted to avoid large memory usage; processor will query by meta_key + LIMIT/OFFSET
            'total' => max(0, (int) $total),
            'offset' => 0,
            'processed' => 0,
            'skipped' => 0,
            'replaced' => 0,
            'batch' => $batch,
            'policy' => $policy, // prompt|replace|skip
            'dry_run' => (bool) $dry_run,
            'status' => 'pending', // pending|running|paused_for_confirmation|completed|cancelled
            'conflicts' => [],
            'processed_preview' => [],
            'audit_log' => [],
            'started_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ];

        update_option('bizmart_final_import_job', $job);
        return $job;
    }
}

if (!function_exists('bizmart_import_job_schedule_next')) {
    function bizmart_import_job_schedule_next(): void {
        if (!wp_next_scheduled('bizmart_final_import_job_process')) {
            wp_schedule_single_event(time() + 2, 'bizmart_final_import_job_process');
        }
    }
}

add_action('bizmart_final_import_job_process', function() {
    bizmart_process_import_job();
});

if (!function_exists('bizmart_final_import_build_item_row')) {
    function bizmart_final_import_build_item_row(int $pid, string $meta_key): array {
        $title = function_exists('get_the_title') ? get_the_title($pid) : '';
        $type = function_exists('get_post_type') ? get_post_type($pid) : '';
        $sku = get_post_meta($pid, '_sku', true);
        $existing_purchase = get_post_meta($pid, '_purchase_price', true);
        $selling = get_post_meta($pid, '_price', true);
        $source_raw = get_post_meta($pid, $meta_key, true);
        $source_normalized = bizmart_final_import_normalize_price($source_raw);

        return [
            'id' => $pid,
            'title' => $title,
            'type' => $type,
            'sku' => $sku,
            'existing_purchase' => $existing_purchase,
            'selling' => $selling,
            'source_raw' => $source_raw,
            'source_normalized' => $source_normalized,
            'edit_url' => admin_url('post.php?post=' . $pid . '&action=edit'),
        ];
    }
}

if (!function_exists('bizmart_process_import_job')) {
    function bizmart_process_import_job(): void {
        $job = bizmart_import_job_get();
        if (!$job || !is_array($job)) return;
        if (($job['status'] ?? '') === 'completed' || ($job['status'] ?? '') === 'cancelled') return;

        $job_id = (string) ($job['job_id'] ?? '');
        if ($job_id === '') {
            $job_id = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('bizmart_import_', true);
            $job['job_id'] = $job_id;
        }

        // Acquire a short-lived lock so cron overlaps can't double-run.
        if (!bizmart_final_import_lock_acquire($job_id, 120)) {
            return;
        }

        $job['status'] = 'running';
        $job['updated_at'] = current_time('mysql');
        bizmart_import_job_update($job);

        $batch = max(1, (int) ($job['batch'] ?? 50));
        $ids = isset($job['ids']) && is_array($job['ids']) ? $job['ids'] : null;
        $total = (int) ($job['total'] ?? 0);
        $offset = (int) ($job['offset'] ?? 0);
        $meta_key = (string) ($job['meta_key'] ?? '');
        $dry_run = (bool) ($job['dry_run'] ?? false);

        global $wpdb;

        if ($meta_key === '') {
            $job['status'] = 'cancelled';
            $job['updated_at'] = current_time('mysql');
            bizmart_import_job_update($job);
            bizmart_final_import_lock_release($job_id);
            return;
        }

        // Ensure we have a total count recorded; compute if missing.
        if ($total <= 0) {
            $total = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT pm.post_id)
                 FROM {$wpdb->postmeta} pm
                 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                 WHERE pm.meta_key = %s
                   AND pm.meta_value != ''
                   AND p.post_type IN ('product','product_variation')",
                $meta_key
            ));
            $job['total'] = $total;
            bizmart_import_job_update($job);
        }

        // If an explicit IDs list exists (older jobs), slice from it; otherwise fetch the next batch from DB.
        if (is_array($ids) && !empty($ids)) {
            $slice = array_slice($ids, $offset, $batch);
        } else {
            $slice = $wpdb->get_col($wpdb->prepare(
                "SELECT DISTINCT pm.post_id
                 FROM {$wpdb->postmeta} pm
                 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                 WHERE pm.meta_key = %s
                   AND pm.meta_value != ''
                   AND p.post_type IN ('product','product_variation')
                 ORDER BY pm.post_id ASC
                 LIMIT %d OFFSET %d",
                $meta_key,
                $batch,
                $offset
            ));
            if (!is_array($slice)) $slice = [];
        }
        // If no items were returned for this batch, finish the job.
        if (empty($slice)) {
            $job['status'] = 'completed';
            $job['updated_at'] = current_time('mysql');
            bizmart_import_job_update($job);
            bizmart_final_import_lock_release($job_id);
            return;
        }
        foreach ($slice as $pid_raw) {
            $pid = (int) $pid_raw;
            if ($pid <= 0) {
                $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
                $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
                continue;
            }

            $source_val = get_post_meta($pid, $meta_key, true);
            if ($source_val === '' || $source_val === null) {
                $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
                $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
                continue;
            }

            $normalized = bizmart_final_import_normalize_price($source_val);
            if ($normalized === null) {
                bizmart_import_audit_log($job, [
                    'id'         => $pid,
                    'action'     => 'skip_invalid_source',
                    'meta_key'   => $meta_key,
                    'source_raw' => $source_val,
                ]);

                $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
                $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
                continue;
            }

            $existing = get_post_meta($pid, '_purchase_price', true);
            $has_existing = ($existing !== '' && $existing !== null && (float) $existing != 0.0);

            if ($has_existing) {
                $policy = (string) ($job['policy'] ?? 'prompt');
                if ($policy === 'prompt') {
                    $job['conflicts'][] = array_merge(
                        bizmart_final_import_build_item_row($pid, $meta_key),
                        [
                            'existing' => $existing,
                            'source' => $normalized,
                        ]
                    );
                    $job['status'] = 'paused_for_confirmation';
                    $job['updated_at'] = current_time('mysql');
                    bizmart_import_job_update($job);
                    bizmart_final_import_lock_release($job_id);
                    return;
                }
                if ($policy === 'skip') {
                    bizmart_import_audit_log($job, [
                        'id'     => $pid,
                        'action' => 'skip_existing',
                        'prev'   => $existing,
                        'source' => $normalized,
                    ]);

                    $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
                    $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
                    continue;
                }
                if ($policy === 'replace') {
                    if (!$dry_run) {
                        update_post_meta($pid, '_purchase_price', $normalized);
                    }
                    bizmart_import_audit_log($job, [
                        'id'     => $pid,
                        'action' => $dry_run ? 'dry_run_replace' : 'replace',
                        'prev'   => $existing,
                        'next'   => $normalized,
                    ]);

                    $job['replaced'] = (int) ($job['replaced'] ?? 0) + 1;
                }
            } else {
                if (!$dry_run) {
                    update_post_meta($pid, '_purchase_price', $normalized);
                }
                bizmart_import_audit_log($job, [
                    'id'     => $pid,
                    'action' => $dry_run ? 'dry_run_set' : 'set',
                    'prev'   => $existing,
                    'next'   => $normalized,
                ]);

                $job['processed'] = (int) ($job['processed'] ?? 0) + 1;
            }

            $selling = get_post_meta($pid, '_price', true);
            if (!isset($job['processed_preview']) || !is_array($job['processed_preview'])) {
                $job['processed_preview'] = [];
            }
            $job['processed_preview'][] = array_merge(
                bizmart_final_import_build_item_row($pid, $meta_key),
                [
                    'purchase' => $normalized,
                    'selling' => $selling,
                    'prev_purchase' => $existing,
                    'action' => $has_existing ? (($job['policy'] ?? '') === 'replace' ? ($dry_run ? 'dry_run_replace' : 'replace') : 'skip') : ($dry_run ? 'dry_run_set' : 'set'),
                ]
            );
            if (count($job['processed_preview']) > 200) array_shift($job['processed_preview']);

            $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
            $job['updated_at'] = current_time('mysql');
        }

        if (((int) ($job['offset'] ?? 0)) >= $total) {
            $job['status'] = 'completed';
            $job['updated_at'] = current_time('mysql');
            bizmart_import_job_update($job);
            bizmart_final_import_lock_release($job_id);
            return;
        }

        bizmart_import_job_update($job);
        bizmart_import_job_schedule_next();
        bizmart_final_import_lock_release($job_id);
    }
}

// AJAX: preview available items for a meta key (sample list)
if (!function_exists('bizmart_final_import_preview_items')) {
    add_action('wp_ajax_bizmart_final_import_preview_items', 'bizmart_final_import_preview_items');

    function bizmart_final_import_preview_items() {
        bizmart_check_ajax_nonce('nonce');
        if (!current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => 'Insufficient permissions'], 403);
        }

        global $wpdb;
        $meta_raw = wp_unslash($_POST['meta_key'] ?? '');
        $meta_keys = bizmart_final_import_parse_meta_keys($meta_raw);
        if (!$meta_keys) {
            wp_send_json_error(['message' => 'No key'], 400);
        }

        $limit = isset($_POST['limit']) ? absint(wp_unslash($_POST['limit'])) : 25;
        $limit = max(5, min(100, $limit));

        $selected_meta_key = '';
        foreach ($meta_keys as $mk) {
            $mk = sanitize_text_field($mk);
            if ($mk === '') continue;
            $cnt = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT pm.post_id)
                 FROM {$wpdb->postmeta} pm
                 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                 WHERE pm.meta_key = %s
                   AND pm.meta_value != ''
                   AND p.post_type IN ('product','product_variation')",
                $mk
            ));
            if ($cnt > 0) {
                $selected_meta_key = $mk;
                break;
            }
            // If none found, keep trying
        }

        if ($selected_meta_key === '') {
            // fallback to first sanitized key
            $selected_meta_key = sanitize_text_field((string) ($meta_keys[0] ?? ''));
        }
        if ($selected_meta_key === '') {
            wp_send_json_error(['message' => 'No key'], 400);
        }

        $ids = $wpdb->get_col($wpdb->prepare(
            "SELECT DISTINCT pm.post_id
             FROM {$wpdb->postmeta} pm
             INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
             WHERE pm.meta_key = %s
               AND pm.meta_value != ''
               AND p.post_type IN ('product','product_variation')
             ORDER BY pm.post_id ASC
             LIMIT %d",
            $selected_meta_key,
            $limit
        ));
        if (!is_array($ids)) $ids = [];

        $items = [];
        foreach ($ids as $pid_raw) {
            $pid = (int) $pid_raw;
            if ($pid <= 0) continue;
            $items[] = bizmart_final_import_build_item_row($pid, $selected_meta_key);
        }

        wp_send_json_success([
            'used_meta_key' => $selected_meta_key,
            'limit' => $limit,
            'items' => $items,
        ]);
    }
}
// AJAX: start import job
add_action('wp_ajax_bizmart_final_start_import_job', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    $existing_job = bizmart_import_job_get();
    if (is_array($existing_job)) {
        $st = (string) ($existing_job['status'] ?? '');
        if (in_array($st, ['pending','running','paused_for_confirmation'], true)) {
            wp_send_json_error(['message' => 'An import job is already active. Cancel it before starting a new one.'], 409);
        }
    }

    global $wpdb;
    $meta_raw = wp_unslash($_POST['meta_key'] ?? '');
    $meta_keys = bizmart_final_import_parse_meta_keys($meta_raw);
    $policy = isset($_POST['policy']) ? sanitize_text_field(wp_unslash($_POST['policy'])) : 'prompt';
    // Ignore user-supplied batch/dry_run values — use fixed internal batch and always write.
    $batch = 50; // internal fixed batch size
    $dry_run = false; // always perform writes
    if (!$meta_keys) { wp_send_json_error(['message' => 'Missing meta_key'], 400); }

    $selected_meta_key = '';
    $total = 0;
    foreach ($meta_keys as $mk) {
        $mk = sanitize_text_field($mk);
        if ($mk === '') continue;
        $cnt = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.post_id)
             FROM {$wpdb->postmeta} pm
             INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
             WHERE pm.meta_key = %s
               AND pm.meta_value != ''
               AND p.post_type IN ('product','product_variation')",
            $mk
        ));
        if ($cnt > 0) {
            $selected_meta_key = $mk;
            $total = $cnt;
            break;
        }
    }

    if (!$selected_meta_key || $total <= 0) { wp_send_json_error(['message' => 'No items found for this meta key'], 404); }

    $job = bizmart_import_job_create($selected_meta_key, $total, in_array($policy, ['prompt','replace','skip'], true) ? $policy : 'prompt', $batch, $dry_run);
    bizmart_import_job_schedule_next();
    wp_send_json_success($job);
});

/**
 * Cleanup job: delete source meta values in background
 */
if (!function_exists('bizmart_cleanup_job_get')) {
    function bizmart_cleanup_job_get() {
        $job = get_option('bizmart_final_cleanup_job', null);
        return $job ? $job : null;
    }
}

if (!function_exists('bizmart_cleanup_job_update')) {
    function bizmart_cleanup_job_update($data): void {
        update_option('bizmart_final_cleanup_job', $data);
    }
}

if (!function_exists('bizmart_cleanup_job_clear')) {
    function bizmart_cleanup_job_clear(): void {
        delete_option('bizmart_final_cleanup_job');
        wp_clear_scheduled_hook('bizmart_final_cleanup_job_process');
    }
}

if (!function_exists('bizmart_cleanup_job_create')) {
    function bizmart_cleanup_job_create(string $meta_key, int $total, int $batch = 50): array {
        $batch = max(1, (int) $batch);
        $job_id = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('bizmart_cleanup_', true);
        $job = [
            'job_id' => $job_id,
            'meta_key' => $meta_key,
            'total' => max(0, (int) $total),
            'offset' => 0,
            'processed' => 0,
            'skipped' => 0,
            'batch' => $batch,
            'status' => 'pending', // pending|running|completed|cancelled
            'audit_log' => [],
            'started_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ];
        update_option('bizmart_final_cleanup_job', $job);
        return $job;
    }
}

if (!function_exists('bizmart_cleanup_job_schedule_next')) {
    function bizmart_cleanup_job_schedule_next(): void {
        if (!wp_next_scheduled('bizmart_final_cleanup_job_process')) {
            wp_schedule_single_event(time() + 2, 'bizmart_final_cleanup_job_process');
        }
    }
}

add_action('bizmart_final_cleanup_job_process', function() {
    bizmart_process_cleanup_job();
});

if (!function_exists('bizmart_process_cleanup_job')) {
    function bizmart_process_cleanup_job(): void {
        $job = bizmart_cleanup_job_get();
        if (!$job || !is_array($job)) return;
        if (($job['status'] ?? '') === 'completed' || ($job['status'] ?? '') === 'cancelled') return;

        $job_id = (string) ($job['job_id'] ?? '');
        if ($job_id === '') {
            $job_id = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('bizmart_cleanup_', true);
            $job['job_id'] = $job_id;
        }

        if (!bizmart_final_import_lock_acquire($job_id, 120)) return;

        $job['status'] = 'running';
        $job['updated_at'] = current_time('mysql');
        bizmart_cleanup_job_update($job);

        $batch = max(1, (int) ($job['batch'] ?? 50));
        $total = (int) ($job['total'] ?? 0);
        $offset = (int) ($job['offset'] ?? 0);
        $meta_key = (string) ($job['meta_key'] ?? '');

        global $wpdb;
        if ($meta_key === '') {
            $job['status'] = 'cancelled';
            $job['updated_at'] = current_time('mysql');
            bizmart_cleanup_job_update($job);
            bizmart_final_import_lock_release($job_id);
            return;
        }

        if ($total <= 0) {
            $total = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT pm.post_id)
                 FROM {$wpdb->postmeta} pm
                 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                 WHERE pm.meta_key = %s
                   AND pm.meta_value != ''
                   AND p.post_type IN ('product','product_variation')",
                $meta_key
            ));
            $job['total'] = $total;
            bizmart_cleanup_job_update($job);
        }

        $ids = $wpdb->get_col($wpdb->prepare(
            "SELECT DISTINCT pm.post_id
             FROM {$wpdb->postmeta} pm
             INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
             WHERE pm.meta_key = %s
               AND pm.meta_value != ''
               AND p.post_type IN ('product','product_variation')
             ORDER BY pm.post_id ASC
             LIMIT %d OFFSET %d",
            $meta_key,
            $batch,
            $offset
        ));
        if (!is_array($ids)) $ids = [];

        if (empty($ids)) {
            $job['status'] = 'completed';
            $job['updated_at'] = current_time('mysql');
            bizmart_cleanup_job_update($job);
            bizmart_final_import_lock_release($job_id);
            return;
        }

        foreach ($ids as $pid_raw) {
            $pid = (int) $pid_raw;
            if ($pid <= 0) {
                $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
                $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
                continue;
            }

            $removed = delete_post_meta($pid, $meta_key);
            if ($removed) {
                $job['processed'] = (int) ($job['processed'] ?? 0) + 1;
                $job['audit_log'][] = ['id' => $pid, 'action' => 'deleted', 'meta_key' => $meta_key, 'ts' => current_time('mysql')];
            } else {
                $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
                $job['audit_log'][] = ['id' => $pid, 'action' => 'none_removed', 'meta_key' => $meta_key, 'ts' => current_time('mysql')];
            }
            if (count($job['audit_log']) > 400) array_shift($job['audit_log']);

            $job['offset'] = (int) ($job['offset'] ?? 0) + 1;
            $job['updated_at'] = current_time('mysql');
        }

        if (((int) ($job['offset'] ?? 0)) >= $total) {
            $job['status'] = 'completed';
            $job['updated_at'] = current_time('mysql');
            bizmart_cleanup_job_update($job);
            bizmart_final_import_lock_release($job_id);
            return;
        }

        bizmart_cleanup_job_update($job);
        bizmart_cleanup_job_schedule_next();
        bizmart_final_import_lock_release($job_id);
    }
}

// AJAX: start cleanup job
add_action('wp_ajax_bizmart_final_start_cleanup_job', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    global $wpdb;
    $meta_raw = wp_unslash($_POST['meta_key'] ?? '');
    $meta_keys = bizmart_final_import_parse_meta_keys($meta_raw);
    if (!$meta_keys) { wp_send_json_error(['message' => 'Missing meta_key'], 400); }

    $selected_meta_key = '';
    $total = 0;
    foreach ($meta_keys as $mk) {
        $mk = sanitize_text_field($mk);
        if ($mk === '') continue;
        $cnt = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.post_id)
             FROM {$wpdb->postmeta} pm
             INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
             WHERE pm.meta_key = %s
               AND pm.meta_value != ''
               AND p.post_type IN ('product','product_variation')",
            $mk
        ));
        if ($cnt > 0) {
            $selected_meta_key = $mk;
            $total = $cnt;
            break;
        }
    }

    if (!$selected_meta_key || $total <= 0) { wp_send_json_error(['message' => 'No items found for this meta key'], 404); }

    $batch = 50;
    $job = bizmart_cleanup_job_create($selected_meta_key, $total, $batch);
    bizmart_cleanup_job_schedule_next();
    wp_send_json_success($job);
});

// AJAX: get cleanup job status
add_action('wp_ajax_bizmart_final_get_cleanup_job_status', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);
    $job = bizmart_cleanup_job_get();
    wp_send_json_success($job ? $job : null);
});

// Note: cancel cleanup endpoint removed by request — job cancellation not exposed via AJAX.

// AJAX: get job status
add_action('wp_ajax_bizmart_final_get_import_job_status', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);
    $job = bizmart_import_job_get();
    wp_send_json_success($job ? $job : null);
});

// Helper: convert audit array to CSV string (simple, admin use only)
if (!function_exists('bizmart_audit_to_csv')) {
    function bizmart_audit_to_csv(array $audit): string {
        if (empty($audit)) return '';
        // Common columns we expect
        $cols = ['id','action','meta_key','prev','next','source_raw','ts'];
        $out = fopen('php://temp', 'r+');
        // header
        fputcsv($out, $cols);
        foreach ($audit as $row) {
            $r = [];
            foreach ($cols as $c) {
                $r[] = isset($row[$c]) ? (string)$row[$c] : (isset($row[$c]) ? '' : (isset($row[strtolower($c)]) ? (string)$row[strtolower($c)] : ''));
            }
            fputcsv($out, $r);
        }
        rewind($out);
        $csv = stream_get_contents($out);
        fclose($out);
        return $csv === false ? '' : $csv;
    }
}

// AJAX: export import audit CSV
add_action('wp_ajax_bizmart_final_export_import_audit', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);
    $job = bizmart_import_job_get();
    if (!$job || empty($job['audit_log'])) wp_send_json_error(['message' => 'No audit data'], 404);
    $csv = bizmart_audit_to_csv((array)$job['audit_log']);
    wp_send_json_success(['csv' => $csv]);
});

// AJAX: export cleanup audit CSV
add_action('wp_ajax_bizmart_final_export_cleanup_audit', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);
    $job = bizmart_cleanup_job_get();
    if (!$job || empty($job['audit_log'])) wp_send_json_error(['message' => 'No audit data'], 404);
    $csv = bizmart_audit_to_csv((array)$job['audit_log']);
    wp_send_json_success(['csv' => $csv]);
});

// AJAX: hidden debug clear endpoint (admin-only; not exposed in UI)
add_action('wp_ajax_bizmart_final_clear_jobs_debug', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Unauthorized'], 403);
    bizmart_import_job_clear();
    bizmart_cleanup_job_clear();
    wp_send_json_success(true);
});

// Minimal AJAX: return paged product rows (IDs -> render simple rows)
add_action('wp_ajax_bizmart_get_products_list', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    $page = max(1, absint(wp_unslash($_POST['page'] ?? 1)));
    $per = max(5, min(100, absint(wp_unslash($_POST['per'] ?? 25))));
    $s = isset($_POST['s']) ? sanitize_text_field(wp_unslash($_POST['s'])) : '';

    $args = [
        'post_type' => 'product',
        'posts_per_page' => $per,
        'paged' => $page,
        's' => $s,
        'fields' => 'ids',
    ];
    $q = new WP_Query($args);
    $rows = '';
    if ($q->have_posts()) {
        foreach ($q->posts as $pid) {
            $p = wc_get_product($pid);
            if (!$p) continue;
            
            $name = esc_html($p->get_name());
            $sku = esc_html($p->get_sku() ?: '—');
            $price = $p->get_price_html() ?: wc_price(0);
            $edit = esc_url(get_edit_post_link($pid));
            
            $img_id = $p->get_image_id();
            $img_url = $img_id ? wp_get_attachment_image_url($img_id, 'thumbnail') : wc_placeholder_img_src();
            
            $stock_class = $p->get_stock_status() === 'instock' ? 'color:#065f46;background:#d1fae5;' : 'color:#991b1b;background:#fee2e2;';
            $stock_label = $p->get_stock_status() === 'instock' ? 'In Stock' : 'Out of Stock';
            $stock_html = "<span style=\"display:inline-block;padding:2px 8px;border-radius:12px;font-size:11px;font-weight:600;$stock_class\">$stock_label</span>";
            if ($p->managing_stock()) {
                $qty = $p->get_stock_quantity();
                $stock_html .= " <small style=\"color:#6b7280;margin-left:4px;\">($qty)</small>";
            }

            $rows .= "<tr style=\"border-bottom:1px solid #f3f4f6;transition:background 0.2s;\">
                <td style=\"padding:12px 20px;\"><img src=\"" . esc_url($img_url) . "\" style=\"width:40px;height:40px;border-radius:4px;object-fit:cover;border:1px solid #e5e7eb;\"></td>
                <td style=\"padding:12px 20px;\"><a href=\"$edit\" target=\"_blank\" style=\"font-weight:500;color:#4f46e5;text-decoration:none;\">$name</a></td>
                <td style=\"padding:12px 20px;color:#6b7280;font-size:13px;\">$sku</td>
                <td style=\"padding:12px 20px;\">$stock_html</td>
                <td style=\"padding:12px 20px;font-weight:600;color:#374151;font-size:13px;\">$price</td>
                <td style=\"padding:12px 20px;text-align:right;\"><a href=\"$edit\" class=\"button button-small\" target=\"_blank\" style=\"border-radius:4px;\">Edit</a></td>
            </tr>";
        }
    }
    wp_send_json_success(['html' => $rows, 'total' => (int) $q->found_posts]);
});

// AJAX: run a single batch immediately (manual fallback if WP-Cron is unreliable)
add_action('wp_ajax_bizmart_final_run_import_batch_now', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);
    bizmart_process_import_job();
    $job = bizmart_import_job_get();
    wp_send_json_success($job ? $job : null);
});

// AJAX: resume job (user confirms replacing/skipping)
add_action('wp_ajax_bizmart_final_resume_import_job', function() {
    bizmart_check_ajax_nonce('nonce');
    if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'Unauthorized'], 403);

    $action = isset($_POST['action_choice']) ? sanitize_text_field(wp_unslash($_POST['action_choice'])) : '';
    $job = bizmart_import_job_get();
    if (!$job) { wp_send_json_error(['message' => 'No active job'], 404); }
    if (($job['status'] ?? '') !== 'paused_for_confirmation') { wp_send_json_error(['message' => 'Job is not paused for confirmation'], 400); }

    if ($action === 'replace_all') {
        $job['policy'] = 'replace';
        $job['conflicts'] = [];
    } elseif ($action === 'skip_all') {
        $job['policy'] = 'skip';
        $job['conflicts'] = [];
    } elseif ($action === 'decide') {
        $decisions_raw = isset($_POST['decisions']) ? wp_unslash($_POST['decisions']) : '';
        $decisions = json_decode($decisions_raw, true);
        if (!is_array($decisions)) {
            wp_send_json_error(['message' => 'Invalid decisions payload'], 400);
        }

        $new_conflicts = [];
        foreach (($job['conflicts'] ?? []) as $conf) {
            $pid = is_array($conf) && isset($conf['id']) ? (int) $conf['id'] : 0;
            if ($pid <= 0) continue;
            $choice = $decisions[$pid] ?? null;
            if ($choice === 'replace') {
                $dry_run = !empty($job['dry_run']);
                if (!$dry_run) {
                    update_post_meta($pid, '_purchase_price', $conf['source'] ?? '');
                }
                $job['replaced'] = (int) ($job['replaced'] ?? 0) + 1;
            } elseif ($choice === 'skip') {
                $job['skipped'] = (int) ($job['skipped'] ?? 0) + 1;
            } else {
                $new_conflicts[] = $conf;
            }
        }
        $job['conflicts'] = $new_conflicts;
    } else {
        wp_send_json_error(['message' => 'Invalid action'], 400);
    }

    $job['status'] = 'running';
    $job['updated_at'] = current_time('mysql');
    bizmart_import_job_update($job);
    bizmart_import_job_schedule_next();
    wp_send_json_success($job);
});

// Note: cancel import endpoint removed by request — job cancellation is now not exposed via AJAX.

// AJAX: get counts for a meta key
if (!function_exists('bizmart_import_get_count')) {
    add_action('wp_ajax_bizmart_final_import_get_count', 'bizmart_import_get_count');

    function bizmart_import_get_count() {
        bizmart_check_ajax_nonce('nonce');
        if (!current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => 'Insufficient permissions'], 403);
        }

        global $wpdb;
        $meta_raw = wp_unslash($_POST['meta_key'] ?? '');
        $meta_keys = bizmart_final_import_parse_meta_keys($meta_raw);
        if (!$meta_keys) { wp_send_json_error(['message' => 'No key'], 400); }

        $chosen = '';
        $total_exists = 0;
        $res = ['total_non_empty' => 0, 'products' => 0, 'variations' => 0, 'total_exists' => 0, 'used_meta_key' => null];

        foreach ($meta_keys as $mk) {
            $mk = sanitize_text_field($mk);
            if ($mk === '') continue;

            $total_exists = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT pm.post_id)
                 FROM {$wpdb->postmeta} pm
                 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                 WHERE pm.meta_key = %s
                   AND p.post_type IN ('product','product_variation')",
                $mk
            ));

            $counts = $wpdb->get_results($wpdb->prepare(
                "SELECT p.post_type, COUNT(DISTINCT pm.post_id) as qty
                 FROM {$wpdb->postmeta} pm
                 JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                 WHERE pm.meta_key = %s
                   AND pm.meta_value != ''
                   AND p.post_type IN ('product', 'product_variation')
                 GROUP BY p.post_type",
                $mk
            ));

            $tmp = ['total_non_empty' => 0, 'products' => 0, 'variations' => 0, 'total_exists' => $total_exists, 'used_meta_key' => $mk];
            foreach ($counts as $c) {
                $tmp['total_non_empty'] += (int) $c->qty;
                if ($c->post_type === 'product') $tmp['products'] = (int) $c->qty;
                if ($c->post_type === 'product_variation') $tmp['variations'] = (int) $c->qty;
            }

            // Choose the first key that has any non-empty values; otherwise keep last computed.
            $res = $tmp;
            $chosen = $mk;
            if ($tmp['total_non_empty'] > 0) break;
        }

        if (!$chosen) {
            wp_send_json_error(['message' => 'No key'], 400);
        }
        wp_send_json_success($res);
    }
}
