<?php

namespace WcElectronInvoice;

use WcElectronInvoice\Admin\Settings\OptionPage;
use WcElectronInvoice\Providers\OrderQuery;
use WcElectronInvoice\Utils\TimeZone;
use function WcElectronInvoice\Functions\disableInvoiceOnOrderTotalZero;
use function WcElectronInvoice\Functions\getXmlFromOrder;
use function WcElectronInvoice\Functions\sendEmailToAdminForError;

if (! defined('ABSPATH')) {
    exit; // Exit if accessed directly.
}

/**
 * InvoiceApi integration - ACUBE (Italian Invoicing API)
 *
 * @see https://docs.acubeapi.com/documentation/gov-it/
 */
class InvoiceApi
{
    /**
     * Normalize mixed document type inputs to provider order types.
     *
     * @param string $documentType
     *
     * @return string
     */
    protected static function normalizeProviderDocumentType(string $documentType): string
    {
        $documentType = strtolower(trim($documentType));
        $documentType = str_replace('-', '_', sanitize_key($documentType));

        if ('credit_note' === $documentType || 'shop_order_refund' === $documentType) {
            return 'shop_order_refund';
        }

        return 'shop_order';
    }

    /**
     * Resolve the concrete provider document targeted by UI/admin actions.
     *
     * @param int|string  $id
     * @param string      $provider
     * @param array|null  $item
     * @param string|null $documentType
     * @param int|string  $parentId
     *
     * @return array<string,mixed>
     */
    protected static function resolveActionDocumentContext($id, string $provider = 'woocommerce', ?array $item = null, ?string $documentType = null, $parentId = 0): array
    {
        if ($item && ! $provider) {
            $provider = isset($item['order_provider']) ? (string)$item['order_provider'] : '';
            $provider = ! $provider && isset($item['provider']) ? (string)$item['provider'] : $provider;
        }

        $provider      = sanitize_key((string)$provider);
        $resolvedOrder = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder((int)$id, $provider);
        $requestedType = $documentType ?: (string)($item['document_type'] ?? $item['order_type'] ?? '');
        $requestedType = self::normalizeProviderDocumentType(
            $requestedType ?: (is_object($resolvedOrder) && method_exists($resolvedOrder, 'get_type') ? (string)$resolvedOrder->get_type() : 'shop_order')
        );

        $documentParentId = (int)$parentId;
        if ($item && isset($item['parent_id'])) {
            $documentParentId = (int)$item['parent_id'];
        } elseif (! $documentParentId && is_object($resolvedOrder) && method_exists($resolvedOrder, 'get_parent_id')) {
            $documentParentId = (int)$resolvedOrder->get_parent_id();
        }

        $document = \WcElectronInvoice\Functions\resolveProviderDocument((int)$id, $provider, $requestedType, $documentParentId);
        if (! $document) {
            $document = $resolvedOrder;
        }

        $orderType = $requestedType;
        if (is_object($document) && method_exists($document, 'get_type')) {
            $orderType = (string)$document->get_type();
        }

        return [
            'provider'      => $provider,
            'order'         => $resolvedOrder,
            'document'      => $document,
            'order_type'    => $orderType,
            'document_type' => \WcElectronInvoice\Functions\resolveDocumentTypeFromOrderType($orderType),
            'parent_id'     => $documentParentId,
        ];
    }

    /**
     * Persist invoice sent flags through the provider abstraction.
     *
     * @param mixed $document
     * @param mixed $timestamp
     *
     * @return void
     */
    protected static function markDocumentAsSent($document, $timestamp = null): void
    {
        if (! is_object($document)) {
            return;
        }

        $timestamp = null === $timestamp ? time() : (int)$timestamp;
        if (method_exists($document, 'set_invoice_sent')) {
            $document->set_invoice_sent('sent');
        } elseif (method_exists($document, 'update_meta')) {
            $document->update_meta('_invoice_sent', 'sent');
        }

        if (method_exists($document, 'set_invoice_sent_timestamp')) {
            $document->set_invoice_sent_timestamp($timestamp);
        } elseif (method_exists($document, 'update_meta')) {
            $document->update_meta('_invoice_sent_timestamp', $timestamp);
        }
    }


    /**
     * getUserIntegrationData
     *
     * @param $userID
     *
     * @return object
     */
    public static function getSdiPOPIntegrationData(): object
    {
        $integrationPluginActive = get_option('wc_el_inv-addon-sdi-via-pop', false);
        $isLiveActive            = 'active' === get_option("wc_el_inv-active_sdipop_integration");
        $isSandboxActive         = 'active' === get_option("wc_el_inv-active_sdipop_integration_sandbox");
        $suffix                  = (! $isLiveActive && $isSandboxActive) ? '_sandbox' : '';
        $sdiViaPOPSignature      = get_option("wc_el_inv-business_apply_signature{$suffix}");
        $sdiViaPOPStorage        = get_option("wc_el_inv-business_apply_legal_storage{$suffix}");

        return (object)[
            'active_plugin_integration' => (bool)$integrationPluginActive,
            'active_integration'        => $isLiveActive || $isSandboxActive,
            'live_integration'          => $isLiveActive,
            'sandbox_integration'       => $isSandboxActive,
            'active_signature'          => 'active' === $sdiViaPOPSignature,
            'active_storage'            => 'active' === $sdiViaPOPStorage,
            'live_signature'            => 'active' === get_option('wc_el_inv-business_apply_signature'),
            'live_storage'              => 'active' === get_option('wc_el_inv-business_apply_legal_storage'),
            'sandbox_signature'         => 'active' === get_option('wc_el_inv-business_apply_signature_sandbox'),
            'sandbox_storage'           => 'active' === get_option('wc_el_inv-business_apply_legal_storage_sandbox'),
        ];
    }

    /**
     * scheduledOptions
     *
     * @return object
     */
    public static function scheduledOptions()
    {
        $level                  = \WcElectronInvoice\Wizard::popGetUserLevel();
        $automaticSendingActive = get_option('wc_el_inv-enable_automatic_sending');
        if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic')) {
            $automaticSendingActive = 'no';
        }

