<?php
// EN: If this file is called directly, abort.
// IT: Se questo file viene chiamato direttamente, interrompi.
if (! defined('ABSPATH')) {
    exit; // Exit
}

/*
 * FILE:        pkinex-handle-async-import.php
 * FROM  →      admin/pkinex-admin-init.php (AJAX actions registration)
 * TO    →      Async runners (e.g. class-pkinex-run-*-houzez.php in batches)
 * DESCRIPTION:
 * EN: Handles the asynchronous (AJAX-based) import flow for PK InExpress.
 *     - Validates nonce and user capabilities.
 *     - Dispatches "start", "step" and "cancel" actions.
 *     - Returns JSON responses for the admin UI.
 *
 * IT: Gestisce il flusso di importazione asincrona (basata su AJAX) per PK InExpress.
 *     - Valida nonce e permessi utente.
 *     - Instrada le azioni "start", "step" e "cancel".
 *     - Restituisce risposte JSON per l'interfaccia admin.
 */

// EN: Ensure the Houzez runner class is loaded for async imports.
// IT: Garantisce che la classe runner Houzez sia caricata per gli import asincroni.
/* require_once PKINEX_INEXPRESS_DIR . 'includes/services/houzez/class-pkinex-run-real-houzez.php'; */

/**
 * ============================================================
 * 1) AJAX HOOK REGISTRATION
 * ------------------------------------------------------------
 * EN: Register admin-ajax.php hooks for async import.
 * IT: Registra gli hook di admin-ajax.php per l'import asincrono.
 * ============================================================
 */
add_action('wp_ajax_pkinex_async_import_start', 'pkinex_async_import_start');
add_action('wp_ajax_pkinex_async_import_step', 'pkinex_async_import_step');
add_action('wp_ajax_pkinex_async_import_cancel', 'pkinex_async_import_cancel');

/**
 * ============================================================
 * 2) HELPER: CHECK PERMISSIONS + NONCE
 * ------------------------------------------------------------
 * EN: Centralize capability + nonce checks.
 * IT: Centralizza i controlli su permessi e nonce.
 * ============================================================
 *
 * @param string $action Nonce action string used both in wp_create_nonce and check_ajax_referer.
 */
function pkinex_async_check_permissions_and_nonce($action)
{
    // EN: Verify nonce sent by JS
    // IT: Verifica il nonce inviato dal JS
    check_ajax_referer($action, 'nonce');

    // EN: Restrict to admins (or similar) for safety
    // IT: Restringe ad admin (o ruoli simili) per sicurezza
    if (! current_user_can('manage_options')) {
        wp_send_json_error(
            array(
                'message' => __('You are not allowed to run this import.', 'pk-inexpress'),
            ),
            403
        );
    }
}

/**
 * EN: Helper to delete a job state option from the database.
 * IT: Helper per cancellare lo stato di un job dal database.
 *
 * @param string $job_id
 *     EN: Job identifier used when the async import was started.
 *     IT: Identificatore del job usato all'avvio dell'import asincrono.
 */
function pkinex_async_delete_job_state(string $job_id): void
{
    $job_id = trim($job_id);
    if ($job_id === '') {
        return;
    }

    // EN: Build the option key and delete it.
    // IT: Costruisci il nome dell'option e cancellala.
    $option_key = 'pkinex_async_job_' . $job_id;
    delete_option($option_key);
}

/**
 * ============================================================
 * 3) START HANDLER
 * ------------------------------------------------------------
 * EN: Bootstrap the async import job.
 *     - read saved options (source, target, XML URL, ID field, etc.)
 *     - validate minimal data
 *     - initialize a "job state"
 *     - store the state in a WordPress option by job_id.
 *
 * IT: Avvia il "job" di import asincrono.
 *     - legge le opzioni salvate (sorgente, destinazione, URL XML, campo ID, ecc.)
 *     - verifica che ci siano i dati minimi
 *     - inizializza uno "stato del job"
 *     - salva lo stato in una option di WordPress usando il job_id.
 * ============================================================
 */
function pkinex_async_import_start()
{
    // EN: Capability + nonce check (action name shared with JS)
    // IT: Controllo permessi + nonce (nome azione condiviso con il JS)
    pkinex_async_check_permissions_and_nonce('pkinex_async_import');

    // ====================================================
    // EN: 1) Read saved options (step 1/2 of Quick Import)
    // IT: 1) Leggi le opzioni salvate (step 1/2 del Quick Import)
    // ====================================================

    $service_source = sanitize_text_field((string) get_option('pkinex_service_source', ''));
    $service_target = sanitize_text_field((string) get_option('pkinex_service_target', ''));
    $source_type    = sanitize_text_field((string) get_option('pkinex_source_type', 'url'));
    $source_url     = (string) get_option('pkinex_source_url', '');
    $id_name        = sanitize_text_field((string) get_option('pkinex_id_name', ''));

    // ====================================================
    // EN: 2) Minimal validation: if something critical is missing, stop.
    // IT: 2) Validazione minima: se manca qualcosa di critico, fermati.
    // ====================================================

    if ($service_source === '' || $service_target === '') {
        wp_send_json_error(
            array(
                'message' => __('Missing source or target service. Please save the Quick Import options first.', 'pk-inexpress'),
            ),
            400
        );
    }

    if ($id_name === '') {
        wp_send_json_error(
            array(
                'message' => __('Missing ID field name. Please fill and save the Quick Import options.', 'pk-inexpress'),
            ),
            400
        );
    }

    // ====================================================
    // EN: 3) Build a new job_id and initial job state
    // IT: 3) Crea un nuovo job_id e lo stato iniziale del job
    // ====================================================

    $job_id  = uniqid('pkinex_job_', true);
    $user_id = get_current_user_id();

    $job_state = array(
        'job_id'         => $job_id,
        'user_id'        => $user_id,
        'service_source' => $service_source,
        'service_target' => $service_target,
        'source_type'    => $source_type,
        'source_url'     => $source_url,
        'id_name'        => $id_name,

        // EN: Progress information for IMPORT phase
        // IT: Informazioni di avanzamento per la fase di IMPORT
        'total'          => 0,
        'processed'      => 0,

        // EN: Progress for UPDATE phase (separate counters)
        // IT: Avanzamento per la fase di UPDATE (contatori separati)
        'update_total'    => 0,
        'update_processed' => 0,

        // EN: Global job status + phase
        // IT: Stato globale del job + fase corrente
        // EN: phase: delete | import | update | done
        // IT: phase: delete | import | update | done
        'status'         => 'pending',
        'phase'          => 'delete',

        'errors'         => array(),

        'delete_done'    => false,
        'update_done'    => false,

        'created_at'     => time(),
        'updated_at'     => time(),
    );

    $option_key = 'pkinex_async_job_' . $job_id;


    // EN: Save the job state without autoload (no need on every page load)
    // IT: Salva lo stato del job senza autoload (inutile caricarlo su ogni pagina)
    update_option($option_key, $job_state, false);


    // ====================================================
    // EN: 4) Return JSON response to JS
    // IT: 4) Restituisci la risposta JSON al JS
    // ====================================================
    wp_send_json_success(
        array(
            'message'        => __('Import job initialized.', 'pk-inexpress'),
            'job_id'         => $job_id,
            'total'          => (int) $job_state['total'],
            'processed'      => (int) $job_state['processed'],
            'done'           => false,
            'status'         => $job_state['status'],
            'phase'          => $job_state['phase'],
            'service_source' => $service_source,
            'service_target' => $service_target,
            'source_type'    => $source_type,
            'id_name'        => $id_name,
        )
    );
}



/**
 * ============================================================
 * 4) STEP HANDLER
 * ------------------------------------------------------------
 * EN: Process one "batch" of items:
 *     PHASE 1 → DELETE (sync, once)
 *     PHASE 2 → IMPORT NEW (async batches)
 *     PHASE 3 → UPDATE (sync, once, after import finishes)
 *
 * IT: Gestisce un "batch" dell'import:
 *     FASE 1 → DELETE (sincrono, una volta sola)
 *     FASE 2 → IMPORT NUOVI (batch asincroni)
 *     FASE 3 → UPDATE (sincrono, una volta sola, dopo la fine dell'import)
 * ============================================================
 */