        return (object)[
            'status' => get_option('wc_el_inv-automatic_sending_status'),
            'active' => $automaticSendingActive,
            'time'   => get_option('wc_el_inv-automatic_sending_time'),
            'nonce'  => wp_create_nonce('wc_el_inv-scheduled_nonce'),
        ];
    }

    /**
     * Job timing
     *
     * @return int|mixed|null
     */
    public static function jobTiming($type = null)
    {
        $scheduledOptions = self::scheduledOptions();

        $devTime  = time() + MINUTE_IN_SECONDS * 5 + mt_rand(1, 10);
        $prodTime = time() + HOUR_IN_SECONDS * (int)$scheduledOptions->time + mt_rand(1, 10);
        $prodTime = apply_filters('wc_el_inv-job_timing', $prodTime);

        if ('notify' === $type) {
            // Notifications default 2 hour
            $notifyTiming = apply_filters("wc_el_inv-job_notify_timing_default", 2);
            $devTime      = time() + MINUTE_IN_SECONDS * 5 + mt_rand(1, 10);
            $prodTime     = time() + (HOUR_IN_SECONDS * $notifyTiming) + mt_rand(1, 10);

            $prodTime = apply_filters('wc_el_inv-job_notify_timing', $prodTime);
        }

        return 'dev' === WC_EL_INV_ENV ? $devTime : $prodTime;
    }

    /**
     * Scheduled
     *
     * @param string $hook       Action hook to execute when the event is run.
     * @param array  $data       Optional. Array containing arguments to pass to the
     *                           hook's callback function. Each value in the array is passed
     *                           to the callback as an individual parameter. The array keys
     *                           are ignored. Default: empty array.
     * @param int    $time       Unix timestamp (UTC) for when to next run the event.
     *
     * @return bool
     */
    public static function scheduled(string $hook, array $data, int $time): bool
    {
        $scheduled = wp_schedule_single_event(
            $time,
            $hook,
            $data
        );

        $timeZone = new TimeZone();
        $timeZone = new \DateTimeZone($timeZone->getTimeZone()->getName());
        $dateTime = new \DateTime('now');
        $dateTime->setTimezone($timeZone);
        $date = $dateTime->format('d/m/Y H:i');

        if ($scheduled) {
            // Log
            \WcElectronInvoice\Functions\log("Scheduled - {$hook} \nDate: {$date} \nTimeStamp: {$time}\nEvent Data: " . wp_json_encode(array($data)) . "\n");

            return true;
        }

        \WcElectronInvoice\Functions\log("Scheduled Error - {$hook} \nDate: {$date} \nTimeStamp: {$time}\nEvent Data: " . wp_json_encode(array($data)) . "\n");

        return false;
    }

    /**
     * Change status trigger
     *
     * @param int    $orderID
     * @param string $from
     * @param string $to
     * @param string $provider The provider (default woocommerce)
     *
     * @return bool
     * @throws \Exception
     */
    public static function changeStatus(int $orderID, string $from, string $to, string $provider = 'woocommerce'): bool
    {
        $getSdiPOPIntegrationData = self::getSdiPOPIntegrationData();
        $level                    = \WcElectronInvoice\Wizard::popGetUserLevel();
        if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') ||
            (! $getSdiPOPIntegrationData->active_plugin_integration ||
             ! $getSdiPOPIntegrationData->active_integration)

        ) {
            return false;
        }

        $scheduledOptions = self::scheduledOptions();

        if ('yes' !== $scheduledOptions->active) {
            return false;
        }

        // Get order
        $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($orderID, $provider);
        // Check WC_Order instance
        $wcOrderClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order');

        if (! is_subclass_of($order, '\WcElectronInvoice\Providers\Order') &&
            ! $order instanceof $wcOrderClass) {
            return false;
        }

        // Return if total is 0
        if (disableInvoiceOnOrderTotalZero($order)) {
            return false;
        }

        // Check if uuid exist
        $uuid = $order->get_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', 'invoice'));
        if ($uuid) {
            // Log
            \WcElectronInvoice\Functions\log("changeStatus > UUID exist: {$uuid}" . "\n");

            return false;
        }

        // Check if the invoice was already sent
        $checkSent = $order->get_meta("_invoice_sent", true);
        if ('sent' === $checkSent) {
            // Log
            \WcElectronInvoice\Functions\log("changeStatus > Document is sent: {$checkSent}" . "\n");

            return false;
        }

        $valueOrderStatus = $scheduledOptions->status;
        // FROM completed or FROM equal TO or TO not completed return false
        if ($valueOrderStatus === $from || $from === $to || $valueOrderStatus !== $to) {
            $nextScheduled = wp_next_scheduled(
                'PoPToSDIJobsScheduledImport',
                [$order->get_id(), $provider]
            );
            // Clear scheduled
            if ($nextScheduled) {
                wp_clear_scheduled_hook('PoPToSDIJobsScheduledImport', [$order->get_id(), $provider]);
            }

            return false;
        }

        // Scheduled Import
        $time          = self::jobTiming();
        $nextScheduled = wp_next_scheduled(
            'PoPToSDIJobsScheduledImport',
            [$order->get_id(), $provider]
        );

        if (! $nextScheduled) {
            self::scheduled(
                'PoPToSDIJobsScheduledImport',
                [$order->get_id(), $provider],
                $time
            );
        } else {
            \WcElectronInvoice\Functions\log("Event already scheduled for order {$order->get_id()} and provider {$provider} to the " . date('Y-m-d H:i:s',
                    $nextScheduled));
        }

        return true;
    }

    /**
     * Auto complete trigger
     *
     * @param int    $id
     * @param string $provider The provider (default woocommerce)
     *
     * @return bool
     * @throws \Exception
     */
    public static function autoCompleted(int $id, string $provider = 'woocommerce'): bool
    {
        $getSdiPOPIntegrationData = self::getSdiPOPIntegrationData();
        $level                    = \WcElectronInvoice\Wizard::popGetUserLevel();
        if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') ||
            (! $getSdiPOPIntegrationData->active_plugin_integration ||
             ! $getSdiPOPIntegrationData->active_integration)

        ) {
            return false;
        }

        $scheduledOptions = self::scheduledOptions();

        if ('yes' !== $scheduledOptions->active) {
            return false;
        }

        $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);
        // Check if order exist.
        $checkSent = $order->get_meta("_invoice_sent", true);
        // Check if uuid exist
        $uuid = $order->get_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', 'invoice'));
        // Check WC_Order instance
        $wcOrderClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order');

        if (! is_subclass_of($order, '\WcElectronInvoice\Providers\Order') &&
            ! $order instanceof $wcOrderClass) {
            return false;
        }

        // Return if total is 0
        if (disableInvoiceOnOrderTotalZero($order)) {
            return false;
        }

        // Document id exist
        if (isset($uuid) && '' !== $uuid) {
            // Log
            \WcElectronInvoice\Functions\log("autoCompleted > UUID exist: {$uuid}" . "\n");

            return false;
        }

        // Sent
        if ('sent' === $checkSent) {
            // Log
            \WcElectronInvoice\Functions\log("autoCompleted > Document is sent: {$checkSent}" . "\n");

            return false;
        }

        // Check status.
        $valueOrderStatus = $scheduledOptions->status;
        $orderStatus      = $order->get_status();
        if ($valueOrderStatus === $orderStatus) {
            // Scheduled Import
            $time          = self::jobTiming();
            $nextScheduled = wp_next_scheduled(
                'PoPToSDIJobsScheduledImport',
                [$order->get_id(), $provider]
            );
            if (! $nextScheduled) {
                self::scheduled(
                    'PoPToSDIJobsScheduledImport',
                    [$order->get_id(), $provider],
                    $time
                );
            } else {
                \WcElectronInvoice\Functions\log("Event already scheduled for order {$order->get_id()} and provider {$provider} to the " . date('Y-m-d H:i:s',
                        $nextScheduled));
            }

            return true;
        }

        return false;
    }

    /**
     * actions
     *
     * @param $output
     * @param $id
     * @param $item
     *
     * @return string
     */
    public static function actions($output, $id, $item = null, $provider = 'woocommerce'): string
    {
        $getSdiPOPIntegrationData = self::getSdiPOPIntegrationData();
        if (! $getSdiPOPIntegrationData->active_plugin_integration) {
            return $output;
        }
        $level = \WcElectronInvoice\Wizard::popGetUserLevel();
        // Get provider from item data
        if ($item && ! $provider) {
            $provider = isset($item['order_provider']) ? $item['order_provider'] : null;
            $provider = ! $provider && isset($item['provider']) ? $item['provider'] : $provider;
        }

        $context           = self::resolveActionDocumentContext($id, (string)$provider, is_array($item) ? $item : null);
        $orderProvider     = $context['document'];
        if (! $orderProvider) {
            return $output;
        }
        $uuidMetaKey       = \WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', $context['document_type']);
        $notificationsKey  = \WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', $context['document_type']);
        $validationMetaKey = 'sdi_pop-' . $context['document_type'] . '_validation_errors';
        $uuid              = $orderProvider->get_meta($uuidMetaKey) ?: null;
        $checkSent         = method_exists($orderProvider, 'get_invoice_sent') ? $orderProvider->get_invoice_sent() : null;
        $notificationsData = $orderProvider->get_meta($notificationsKey) ?: null;
        $errorsData        = $orderProvider->get_meta($validationMetaKey) ?: null;
        $timeoutPending    = \WcElectronInvoice\Functions\normalizeBooleanMeta(
            $orderProvider->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey())
        );
        $timeoutChannel     = (string)($orderProvider->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey()) ?: '');
        $timeoutDocType     = (string)($orderProvider->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey()) ?: $context['document_type']);
        $orderNumberInvoice = method_exists($orderProvider, 'get_order_number_invoice') ? (string)$orderProvider->get_order_number_invoice() : '';
        $buttonDocumentType = $context['order_type'];

        $send = __('Invoice', WC_EL_INV_TEXTDOMAIN);
        if ($orderProvider->get_type() === 'shop_order_refund' || (is_array($item) && 'shop_order_refund' === $item['order_type'])) {
            $send = __('Credit note', WC_EL_INV_TEXTDOMAIN);
        }
        $sent          = __('Sent', WC_EL_INV_TEXTDOMAIN);
        $notifications = __('Notifications', WC_EL_INV_TEXTDOMAIN);
        $preserve      = __('Preserve', WC_EL_INV_TEXTDOMAIN);

        $output .= '<div class="sdi_pop_actions">';
        $output .= sprintf('<hr><h4>%s:</h4>', __('SdI', WC_EL_INV_TEXTDOMAIN));
        if ($timeoutPending && 'sdi' === $timeoutChannel) {
            $output .= sprintf(
                '<div class="pop-notice notice-warning timeout-native-pending"><p>%s</p><p>%s%s</p></div>',
                esc_html__('Send not confirmed due to provider timeout. Do not resend: verify with POP support and enter document UUID.',
                    WC_EL_INV_TEXTDOMAIN),
                esc_html__('Invoice number: ', WC_EL_INV_TEXTDOMAIN),
                esc_html('' !== $orderNumberInvoice ? $orderNumberInvoice : 'N/A')
            );
            $output .= sprintf(
                '<input type="text" class="native-timeout-uuid-input" style="display:block;width:100%%;min-width:100%%;margin:0 0 8px 0;" id="native_timeout_uuid_%1$s" placeholder="%2$s" />',
                esc_attr($id),
                esc_attr__('Enter verified UUID', WC_EL_INV_TEXTDOMAIN)
            );
            $output .= sprintf(
                '<a href="javascript:;" data-provider="%1$s" data-id="%2$s" data-channel="sdi" data-document-type="%3$s" class="api-action confirm-timeout-uuid button button-primary" id="api_confirm_timeout_uuid_%2$s">%4$s</a>',
                esc_attr($provider),
                esc_attr($id),
                esc_attr($timeoutDocType),
                esc_html__('Confirm UUID', WC_EL_INV_TEXTDOMAIN)
            );
            $output .= sprintf(
                '<a href="javascript:;" data-provider="%1$s" data-id="%2$s" data-channel="sdi" data-document-type="%4$s" class="api-action cancel-timeout-pending button button-secondary" id="api_cancel_timeout_%2$s">%3$s</a>',
                esc_attr($provider),
                esc_attr($id),
                esc_html__('Cancel timeout', WC_EL_INV_TEXTDOMAIN),
                esc_attr($buttonDocumentType)
            );
            $output .= '</div>';

            return $output;
        }

        if ($uuid) {
            // Created
            $output .= "<a disabled='disabled' data-provider='{$provider}' data-id='{$id}' data-uuid='{$uuid}' class='disabled created button button-secondary' id='api_created_{$id}'>{$sent}</a>";
            // Notifications
            if (! $getSdiPOPIntegrationData->active_integration ||
                ! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic')
            ) {
                $output .= "<a disabled='disabled' data-provider='{$provider}' data-id='{$id}' data-document-type='{$buttonDocumentType}' data-uuid='{$uuid}' class='disabled notifications button button-primary' id='api_notifications_{$id}'>{$notifications}</a>";
            } else {
                $output .= "<a href='javascript:;' data-provider='{$provider}' data-id='{$id}' data-document-type='{$buttonDocumentType}' data-uuid='{$uuid}' class='api-action notifications button button-primary' id='api_notifications_{$id}'>{$notifications}</a>";
            }

            if (! empty($notificationsData)) {
                foreach ($notificationsData as $notification) {
                    $typeClass = isset($notification['type']) && preg_match("/<[^<]+>/",
                        $notification['type']) === 1 ? 'PRESERVE' : $notification['type'];
                    if ('PRESERVE' === $typeClass && 400 === (int)$notification['code']) {
                        if ($getSdiPOPIntegrationData->active_storage) {
                            if (! $getSdiPOPIntegrationData->active_integration ||
                                ! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic')
                            ) {
                                $output .= "<a disabled='disabled' data-provider='{$provider}' data-id='{$id}' data-document-type='{$buttonDocumentType}' data-uuid='{$uuid}' class='disabled preserve button button-primary' id='api_preserve_{$id}'>{$preserve}</a>";
                            } else {
                                $output .= "<a href='javascript:;' data-provider='{$provider}' data-id='{$id}' data-document-type='{$buttonDocumentType}' data-uuid='{$uuid}' class='api-action preserve button button-primary' id='api_preserve_{$id}'>{$preserve}</a>";
                            }
                        }
                        break;
                    }
                }
            }
        } else {
            // Create
            if (! $getSdiPOPIntegrationData->active_integration ||
                ! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic')
            ) {
                $output .= "<a disabled='disabled' data-provider='{$provider}' data-id='{$id}' data-document-type='{$buttonDocumentType}' data-uuid='{$uuid}' class='disabled create button button-primary' id='api_create_{$id}'>{$send}</a>";
            } else {
                $output .= "<a href='javascript:;' data-provider='{$provider}' data-id='{$id}' data-document-type='{$buttonDocumentType}' data-uuid='{$uuid}' class='api-action create button button-primary' id='api_create_{$id}'>{$send}</a>";
            }
        }
        $output .= '</div>';

        if ($uuid && ! empty($notificationsData) && 'sent' === $checkSent) {
            $output .= '<div style="margin: 10px 0 0 0;" class="pop-notice notice-info">';
            $types  = [];
            foreach ($notificationsData as $notification) {
                $type = $notification['type'] ?: 'NAN';
                if (! in_array($type, $types)) {
                    if ('NAN' !== $type) {
                        $typeClass = preg_match("/<[^<]+>/",
                            $notification['type']) === 1 ? 'PRESERVE' : $notification['type'];
                        $output    .= "<p class='type-{$typeClass}'>{$notification['type']}: {$notification['message']}</p>";
                    } else {
                        $output .= "<p class='type-NAN'>{$notification['message']}</p>";
                    }
                }

                $types[] = $type;
            }
            $output .= '</div>';
        }

        if (! empty($errorsData) && 'sent' !== $checkSent) {
            $output .= '<div style="margin: 10px 0 0 0;" class="pop-notice notice-error">';
            $output .= "<p class='errors'>{$errorsData}</p>";
            $output .= '</div>';
        }

        return $output;
    }

    /**
     * createAndSendInvoice
     *
     * @return mixed|void
     * @throws \Exception
     */
    public static function createAndSendInvoice()
    {
        $getSdiPOPIntegrationData = self::getSdiPOPIntegrationData();
        $level                    = \WcElectronInvoice\Wizard::popGetUserLevel();
        $provider                 = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST,
            'provider') ?? '');
        $id                       = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? '');
        $action                   = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null;
        $method                   = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null;

        if (! \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'feCronJobUploadRequest'])) {
            if (! check_ajax_referer('wc_el_inv_ajax-ajax_nonce', 'nonce', false)) {
                wp_send_json_error(['message' => esc_html__('Unauthorized.', WC_EL_INV_TEXTDOMAIN)]);
                return;
            }
            if (! current_user_can('manage_woocommerce')) {
                wp_send_json_error(['message' => esc_html__('Forbidden.', WC_EL_INV_TEXTDOMAIN)]);
                return;
            }
        }

        if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_SDIPOP)) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi',
                    'channel_mismatch');
                wp_send_json_error([
                    'message' => esc_html__('Action not allowed: another integration channel is active.',
                        WC_EL_INV_TEXTDOMAIN),
                    'code'    => $preconditionError['code'],
                ]);
            }

            return;
        }

        if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') ||
            (! $getSdiPOPIntegrationData->active_plugin_integration ||
             ! $getSdiPOPIntegrationData->active_integration)
        ) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi',
                    'integration_inactive');
                wp_send_json_error([
                    'message' => esc_html__('Action not allowed.',
                        WC_EL_INV_TEXTDOMAIN),
                    'code'    => $preconditionError['code'],
                ]);
            }

            return;
        }

        // I check the action parameter if it does not match I get an error
        if ('createAndSendInvoice' !== $action) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi',
                    'action_invalid');
                wp_send_json_error([
                    'message' => esc_html__('Action not valid.',
                        WC_EL_INV_TEXTDOMAIN),
                    'code'    => $preconditionError['code'],
                ]);
            }

            return;
        }

        $requestedType = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'document_type') ?? '');
        $context       = self::resolveActionDocumentContext((int)$id, $provider, null, $requestedType);
        $order         = $context['document'];
        if (! $order) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }
        $type    = $context['document_type'];
        $metaKey = \WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', $type);

        $uuid = $order->get_meta($metaKey);
        // Document id exist
        if (isset($uuid) && '' !== $uuid) {
            // Log
            \WcElectronInvoice\Functions\log(
                \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi', "UUID exist: {$uuid}") . "\n"
            );

            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi',
                    'uuid_already_exists');
                wp_send_json_error([
                    'message' => \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        sprintf(esc_html__('UUID exist: %s', WC_EL_INV_TEXTDOMAIN), $uuid)),
                    'code'    => $preconditionError['code'],
                ]);
            }

            return;
        }

        $lockKey      = \WcElectronInvoice\Functions\buildDbLockKey('sdi', 'create_and_send', (int)$order->get_id(),
            (string)$provider, $metaKey);
        $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision(
            \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0),
            \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob'])
        );
        if (! $lockDecision['continue']) {
            if ($lockDecision['respond_error']) {
                $lockError = \WcElectronInvoice\Functions\mapLockBusyError('sdi', 'create_and_send');
                wp_send_json_error([
                    'message' => esc_html__('Action skipped: another send/create process is already running for this order.',
                        WC_EL_INV_TEXTDOMAIN),
                    'code'    => $lockError['code'],
                ]);
            }

            return;
        }

        try {
            add_filter('wc_el_inv-params_filter_before_api_request', function ($params) {
                $integrationMeta = \WcElectronInvoice\Functions\buildNativeCreateIntegrationMeta('sdi');
                if (! empty($integrationMeta['integration'])) {
                    $params['integration'] = $integrationMeta['integration'];
                }

                return $params;
            });

            $data             = getXmlFromOrder($order);
            $decodedJson      = \WcElectronInvoice\Functions\safeJsonDecode($data);
            $response         = $decodedJson['data'];
            $overquotaPayload = \WcElectronInvoice\Functions\extractPopApiOverquotaUiPayload($response);
            if (\WcElectronInvoice\Functions\isLikelyTimeoutError($data)) {
                self::markNativeTimeoutPending($order, 'sdi', $type);
            }

            if (is_array($overquotaPayload)) {
                \WcElectronInvoice\Functions\persistPopApiOverquotaState($order, $overquotaPayload);
                \WcElectronInvoice\Functions\log(
                    \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        'overquota block while generating XML') .
                    "\n" . print_r($overquotaPayload, true) . "\n"
                );

                if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                    wp_send_json_error($overquotaPayload, (int)($overquotaPayload['status'] ?? 500));
                }

                return;
            }

            if (! $decodedJson['ok'] || ! is_object($response)) {
                if (\WcElectronInvoice\Functions\isLikelyTimeoutError($data)) {
                    self::markNativeTimeoutPending($order, 'sdi', $type);
                }
                \WcElectronInvoice\Functions\log(
                    \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        'check json response: Invalid response from the XML generator:') .
                    "\n" . print_r($data, true) .
                    (! empty($decodedJson['error']) ? "\njson_error: " . $decodedJson['error'] : '') . "\n"
                );

                if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                    wp_send_json_error([
                        'message' => \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                            esc_html__('Invalid response from the XML generator.',
                                WC_EL_INV_TEXTDOMAIN)),
                        'raw'     => $data,
                        'error'   => $decodedJson['error'],
                    ]);
                }

                return;
            }

            if (
                ! property_exists($response, 'message') ||
                ! property_exists($response, 'data') ||
                ! property_exists($response, 'xml_string')
            ) {
                if (\WcElectronInvoice\Functions\isLikelyTimeoutError($response)) {
                    self::markNativeTimeoutPending($order, 'sdi', $type);
                }
                \WcElectronInvoice\Functions\log(
                    \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        'check json property: Invalid response from the XML generator:') .
                    "\n" . print_r($response, true) . "\n"
                );

                if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                    wp_send_json_error([
                        'message' => \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                            esc_html__('Invalid response from the XML generator.',
                                WC_EL_INV_TEXTDOMAIN)),
                        'check'   => 'json property',
                        'raw'     => $response,
                    ]);
                }

                return;
            }

            if (! $response->success) {
                if (\WcElectronInvoice\Functions\isLikelyTimeoutError($response)) {
                    self::markNativeTimeoutPending($order, 'sdi', $type);
                }
                \WcElectronInvoice\Functions\log(
                    \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        'check success: Invalid response from the XML generator') .
                    "\n" . print_r($response, true) . "\n"
                );

                if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                    $message = \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        esc_html__('Invalid response from the XML generator.',
                            WC_EL_INV_TEXTDOMAIN));
                    $detail  = \WcElectronInvoice\Functions\extractNativeErrorDetail($response);
                    if ('' !== $detail) {
                        $message .= ' ' . $detail;
                    }
                    wp_send_json_error([
                        'message' => $message,
                        'check'   => 'success',
                        'raw'     => $response,
                    ]);
                }

                return;
            }

            $responseData = is_object($response->data ?? null) ? $response->data : null;
            $responseUuid = (is_object($responseData) && property_exists($responseData,
                    'uuid')) ? (string)$responseData->uuid : '';
            if (! \WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($responseUuid)) {
                \WcElectronInvoice\Functions\log(
                    \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        'check uuid: Invalid response from the XML generator') .
                    "\n" . print_r($response, true) . "\n"
                );

                if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                    $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi',
                        'response_uuid_missing');
                    $message           = \WcElectronInvoice\Functions\prefixNativeFlowMessage('sdi',
                        esc_html__('Invalid response from the XML generator.',
                            WC_EL_INV_TEXTDOMAIN));
                    $detail            = \WcElectronInvoice\Functions\extractNativeErrorDetail($response);
                    if ('' !== $detail) {
                        $message .= ' ' . $detail;
                    }
                    wp_send_json_error([
                        'message' => $message,
                        'check'   => 'uuid',
                        'raw'     => $response,
                        'code'    => $preconditionError['code'],
                    ]);
                }

                return;
            }

            if ('credit_note' === $type && $order->get_parent_id()) {
                $parent = OrderQuery::instance()->getProviderOrder((int)$order->get_parent_id(), $provider);
                if ($parent) {
                    $parent->update_meta($metaKey, $responseUuid);
                }
            } else {
                $order->update_meta($metaKey, $responseUuid);
            }

            if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($responseUuid)) {
                self::markDocumentAsSent($order);
                self::clearNativeTimeoutPending($order);
                \WcElectronInvoice\Functions\clearPopApiOverquotaState($order);
            }
        } finally {
            \WcElectronInvoice\Functions\releaseDbLock($lockKey);
        }

        if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
            wp_send_json_success([
                'message'  => $response->message,
                'provider' => $provider,
                'order_id' => $id,
                'uuid'     => $responseUuid,
                'xml'      => $response->xml_string,
            ]);

            return;
        }

        // Notify
        if ($uuid = $responseUuid) {
            $time          = self::jobTiming('notify');
            $nextScheduled = wp_next_scheduled(
                'PoPToSDIJobsScheduledNotify',
                [$order->get_id(), $uuid, $provider]
            );
            if (! $nextScheduled) {
                self::scheduled(
                    'PoPToSDIJobsScheduledNotify',
                    [$order->get_id(), $uuid, $provider],
                    $time
                );
            }
        }

        \WcElectronInvoice\Functions\log($response->message . "\n" . print_r([
                'provider' => $provider,
                'order_id' => $id,
                'uuid'     => property_exists($response->data, 'uuid') ? $response->data->uuid : null,
                'xml'      => $response->xml_string,
            ],
                true) . "\n");

        return $response;
    }

    /**
     * notificationsInvoice
     *
     * The method is called either via Ajax or via Cron by running a request to /document-notifications.
     * if from the request I get notifications with RC or MC code
     * the request for legal preservation /document-preserve is made.
     *
     * If I don't get notifications and the method has been executed by cron it
     * reprograms the request until it gets a notification
     *
     * @return array|void|\WP_Error
     * @throws \Exception
     */
    public static function notificationsInvoice()
    {
        $getSdiPOPIntegrationData = self::getSdiPOPIntegrationData();
        $level                    = \WcElectronInvoice\Wizard::popGetUserLevel();
        $licenseKey               = OptionPage::init()->getOptions('license_key') ?: null;
        $uuid                     = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'uuid') ?? '');
        $provider                 = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST,
            'provider') ?? '');
        $id                       = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? '');
        $action                   = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null;
        $method                   = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null;

        if (! \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'feCronJobUploadRequest'])) {
            if (! check_ajax_referer('wc_el_inv_ajax-ajax_nonce', 'nonce', false)) {
                wp_send_json_error(['message' => esc_html__('Unauthorized.', WC_EL_INV_TEXTDOMAIN)]);
                return;
            }
            if (! current_user_can('manage_woocommerce')) {
                wp_send_json_error(['message' => esc_html__('Forbidden.', WC_EL_INV_TEXTDOMAIN)]);
                return;
            }
        }

        if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') ||
            (! $getSdiPOPIntegrationData->active_plugin_integration ||
             ! $getSdiPOPIntegrationData->active_integration)
        ) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Action not allowed.',
                        WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }

        // I check the action parameter if it does not match I get an error
        if ('notificationsInvoice' !== $action) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Action not valid.',
                        WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }

        $requestedType     = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'document_type') ?? '');
        $context           = self::resolveActionDocumentContext((int)$id, $provider, null, $requestedType);
        $order             = $context['document'];
        if (! $order) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }
        $notificationsKey  = \WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', $context['document_type']);
        $notificationsMeta = $order->get_meta($notificationsKey);

        if ('' === $uuid) {
            \WcElectronInvoice\Functions\log("notificationsInvoice - check uuid: Missing uuid parameter" . "\n");

            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('notificationsInvoice - check uuid: Missing uuid parameter',
                        WC_EL_INV_TEXTDOMAIN),
                    'uuid'    => $uuid,
                ]);
            }

            return;
        }

        if ($uuid && ! empty($notificationsMeta[$uuid])) {
            \WcElectronInvoice\Functions\log("notificationsInvoice - check notifications for uuid: Exist notifications for uuid" . "\n");

            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message'            => esc_html__('notificationsInvoice - check notifications for uuid: Exist notifications for uuid',
                        WC_EL_INV_TEXTDOMAIN),
                    'uuid'               => $uuid,
                    'notifications_uuid' => $notificationsMeta[$uuid],
                ]);
            }

            return;
        }

        /**
         * Notifications request
         */
        $response = wp_remote_post(
            esc_url_raw(WC_EL_INV_API_URL . 'sdi-via-pop/document-notifications'),
            [
                'headers' => [
                    'Content-Type' => 'application/json',
                    'X-API-Key'    => $licenseKey,
                ],
                'body'    => json_encode([
                    'license_key' => "{$licenseKey}",
                    'integration' => [
                        'uuid' => "{$uuid}",
                    ],
                ]),
                'timeout' => 5,
            ]
        );

        $data = [];
        if (is_wp_error($response)) {
            $message = $response->get_error_message();
            $code    = $response->get_error_code();
            error_log('method notificationsInvoice error: ' . $message);
        } else {
            $code    = wp_remote_retrieve_response_code($response);
            $body    = wp_remote_retrieve_body($response);
            $data    = json_decode($body, true);
            $message = $data['message'];
        }

        // response from wp_send_json_error
        if (! $message && ! $data['success']) {
            $message = $data['data'] ?: null;
        }

        // Initialize
        $notifications     = [];
        $notificationsMeta = [];

        // Check response data
        if (is_array($data['data']) && ! empty($data['data'])) {
            if (! empty($data['data']['hydra:member'])) {

                // clear scheduled
                wp_clear_scheduled_hook('PoPToSDIJobsScheduledNotify', [$order->get_id(), $uuid, $provider]);

                $message = __('Notifications', WC_EL_INV_TEXTDOMAIN) . ":\n";
                $text    = "";
                $types   = [];

                // Set all messages to be saved as metadata
                foreach ((array)$data['data']['hydra:member'] as $notification) {
                    $type = $notification['type'] ?: 'NAN';

                    if (! in_array($type, $types)) {
                        if ('RC' === $type) {
                            $text    = __('The invoice was delivered by the Interchange System (SDI).',
                                WC_EL_INV_TEXTDOMAIN);
                            $message .= sprintf("%s", $text) . "\n";
                        } elseif ('MC' === $type) {
                            $text    = __('The invoice delivery by the Interchange System (SDI) has failed.',
                                WC_EL_INV_TEXTDOMAIN);
                            $message .= sprintf("%s", $text) . "\n";
                        } elseif ('NS' === $type) {
                            $text = __('The invoice was rejected by the Interchange System (SDI) which returned a specific error code.',
                                WC_EL_INV_TEXTDOMAIN);

                            $error     = '';
                            $errorList = $notification['message']['lista_errori'] ?? [];
                            if (isset($errorList['Errore']) && is_array($errorList['Errore'])) {
                                $errore    = $errorList['Errore'];
                                $errorList = isset($errore['Codice']) ? [$errore] : $errore;
                            }
                            if (! empty($errorList)) {
                                foreach ((array)$errorList as $errorItem) {
                                    $error .= "\n" . __('Codice: ',
                                            WC_EL_INV_TEXTDOMAIN) . ($errorItem['Codice'] ?? '') .
                                              "\n" . __('Descrizione: ',
                                            WC_EL_INV_TEXTDOMAIN) . ($errorItem['Descrizione'] ?? '') .
                                              "\n" . __('Suggerimento: ',
                                            WC_EL_INV_TEXTDOMAIN) . ($errorItem['Suggerimento'] ?? '') . "\n";
                                }
                            }

                            $message .= sprintf("%s %s", $text, $error) . "\n";
                        } elseif ('AT' === $type) {
                            $text    = __('The invoice delivery by the Exchange System (SDI) was not successful. No further delivery attempts will be made.',
                                WC_EL_INV_TEXTDOMAIN);
                            $message .= sprintf("%s", $text) . "\n";
                        } elseif ('DT' === $type) {
                            $text    = __('The invoice was delivered by the Interchange System (SDI) but the customer did not send any response within 15 days of delivery.',
                                WC_EL_INV_TEXTDOMAIN);
                            $message .= sprintf("%s", $text) . "\n";
                        } elseif ('NE' === $type) {
                            if ('EC01' === $notification['message']['esito_committente']['esito']) {
                                $text    = __('The customer has accepted the invoice delivered by the Interchange System (SDI).',
                                    WC_EL_INV_TEXTDOMAIN);
                                $message .= sprintf("%s", $text) . "\n";
                            } elseif ('EC02' === $notification['message']['esito_committente']['esito']) {
                                $text    = __('The customer refused the invoice delivered by the Interchange System (SDI).',
                                    WC_EL_INV_TEXTDOMAIN);
                                $message .= sprintf("%s", $text) . "\n";
                            }
                        }

                        $notificationsMeta[$notification['uuid']] = [
                            'type'    => $notification['type'],
                            'message' => $text,
                        ];
                    }

                    $types[] = $type;
                }

                if (in_array('NS', $types)) {
                    // Log
                    $logMessage = wp_strip_all_tags($message);
                    \WcElectronInvoice\Functions\log($logMessage);

                    $subject   = sprintf(esc_html__('POP - SDI Reject Notification – Order ID %s',
                        WC_EL_INV_TEXTDOMAIN), $order->get_id());
                    $emailBody = sprintf(esc_html__('Hi,
					The invoice for the order with ID %s has received a rejection notification from the SDI.
					We recommend that you access the management area to check the rejection details and, if necessary, proceed with the correction and resending of the document.',
                            WC_EL_INV_TEXTDOMAIN) . "\n-----------------\n" . esc_html($logMessage), $order->get_id());

                    sendEmailToAdminForError($subject, $emailBody);
                }

                /**
                 * Preserve document request
                 */
                $getSdiPOPIntegrationData = InvoiceApi::getSdiPOPIntegrationData();
                if ($getSdiPOPIntegrationData->active_storage) {
                    if (in_array('RC', $types) || in_array('MC', $types)) {
                        $invoiceNumber = $order->get_order_number_invoice();
                        $preserve      = wp_remote_post(
                            esc_url_raw(WC_EL_INV_API_URL . 'sdi-via-pop/document-preserve'),
                            [
                                'headers' => [
                                    'Content-Type' => 'application/json',
                                    'X-API-Key'    => $licenseKey,
                                ],
                                'body'    => json_encode([
                                    'license_key'    => "{$licenseKey}",
                                    'invoice_number' => "{$invoiceNumber}",
                                    'integration'    => [
                                        'uuid' => "{$uuid}",
                                    ],
                                ]),
                                'timeout' => 15,
                            ]
                        );
                        if (is_wp_error($preserve)) {
                            $message = $preserve->get_error_message();
                            $code    = $preserve->get_error_code();
                            error_log('preserve request error: ' . $message);
                        } else {
                            $code            = wp_remote_retrieve_response_code($preserve);
                            $preserveBody    = wp_remote_retrieve_body($preserve);
                            $preserveData    = json_decode($preserveBody, true);
                            $preserveMessage = $preserveData['message'];

                            if (! empty($preserveData)) {
                                // Append message for toast
                                $message .= sprintf("%s", $preserveMessage) . "\n";

                                if (200 !== $code) {
                                    $icon = '<span class="dashicons dashicons-warning"></span>';
                                } else {
                                    $icon = '<span class="dashicons dashicons-archive"></span>';
                                }

                                $notificationsMeta[$uuid] = [
                                    'type'    => $icon,
                                    'message' => $preserveMessage,
                                    'code'    => $code,
                                ];

                                $notifications[$uuid] = [
                                    'type'    => 'PRESERVE',
                                    'message' => $preserveMessage,
                                    'code'    => $code,
                                ];
                            } else {
                                $notificationsMeta[$uuid] = [
                                    'type'    => '<span class="dashicons dashicons-warning"></span>',
                                    'message' => null,
                                    'code'    => $code,
                                ];

                                $notifications[$uuid] = [
                                    'type'    => 'PRESERVE',
                                    'message' => null,
                                    'code'    => $code,
                                ];
                            }
                        }
                    }
                }

                // I set all messages to show in toast
                foreach ((array)$data['data']['hydra:member'] as $notification) {
                    $notifications[$notification['uuid']] = [
                        'type'    => $notification['type'],
                        'message' => $message,
                    ];
                }
            } else {
                $notificationsMeta['NAN'] = [
                    'type'    => null,
                    'message' => $data['message'],
                ];

                if (\WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob'])) {
                    // If no notification is present, I reschedule the cron for the request
                    $time          = self::jobTiming('notify');
                    $nextScheduled = wp_next_scheduled(
                        'PoPToSDIJobsScheduledNotify',
                        [$order->get_id(), $uuid, $provider]
                    );
                    if (! $nextScheduled) {
                        self::scheduled(
                            'PoPToSDIJobsScheduledNotify',
                            [$order->get_id(), $uuid, $provider],
                            $time
                        );
                    }
                }
            }
        }

        if (! empty($notificationsMeta)) {
            $order->update_meta($notificationsKey, $notificationsMeta);
        }

        if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
            wp_send_json_success([
                'message'           => $message,
                'notificationsMeta' => $notificationsMeta,
                'notifications'     => $notifications,
                'data'              => $data,
                'uuid'              => $uuid,
                'code'              => $code,
            ]);

            return;
        }

        \WcElectronInvoice\Functions\log($message . "\n" . print_r([
                'message'           => $message,
                'notificationsMeta' => $notificationsMeta,
                'notifications'     => $notifications,
                'data'              => $data,
                'uuid'              => $uuid,
                'code'              => $code,
            ],
                true) . "\n");

        return $response;
    }

    /**
     * preserveInvoice
     *
     * @return array|void|\WP_Error|null
     * @throws \Exception
     */
    public static function preserveInvoice()
    {
        $getSdiPOPIntegrationData = self::getSdiPOPIntegrationData();
        $level                    = \WcElectronInvoice\Wizard::popGetUserLevel();
        $licenseKey               = OptionPage::init()->getOptions('license_key') ?: null;
        $uuid                     = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'uuid') ?? '');
        $provider                 = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST,
            'provider') ?? '');
        $id                       = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? '');
        $action                   = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null;
        $method                   = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null;

        if (! \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'feCronJobUploadRequest'])) {
            if (! check_ajax_referer('wc_el_inv_ajax-ajax_nonce', 'nonce', false)) {
                wp_send_json_error(['message' => esc_html__('Unauthorized.', WC_EL_INV_TEXTDOMAIN)]);
                return;
            }
            if (! current_user_can('manage_woocommerce')) {
                wp_send_json_error(['message' => esc_html__('Forbidden.', WC_EL_INV_TEXTDOMAIN)]);
                return;
            }
        }

        if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') ||
            (! $getSdiPOPIntegrationData->active_plugin_integration ||
             ! $getSdiPOPIntegrationData->active_integration)
        ) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Action not allowed.',
                        WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }

        // I check the action parameter if it does not match I get an error
        if ('preserveInvoice' !== $action) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Action not valid.',
                        WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }

        $requestedType = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'document_type') ?? '');
        $context       = self::resolveActionDocumentContext((int)$id, $provider, null, $requestedType);
        $order         = $context['document'];
        if (! $order) {
            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN),
                ]);
            }

            return;
        }
        $notificationsKey = \WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', $context['document_type']);

        if ('' === $uuid) {
            \WcElectronInvoice\Functions\log("check uuid > Missing uuid parameter" . "\n");

            if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
                wp_send_json_error([
                    'message' => esc_html__('Missing uuid parameter', WC_EL_INV_TEXTDOMAIN),
                    'uuid'    => $uuid,
                ]);
            }

            return;
        }

        // Initialize
        $notifications     = [];
        $notificationsMeta = [];
        $preserve          = null;
        $message           = null;
        $code              = null;
        $preserveData      = null;

        /**
         * Preserve document request
         */
        if ($getSdiPOPIntegrationData->active_storage) {
            $notificationsData = $order->get_meta($notificationsKey) ?: null;

            if (! empty($notificationsData)) {
                foreach ($notificationsData as $notification) {
                    if ('RC' === $notification['type'] || 'MC' === $notification['type']) {
                        $invoiceNumber = $order->get_order_number_invoice();
                        $preserve      = wp_remote_post(
                            esc_url_raw(WC_EL_INV_API_URL . 'sdi-via-pop/document-preserve'),
                            [
                                'headers' => [
                                    'Content-Type' => 'application/json',
                                    'X-API-Key'    => $licenseKey,
                                ],
                                'body'    => json_encode([
                                    'license_key'    => "{$licenseKey}",
                                    'invoice_number' => "{$invoiceNumber}",
                                    'integration'    => [
                                        'uuid' => "{$uuid}",
                                    ],
                                ]),
                                'timeout' => 15,
                            ]
                        );
                        if (is_wp_error($preserve)) {
                            $message = $preserve->get_error_message();
                            $code    = $preserve->get_error_code();
                            error_log('preserve request error: ' . $message);
                        } else {
                            $code         = wp_remote_retrieve_response_code($preserve);
                            $preserveBody = wp_remote_retrieve_body($preserve);
                            $preserveData = json_decode($preserveBody, true);
                            $message      = $preserveData['message'];

                            if (200 !== $code) {
                                $icon = '<span class="dashicons dashicons-warning"></span>';
                            } else {
                                $icon = '<span class="dashicons dashicons-archive"></span>';
                            }

                            if (! empty($preserveData)) {
                                $notificationsMeta[$uuid] = [
                                    'type'    => $icon,
                                    'message' => $message,
                                    'code'    => $code,
                                ];

                                $notifications[$uuid] = [
                                    'type'    => 'PRESERVE',
                                    'message' => $message,
                                    'code'    => $code,
                                ];
                            } else {
                                $notificationsMeta[$uuid] = [
                                    'type'    => $icon,
                                    'message' => null,
                                    'code'    => $code,
                                ];

                                $notifications[$uuid] = [
                                    'type'    => 'PRESERVE',
                                    'message' => null,
                                    'code'    => $code,
                                ];
                            }
                        }
                    }
                }
            }
        }

        // Update notifications meta data
        $notificationsStored = $order->get_meta($notificationsKey);
        if (! empty($notificationsMeta)) {
            $notificationsMeta = array_merge($notificationsStored, $notificationsMeta);
            $order->update_meta($notificationsKey, $notificationsMeta);
        }

        if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) {
            wp_send_json_success([
                'message'           => $message,
                'notificationsMeta' => $notificationsMeta,
                'notifications'     => $notifications,
                'data'              => $preserveData,
                'uuid'              => $uuid,
                'code'              => $code,
            ]);

            return;
        }

        \WcElectronInvoice\Functions\log($message . "\n" . print_r([
                'message'           => $message,
                'notificationsMeta' => $notificationsMeta,
                'notifications'     => $notifications,
                'data'              => $preserveData,
                'uuid'              => $uuid,
                'code'              => $code,
            ],
                true) . "\n");

        return $preserve;
    }

    /**
     * Confirm native (SDI/PEPPOL) timeout-pending send by manual UUID input.
     *
     * @return void
     */
    public static function confirmNativeSendWithUuid()
    {
        $provider = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'provider') ?? '');
        $id       = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? '');
        $channel  = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'channel') ?? '');
        $requestedType = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'document_type') ?? '');
        $uuid     = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'uuid') ?? '');
        $action   = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null;

        if (! check_ajax_referer('wc_el_inv_ajax-ajax_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => esc_html__('Unauthorized.', WC_EL_INV_TEXTDOMAIN)]);
            return;
        }
        if (! current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => esc_html__('Forbidden.', WC_EL_INV_TEXTDOMAIN)]);
            return;
        }

        if ('confirmNativeSendWithUuid' !== $action) {
            wp_send_json_error([
                'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        if (! in_array($channel, ['sdi', 'peppol', 'ksef'], true)) {
            wp_send_json_error([
                'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        if (! \WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($uuid)) {
            wp_send_json_error([
                'message' => esc_html__('UUID is required.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        $context = self::resolveActionDocumentContext((int)$id, $provider, null, $requestedType);
        $order   = $context['document'];
        if (! $order) {
            wp_send_json_error([
                'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        $pending        = \WcElectronInvoice\Functions\normalizeBooleanMeta(
            $order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey())
        );
        $pendingChannel = (string)$order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey());
        if (! $pending || $pendingChannel !== $channel) {
            wp_send_json_error([
                'message' => esc_html__('Timeout verification is not pending for this channel.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        $documentType  = $context['document_type'];
        $channelPrefix = \WcElectronInvoice\Functions\resolveNativeChannelPrefix($channel);
        if ('' === $channelPrefix) {
            wp_send_json_error([
                'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        $metaKey = \WcElectronInvoice\Functions\buildUuidMetaKey($channelPrefix, $documentType);
        $order->update_meta($metaKey, $uuid);
        self::markDocumentAsSent($order);
        self::clearNativeTimeoutPending($order);
        $order->save();

        wp_send_json_success([
            'message' => esc_html__('UUID saved and invoice marked as sent.', WC_EL_INV_TEXTDOMAIN),
            'uuid'    => $uuid,
            'channel' => $channel,
        ]);
    }

    /**
     * Cancel native timeout pending state and re-enable send action.
     *
     * @return void
     */
    public static function cancelNativeTimeoutPending()
    {
        $provider = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'provider') ?? '');
        $id       = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? '');
        $channel  = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'channel') ?? '');
        $requestedType = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'document_type') ?? '');
        $action   = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null;

        if (! check_ajax_referer('wc_el_inv_ajax-ajax_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => esc_html__('Unauthorized.', WC_EL_INV_TEXTDOMAIN)]);
            return;
        }
        if (! current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => esc_html__('Forbidden.', WC_EL_INV_TEXTDOMAIN)]);
            return;
        }

        if ('cancelNativeTimeoutPending' !== $action) {
            wp_send_json_error([
                'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        if (! in_array($channel, ['sdi', 'peppol', 'ksef', 'zoho'], true)) {
            wp_send_json_error([
                'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        $context = self::resolveActionDocumentContext((int)$id, $provider, null, $requestedType);
        $order   = $context['document'];
        if (! $order) {
            wp_send_json_error([
                'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        $pending        = \WcElectronInvoice\Functions\normalizeBooleanMeta(
            $order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey())
        );
        $pendingChannel = (string)$order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey());
        if (! $pending || $pendingChannel !== $channel) {
            wp_send_json_error([
                'message' => esc_html__('Timeout verification is not pending for this channel.', WC_EL_INV_TEXTDOMAIN),
            ]);

            return;
        }

        self::clearNativeTimeoutPending($order);
        $order->save();

        wp_send_json_success([
            'message' => esc_html__('Timeout status removed. Send action is enabled again.', WC_EL_INV_TEXTDOMAIN),
            'channel' => $channel,
        ]);
    }

    /**
     * Store timeout pending verification metadata on order.
     *
     * @param mixed  $order
     * @param string $channel
     * @param string $documentType
     *
     * @return void
     */
    public static function markNativeTimeoutPending($order, string $channel, string $documentType = 'invoice'): void
    {
        if (! is_object($order) || ! method_exists($order, 'update_meta')) {
            return;
        }

        $channel = strtolower(trim($channel));
        if (! in_array($channel, ['sdi', 'peppol', 'ksef', 'zoho'], true)) {
            return;
        }

        $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey(), 'yes');
        $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey(), $channel);
        $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey(), $documentType);
        $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingTimestampMetaKey(), time());
        $order->save();
    }

    /**
     * Clear timeout pending verification metadata on order.
     *
     * @param mixed $order
     *
     * @return void
     */
    public static function clearNativeTimeoutPending($order): void
    {
        if (! is_object($order) || ! method_exists($order, 'delete_meta')) {
            return;
        }

        $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey());
        $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey());
        $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey());
        $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingTimestampMetaKey());
        $order->save();
    }
}