function pkinex_async_import_step()
{
    // EN: Capability + nonce check (action name shared with JS)
    // IT: Controllo permessi + nonce (nome azione condiviso con il JS)
    pkinex_async_check_permissions_and_nonce('pkinex_async_import');

    // ====================================================
    // EN: 1) Read job_id from POST
    // IT: 1) Leggi il job_id dal POST
    // ====================================================
    // phpcs:ignore WordPress.Security.NonceVerification.Missing
    $job_id = isset($_POST['job_id']) ? sanitize_text_field(wp_unslash($_POST['job_id'])) : '';

    if ($job_id === '') {
        wp_send_json_error(
            array(
                'message' => __('Missing job identifier.', 'pk-inexpress'),
            ),
            400
        );
    }

    $option_key = 'pkinex_async_job_' . $job_id;

    // ====================================================
    // EN: 2) Load job state from the database
    // IT: 2) Carica lo stato del job dal database
    // ====================================================
    $job_state = get_option($option_key, null);

    if (! is_array($job_state)) {
        wp_send_json_error(
            array(
                'message' => __('Import job state not found. Please restart the import.', 'pk-inexpress'),
            ),
            404
        );
    }

    // EN: Early exit if job already done
    // IT: Uscita rapida se il job è già concluso
    if (isset($job_state['status']) && $job_state['status'] === 'done') {

        pkinex_async_delete_job_state($job_id);

        wp_send_json_success(
            array(
                'message'          => __('Import completed.', 'pk-inexpress'),
                'job_id'           => $job_id,
                'total'            => isset($job_state['total']) ? (int) $job_state['total'] : 0,
                'processed'        => isset($job_state['processed']) ? (int) $job_state['processed'] : 0,
                'update_total'     => isset($job_state['update_total']) ? (int) $job_state['update_total'] : 0,
                'update_processed' => isset($job_state['update_processed']) ? (int) $job_state['update_processed'] : 0,
                'done'             => true,
                'status'           => 'done',
                'phase'            => isset($job_state['phase']) ? $job_state['phase'] : 'done',
                'errors'           => isset($job_state['errors']) && is_array($job_state['errors']) ? $job_state['errors'] : array(),
            )
        );
    }

    // EN: If job is cancelled, do not execute more batches
    // IT: Se il job è stato cancellato, non eseguiamo altri batch
    if (isset($job_state['status']) && $job_state['status'] === 'cancelled') {

        pkinex_async_delete_job_state($job_id);

        wp_send_json_success(
            array(
                'message'          => __('Import has been cancelled.', 'pk-inexpress'),
                'job_id'           => $job_id,
                'total'            => isset($job_state['total']) ? (int) $job_state['total'] : 0,
                'processed'        => isset($job_state['processed']) ? (int) $job_state['processed'] : 0,
                'update_total'     => isset($job_state['update_total']) ? (int) $job_state['update_total'] : 0,
                'update_processed' => isset($job_state['update_processed']) ? (int) $job_state['update_processed'] : 0,
                'done'             => true,
                'status'           => 'cancelled',
                'phase'            => isset($job_state['phase']) ? $job_state['phase'] : 'cancelled',
                'errors'           => isset($job_state['errors']) && is_array($job_state['errors']) ? $job_state['errors'] : array(),
            )
        );
    }

    // ====================================================
    // EN: 3) Prepare batch parameters
    // IT: 3) Prepara parametri del batch
    // ====================================================
    $limit            = 2; // EN: batch size | IT: dimensione batch //qui imposto quanti immobili importare e updatare alla volta

    $delete_done      = ! empty($job_state['delete_done']);
    $update_done      = ! empty($job_state['update_done']);

    $total            = isset($job_state['total']) ? (int) $job_state['total'] : 0;
    $processed        = isset($job_state['processed']) ? (int) $job_state['processed'] : 0;

    $update_total     = isset($job_state['update_total']) ? (int) $job_state['update_total'] : 0;
    $update_processed = isset($job_state['update_processed']) ? (int) $job_state['update_processed'] : 0;

    $phase            = isset($job_state['phase']) ? (string) $job_state['phase'] : 'delete';

    // ====================================================
    // EN: 3bis) Prepare source data for the runner
    // IT: 3bis) Prepara i dati sorgente per il runner
    // ====================================================
    $source_data = isset($job_state['source_url']) ? (string) $job_state['source_url'] : '';
    $source_type = isset($job_state['source_type']) ? (string) $job_state['source_type'] : 'url';
    $id_name     = isset($job_state['id_name']) ? (string) $job_state['id_name'] : '';

    if ($source_data === '' || $id_name === '') {
        wp_send_json_error(
            array(
                'message' => __('Missing source data or ID field.', 'pk-inexpress'),
            ),
            500
        );
    }

    // ====================================================
    // EN: 4) Resolve runner class/file based on service_source + service_target
    // IT: 4) Risolve classe/file del runner in base a service_source + service_target
    // ====================================================
    $service_source = isset($job_state['service_source']) ? (string) $job_state['service_source'] : 'real';
    $service_target = isset($job_state['service_target']) ? (string) $job_state['service_target'] : 'houzez';

    // EN: Build class name, method base and file path (same convention as Pro)
    // IT: Costruisce nome classe, base dei metodi e path del file (stessa convenzione del Pro)
    $runner_class          = 'PKINEX_Runner' . ucfirst($service_source) . ucfirst($service_target);
    $runner_method_base    = 'pkinex_runner_' . strtolower($service_source) . '_' . strtolower($service_target);
    $runner_import_method  = $runner_method_base . '_batch';
    $runner_update_method  = $runner_method_base . '_update_batch';

    $runner_file = PKINEX_INEXPRESS_DIR
        . 'includes/services/'
        . strtolower($service_target)
        . '/class-pkinex-run-'
        . strtolower($service_source)
        . '-'
        . strtolower($service_target)
        . '.php';

    if (! file_exists($runner_file)) {
        wp_send_json_error(
            array(
                'message' => sprintf(
                    /* translators: 1: service source (XML provider), 2: service target (theme or destination). */
                    __('Import file not found for %1$s → %2$s.', 'pk-inexpress'),
                    $service_source,
                    $service_target
                ),
            ),
            500
        );
    }

    require_once $runner_file;

    if (! class_exists($runner_class)) {
        wp_send_json_error(
            array(
                'message' => sprintf(
                    /* translators: %s: name of the async runner class that could not be found. */
                    __('Import class %s not found.', 'pk-inexpress'),
                    $runner_class
                ),
            ),
            500
        );
    }

    $runner = new $runner_class(
        $source_data, // EN: URL or raw XML | IT: URL o XML grezzo
        $id_name,     // EN: unique ID field    | IT: campo ID univoco
        $source_type  // EN: 'url', 'file', ... | IT: 'url', 'file', ecc.
    );


    $is_done      = false;
    $batch_result = array();
    $message      = __('Import step executed.', 'pk-inexpress');

    // ====================================================
    // PHASE 1 → DELETE (sync, once)
    // FASE 1 → DELETE (sincrono, una volta sola)
    // ====================================================
    if ($phase === 'delete' && ! $delete_done) {

        $deleted_count = $runner->pkinex_run_delete_sync();

        $job_state['delete_done'] = true;
        $job_state['status']      = 'deleting';
        $job_state['phase']       = 'import';
        $job_state['updated_at']  = time();

        update_option($option_key, $job_state, false);

        wp_send_json_success(
            array(
                'message'          => sprintf(
                    /* translators: %d: number of items deleted during the delete phase. */
                    __('Delete phase completed (%d items).', 'pk-inexpress'),
                    (int) $deleted_count
                ),
                'job_id'           => $job_id,
                'total'            => (int) $job_state['total'],      // ancora 0, è ok
                'processed'        => (int) $job_state['processed'],  // ancora 0
                'update_total'     => (int) $job_state['update_total'],
                'update_processed' => (int) $job_state['update_processed'],
                'done'             => false,
                'status'           => $job_state['status'],
                'phase'            => $job_state['phase'],
                'errors'           => $job_state['errors'],
            )
        );
    }

    // ====================================================
    // PHASE 2 → IMPORT NEW (async batches)
    // FASE 2 → IMPORT NUOVI (batch asincroni)
    // ====================================================
    if ($phase === 'import') {

        // EN: Use already processed items as offset
        // IT: Usa il numero di elementi già processati come offset
        $offset       = $processed;
        if (! method_exists($runner, $runner_import_method)) {
            wp_send_json_error(
                array(
                    'message' => sprintf(
                        /* translators: %s: PHP method name expected on the runner instance during the import batch. */
                        __('Import batch method %s not found on runner.', 'pk-inexpress'),
                        $runner_import_method
                    ),
                ),
                500
            );
        }

        $batch_result = $runner->{$runner_import_method}($offset, $limit);


        $batch_import_done = isset($batch_result['import_done']) ? (int) $batch_result['import_done'] : 0;

        $batch_total       = isset($batch_result['total']) ? (int) $batch_result['total'] : 0;
        $batch_done_flag   = ! empty($batch_result['batch_done']);

        // EN: Set "total" only the first time
        // IT: Imposta "total" solo la prima volta
        if ($total === 0 && $batch_total > 0) {
            $job_state['total'] = $batch_total;
            $total              = $batch_total;
        }

        // EN: Update processed count
        // IT: Aggiorna il conteggio processed
        $job_state['processed'] = $processed + $batch_import_done;
        $processed              = $job_state['processed'];

        // EN: Merge errors (if any)
        // IT: Unisci gli eventuali errori
        if (! isset($job_state['errors']) || ! is_array($job_state['errors'])) {
            $job_state['errors'] = array();
        }
        if (! empty($batch_result['errors']) && is_array($batch_result['errors'])) {
            $job_state['errors'] = array_merge($job_state['errors'], $batch_result['errors']);
        }

        $job_state['updated_at'] = time();

        // EN: Determine whether IMPORT phase is finished
        // IT: Determina se la fase di IMPORT è terminata
        $import_finished_or_empty =
            $batch_done_flag
            || ($job_state['total'] > 0 && $job_state['processed'] >= $job_state['total'])
            || ($job_state['total'] === 0 && $job_state['processed'] === 0);

        if ($import_finished_or_empty) {
            // EN: Move to UPDATE phase, do not delete job state here
            // IT: Passa alla fase UPDATE, non cancellare qui lo stato del job
            $job_state['status']          = 'update_pending';
            $job_state['phase']           = 'update';
            $job_state['update_total']    = 0;
            $job_state['update_processed'] = 0;
            $job_state['updated_at']      = time();

            $message = __('Import phase completed. Starting update phase.', 'pk-inexpress');
            $is_done = false; // EN: job not fully done, we still have UPDATE phase
            // IT: job non concluso, manca ancora la fase di UPDATE

            update_option($option_key, $job_state, false);
        } else {
            // EN: There are still import batches to process
            // IT: Ci sono ancora batch di import da processare
            $job_state['status']     = 'running';
            $job_state['updated_at'] = time();
            $is_done                 = false;

            update_option($option_key, $job_state, false);
        }

        wp_send_json_success(
            array(
                'message'          => $message,
                'job_id'           => $job_id,
                'total'            => (int) $job_state['total'],
                'processed'        => (int) $job_state['processed'],
                'update_total'     => (int) $job_state['update_total'],
                'update_processed' => (int) $job_state['update_processed'],
                'done'             => $is_done,
                'status'           => $job_state['status'],
                'phase'            => $job_state['phase'],
                'errors'           => $job_state['errors'],
                'batch'            => $batch_result, // utile per debug in JS
            )
        );
    }

    // ====================================================
    // PHASE 3 → UPDATE (async batches)
    // FASE 3 → UPDATE (batch asincroni)
    // ====================================================
    if ($phase === 'update' && ! $update_done) {

        // EN: Use update_processed as offset for update batch
        // IT: Usa update_processed come offset per i batch di update
        $update_offset = $update_processed;
        if (! method_exists($runner, $runner_update_method)) {
            wp_send_json_error(
                array(
                    'message' => sprintf(
                          /* translators: %s: PHP method name expected on the runner instance during the update batch. */
                        __('Update batch method %s not found on runner.', 'pk-inexpress'),
                        $runner_update_method
                    ),
                ),
                500
            );
        }

        $update_result = $runner->{$runner_update_method}($update_offset, $limit);


        $batch_update_done = isset($update_result['update_done']) ? (int) $update_result['update_done'] : 0;
        $batch_update_total = isset($update_result['total']) ? (int) $update_result['total'] : 0;
        $update_batch_done_flag = ! empty($update_result['batch_done']);

        // EN: Initialize update_total if needed
        // IT: Inizializza update_total se necessario
        if ($update_total === 0 && $batch_update_total > 0) {
            $job_state['update_total'] = $batch_update_total;
            $update_total              = $batch_update_total;
        }

        // EN: Update update_processed count
        // IT: Aggiorna il contatore update_processed
        $job_state['update_processed'] = $update_processed + $batch_update_done;
        $update_processed              = $job_state['update_processed'];

        // EN: Merge errors (if any)
        // IT: Unisci gli eventuali errori
        if (! isset($job_state['errors']) || ! is_array($job_state['errors'])) {
            $job_state['errors'] = array();
        }
        if (! empty($update_result['errors']) && is_array($update_result['errors'])) {
            $job_state['errors'] = array_merge($job_state['errors'], $update_result['errors']);
        }

        $job_state['updated_at'] = time();

        // EN: Check if UPDATE phase is finished
        // IT: Verifica se la fase di UPDATE è terminata
        $update_finished_or_empty =
            $update_batch_done_flag
            || ($job_state['update_total'] > 0 && $job_state['update_processed'] >= $job_state['update_total'])
            || ($job_state['update_total'] === 0 && $job_state['update_processed'] === 0);

        if ($update_finished_or_empty) {
            $job_state['update_done'] = true;
            $job_state['status']      = 'done';
            $job_state['phase']       = 'done';
            $job_state['updated_at']  = time();

            $is_done = true;
            $message = __('Update phase completed. Job done.', 'pk-inexpress');

            // EN: Job fully completed → cleanup option
            // IT: Job completamente concluso → pulizia dell’option
            pkinex_async_delete_job_state($job_id);
        } else {
            $job_state['status']      = 'updating';
            $job_state['phase']       = 'update';
            $job_state['updated_at']  = time();

            $is_done = false;
            $message = __('Update batch executed.', 'pk-inexpress');

            update_option($option_key, $job_state, false);
        }

        wp_send_json_success(
            array(
                'message'          => $message,
                'job_id'           => $job_id,
                'total'            => (int) $job_state['total'],
                'processed'        => (int) $job_state['processed'],
                'update_total'     => (int) $job_state['update_total'],
                'update_processed' => (int) $job_state['update_processed'],
                'done'             => $is_done,
                'status'           => $job_state['status'],
                'phase'            => $job_state['phase'],
                'errors'           => $job_state['errors'],
                'update_batch'     => $update_result, // utile per debug
            )
        );
    }

    // EN: Fallback (should not be reached)
    // IT: Fallback (non dovrebbe mai essere raggiunto)
    wp_send_json_error(
        array(
            'message' => __('Invalid import phase.', 'pk-inexpress'),
        ),
        500
    );
}


/**
 * ============================================================
 * 5) CANCEL HANDLER
 * ------------------------------------------------------------
 * EN: Allow the admin to cancel an ongoing job.
 *     - Marks the job as "cancelled" (status).
 *     - Updates timestamps and keeps the job state stored for inspection.
 *
 * IT: Permette all'admin di annullare un job in corso.
 *     - Imposta lo stato del job su "cancelled".
 *     - Aggiorna i timestamp e mantiene lo stato per consultazione.
 * ============================================================
 */
function pkinex_async_import_cancel()
{
    // EN: Capability + nonce check
    // IT: Controllo permessi + nonce
    pkinex_async_check_permissions_and_nonce('pkinex_async_import');

    // phpcs:ignore WordPress.Security.NonceVerification.Missing
    $job_id = isset($_POST['job_id']) ? sanitize_text_field(wp_unslash($_POST['job_id'])) : '';

    if ($job_id === '') {
        wp_send_json_error(
            array(
                'message' => __('Missing job identifier.', 'pk-inexpress'),
            ),
            400
        );
    }

    $option_key = 'pkinex_async_job_' . $job_id;
    $job_state  = get_option($option_key, null);

    // EN: If there is no state, we still consider the cancel "accepted"
    // IT: Se non esiste uno stato, consideriamo comunque la cancellazione "accettata"
    if (! is_array($job_state)) {
        wp_send_json_success(
            array(
                'message' => __('Import cancelled (no state found).', 'pk-inexpress'),
                'job_id'  => $job_id,
            )
        );
    }

    // EN: Mark as cancelled and delete from database
    // IT: Segna come cancellato e cancella dal database
    $job_state['status']     = 'cancelled';
    $job_state['updated_at'] = time();

    pkinex_async_delete_job_state($job_id);

    wp_send_json_success(
        array(
            'message' => __('Import cancelled.', 'pk-inexpress'),
            'job_id'  => $job_id,
            'status'  => 'cancelled',
        )
    );
}
