<?php
/**
 * Api.php
 *
 * @author     alfiopiccione <alfio.piccione@gmail.com>
 * @license    http://opensource.org/licenses/gpl-2.0.php GPL v2
 *
 * Copyright (C) 2025 POP
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

namespace WooPoPToFattureInCloud\Functions;

use FattureInCloud\Api\CompaniesApi;
use FattureInCloud\ApiException;
use FattureInCloud\Model\CreateClientRequest;
use FattureInCloud\Model\CreateIssuedDocumentRequest;
use FattureInCloud\Model\VatType;
use GuzzleHttp\Exception\GuzzleException;
use WcElectronInvoice\Admin\Settings\OptionPage;
use WcElectronInvoice\Integrations;
use WcElectronInvoice\WooCommerce\Fields\GeneralFields;
use WcElectronInvoice\WooCommerce\Fields\TaxFields;
use WcElectronInvoice\Xml\CreateXml;
use WooPoPToFattureInCloud\Api;
use WooPoPToFattureInCloud\TimeZone;
use function WcElectronInvoice\Functions\getPostMeta;
use function WcElectronInvoice\Functions\getXmlFromOrder;
use function WcElectronInvoice\Functions\isXml;

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

/**
 * getDateTime
 *
 * @return string
 */
function getDateTime()
{
    return date_i18n('d/m/Y H:i');
}

/**
 * Reset login data
 * Used for debug
 *
 * @param $delete
 *
 * @return void
 */
function resetApi($delete = false)
{
    $reset = filterInput($_GET, 'wfc_reset_login', FILTER_VALIDATE_INT);
    if (intval($reset) === 1 || $delete) {
        // Delete API data
        delete_option('wfc_api_url');
        delete_option('wfc_api_uid');
        delete_option('wfc_api_key');
        delete_option('wfc_oauth_state');
        delete_option('wfc_oauth_code');
        delete_option('wfc_store_login');
        delete_option('wfc_access_token');
        delete_option('wfc_refresh_token');
        // Delete user info and companies
        delete_option('wfc_user_info');
        delete_option('wfc_company_data');
        delete_option('wfc_payment_accounts');
        delete_option('wfc_payment_methods');
        // Delete vat type data
        delete_option('wfc_vat_types');

        if (intval($reset) === 1) {
            wp_redirect(admin_url('admin.php?page=wc_el_inv-options-page&tab=addon'));
            die();
        }
    }
}

/**
 * Get Active api
 *
 * @return object
 */
function getActiveApi()
{
    $loginStored       = get_option('wfc_store_login');
    $apiUrl            = get_option('wfc_api_url');
    $apiUid            = get_option('wfc_api_uid');
    $apiKey            = get_option('wfc_api_key');
    $oauthState        = get_option('wfc_oauth_state');
    $oauthCode         = get_option('wfc_oauth_code');
    $accessToken       = get_option('wfc_access_token');
    $refreshToken      = get_option('wfc_refresh_token');
    $expireAccessToken = get_option('wfc_access_token_expires_in') ?: null;

    if ('oauth_device' === Api::$oauthFlow) {
        $apiUid = '';
        $apiKey = '';
    }

    return (object)array(
        'active'            => $loginStored,
        'apiUrl'            => $apiUrl,
        'apiUid'            => $apiUid,
        'apiKey'            => $apiKey,
        'oauthState'        => $oauthState,
        'oauthCode'         => $oauthCode,
        'accessToken'       => $accessToken,
        'refreshToken'      => $refreshToken,
        'expireAccessToken' => $expireAccessToken,
    );
}

/**
 * getMaskedApi
 *
 * Returns a copy of the object or array with sensitive fields obscured.
 *
 * @param object|array $data       Object or array to mask.
 * @param array        $keysToMask Keys to be obscured.
 *
 * @return object|array Copy with masked data.
 */
function getMaskedApi($data, array $keysToMask = ['accessToken', 'refreshToken'])
{
    if (is_object($data)) {
        $masked = clone $data;
        foreach ($keysToMask as $key) {
            if (isset($masked->$key)) {
                $masked->$key = '***';
            }
        }

        return $masked;
    } elseif (is_array($data)) {
        $masked = $data;
        foreach ($keysToMask as $key) {
            if (isset($masked[$key])) {
                $masked[$key] = '***';
            }
        }

        return $masked;
    }

    return $data;
}

/**
 * Set access token, refresh and expires_in
 *
 * @param $data
 *
 * @return bool|void
 */
function updateTokenData($data)
{
    // Store access_token and refresh_token
    update_option('wfc_access_token', "{$data->access_token}");
    update_option('wfc_refresh_token', "{$data->refresh_token}");

    try {
        $timeZone = new TimeZone();
        $timeZone = new \DateTimeZone($timeZone->getTimeZone()->getName());
        $time     = new \DateTime('now');
        $time->setTimezone($timeZone);
        $time->modify('+ 1 day');

        return update_option(
            'wfc_access_token_expires_in',
            $time->getTimestamp()
        );
    } catch (\Exception $e) {
        $dataException = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => esc_html('Exception DateTime: ' . $e->getMessage() . ' Trace: ' . $e->getTraceAsString()),
            'code'    => $e->getCode(),
        );

        wp_send_json($dataException);
        die();
    }
}

/**
 * Get User And Companies data
 *
 * @return array|object
 */
function getUserAndCompanies()
{
    $data = array();
    // User info and Companies
    $userInfo = Api::getUserAndCompanyData();

    if ($userInfo) {
        $data = (object)array(
            'userInfo'           => $userInfo->user,
            'companiesData'      => $userInfo->companies,
            'companyID'          => get_option('wfc_company_id'),
            'companyPermissions' => get_option('wfc_company_details_permissions'),
        );
    }

    return $data;
}

/**
 * OAuth Flow
 *
 * @return void
 */
function OAuthFlow()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $apiUrl          = filterInput($_POST, 'apiUrl', FILTER_UNSAFE_RAW);
    $apiUid          = filterInput($_POST, 'apiUid', FILTER_UNSAFE_RAW);
    $apiKey          = filterInput($_POST, 'apiKey', FILTER_UNSAFE_RAW);
    $permissionInv   = filterInput($_POST, 'permissionInv', FILTER_VALIDATE_INT);
    $permissionOrd   = filterInput($_POST, 'permissionOrd', FILTER_VALIDATE_INT);
    $permissionCre   = filterInput($_POST, 'permissionCre', FILTER_VALIDATE_INT);
    $permissionRec   = filterInput($_POST, 'permissionRec', FILTER_VALIDATE_INT);
    $permissionCli   = filterInput($_POST, 'permissionCli', FILTER_VALIDATE_INT);
    $permissionStock = filterInput($_POST, 'permissionStock', FILTER_VALIDATE_INT);
    $nonce           = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    if ('oauth_token' === Api::$oauthFlow) {
        if (! $apiUid && ! $apiKey) {
            $response = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html__('OAuth Request error: Invalid params', WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 501,
            );

            wp_send_json($response);
            die();
        }
    }

    // All default scopes
    // Inv and Cre Always active
    if ($permissionInv && $permissionCre && $permissionRec && $permissionCli) {

        // Default scopes
        // scopes "situation:r entity.clients:a issued_documents.invoices:a issued_documents.receipts:a issued_documents.credit_notes:a settings:a"
        $scopes = apply_filters('wfc-oauth_flow_access_permission', Api::$accessScopes);
    } else {

        // Order or Invoice scope
        if (1 === $permissionOrd) {
            $scopes = "issued_documents.orders:a settings:a situation:r";
        } else {
            $scopes = "issued_documents.invoices:a settings:a situation:r";
        }

        // Credit note scope
        if (1 === $permissionCre) {
            $scopes = "issued_documents.credit_notes:a " . $scopes;
        }

        // Receipts scope
        if (1 === $permissionRec) {
            $scopes = "issued_documents.receipts:a " . $scopes;
        }

        // Clients scope
        if (1 === $permissionCli) {
            $scopes = "entity.clients:a " . $scopes;
        }
    }

    // Stock scope - manage addon (Write)
    if (1 === $permissionStock) {
        $scopes = "stock:a products:a " . $scopes;
    }

    /**
     * Filter scopes
     */
    $scopes = apply_filters('wfc-oauth_flow_after_defined_access_permission', $scopes);

    // Store login check
    update_option('wfc_store_login', true);
    // Store api, user and pass
    update_option('wfc_api_url', "{$apiUrl}");
    update_option('wfc_api_uid', "{$apiUid}");
    update_option('wfc_api_key', "{$apiKey}");
    // Store api scopes
    update_option('wfc_api_scopes', "{$scopes}");

    $authorizeUrl = \WooPoPToFattureInCloud\Api::$routes['oauth_authorize'];
    $session      = wp_get_session_token();
    $siteUrl      = home_url();

    if ('oauth_token' === Api::$oauthFlow) {
        $params = array(
            'response_type' => 'code',
            'client_id'     => $apiUid,
            'redirect_uri'  => $siteUrl,
            'scope'         => apply_filters('wfc-access_scopes', $scopes),
            'state'         => $session,
        );
        $query  = http_build_query($params);
        \WooPoPToFattureInCloud\Api::setApiBaseUri(getActiveApi()->apiUrl);
        $apiUrl    = Api::getBaseUri();
        $oauthLink = "{$apiUrl}{$authorizeUrl}?" . $query;

        $oauthParamsResponse = array(
            'title'   => '',
            'message' => sprintf('<strong>%s</strong> <a href="%s">%s</a> <p>%s</p>',
                esc_html__('Request the Authorization Code:', WP_FATT_CLOUD_TEXTDOMAIN),
                $oauthLink,
                esc_html__('click here', WP_FATT_CLOUD_TEXTDOMAIN),
                esc_html__('You will ask for permission to Create, delete and send invoices.',
                    WP_FATT_CLOUD_TEXTDOMAIN)
            ),
            'code'    => 200,
            'scopes'  => $scopes,
        );

        wp_send_json($oauthParamsResponse);
        die();
    } elseif ('oauth_device' === Api::$oauthFlow) {
        Api::setApiBaseUri(getActiveApi()->apiUrl);
        $apiUrl = Api::getBaseUri();
        $uri    = $apiUrl . \WooPoPToFattureInCloud\Api::$routes['oauth_device'];
        // Client
        $client = new \GuzzleHttp\Client(['base_uri' => $uri]);
        try {
            $response = $client->post($uri, [
                \GuzzleHttp\RequestOptions::FORM_PARAMS => [
                    'client_id' => Api::getClientID(),
                    'scope'     => $scopes,
                ],
            ]);

            $data    = json_decode($response->getBody(), true);
            $dataObj = (object)$data['data'];

            if ($dataObj && '' !== $dataObj->device_code) {
                $oauthParamsResponse = array(
                    'title'        => '',
                    'message'      => sprintf(
                        '<strong>%s</strong> <p><a onclick="window.open(this.href, \'oauth_win\', \'left=20,top=20,width=700,height=700,toolbar=1,resizable=0\'); return false;" id="api_oauth_device" target="_blank" href="%s">%s</a>, %s: <div class="user_code_wrapper"><input id="user_code" disabled type="text" value="%s"><button class="button" id="copy_trigger">%s</button></div></p>',
                        esc_html__('Request the Authorization Code:', WP_FATT_CLOUD_TEXTDOMAIN),
                        $dataObj->verification_uri,
                        esc_html__('click here', WP_FATT_CLOUD_TEXTDOMAIN),
                        esc_html__('A page will open where you are asked to enter this code', WP_FATT_CLOUD_TEXTDOMAIN),
                        $dataObj->user_code,
                        esc_html__('Copy code', WP_FATT_CLOUD_TEXTDOMAIN)
                    ),
                    'code'         => 200,
                    'oauth_device' => $dataObj->device_code,
                    'interval'     => $dataObj->interval,
                    'scopes'       => $scopes,
                );

                wp_send_json($oauthParamsResponse);
                die();
            }
        } catch (\Exception $e) {
            $dataException = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html('Exception when calling OAuth: ' . $e->getMessage() . ' Trace: ' . $e->getTraceAsString()),
                'code'    => $e->getCode(),
            );

            wp_send_json($dataException);
            die();
        }
    }
}

/**
 * settings Company
 *
 * @return void
 */
function settingsCompany($initSettings = false)
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $companyID = filterInput($_POST, 'companyID', FILTER_VALIDATE_INT);
    $nonce     = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    if (! $companyID || ! is_int($companyID)) {
        $response = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => esc_html__('Invalid Settings Company ID request', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => 501,
        );

        wp_send_json($response);
        die();
    }

    $apiData     = getActiveApi();
    $config      = \FattureInCloud\Configuration::getDefaultConfiguration()->setAccessToken($apiData->accessToken);
    $companyInfo = new CompaniesApi(
        new \GuzzleHttp\Client(),
        $config
    );

    // Store company_id
    update_option('wfc_company_id', $companyID);

    // Translate
    $none = __('none', WP_FATT_CLOUD_TEXTDOMAIN);
    // Use for translation
    $write = __('write', WP_FATT_CLOUD_TEXTDOMAIN);

    // Default
    $defaultPermissions = array(
        'clients'         => $none,
        'issued_receipts' => $none,
        'receipts'        => $none,
        'stock'           => $none,
        'invoices'        => $none,
        'creditNote'      => $none,
    );

    try {
        $CompanyInfo        = $companyInfo->getCompanyInfo($companyID);
        $invoicesPermission = $creditNotePermission = $ordersPermission = 'none';

        $clientsPermission                = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicClients();
        $receiptsIssuedDocumentPermission = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicIssuedDocumentsDetailed()->getReceipts();
        $receiptsPermission               = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicReceipts();
        $stockPermission                  = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicStock();

        if ('none' !== $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicIssuedDocuments()) {
            $invoicesPermission   = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicIssuedDocumentsDetailed()->getInvoices();
            $ordersPermission     = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicIssuedDocumentsDetailed()->getOrders();
            $creditNotePermission = $CompanyInfo->getData()->getAccessInfo()->getPermissions()->getFicIssuedDocumentsDetailed()->getCreditNotes();
        }

        $companyInfoDetailPermissions = wp_parse_args(array(
            'clients'         => $clientsPermission,
            'issued_receipts' => $receiptsIssuedDocumentPermission,
            'receipts'        => $receiptsPermission,
            'stock'           => $stockPermission,
            'invoices'        => $invoicesPermission,
            'orders'          => $ordersPermission,
            'creditNote'      => $creditNotePermission,
        ), $defaultPermissions);

        // InfoApi
        $InfoApi = Api::fattureInCloudInfoApi();

        /**
         * listPaymentMethods
         */
        Api::setDefaultPaymentMethods($InfoApi, $companyID);

        /**
         * listPaymentAccounts
         */
        Api::setDefaultPaymentAccounts($InfoApi, $companyID);

        /**
         * Update listVatTypes
         */
        Api::setVatTypes(true);
    } catch (ApiException $e) {
        if (401 === $e->getCode()) {
            $dataException = array(
                'title'     => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'e-message' => $e->getMessage(),
                'code'      => $e->getCode(),
            );
        } else {

            $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

            $dataException = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'type'    => __('Exception when calling CompaniesApi->getCompanyInfo:', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => $message,
                'body'    => $e->getResponseBody(),
                'trace'   => $e->getTraceAsString(),
                'code'    => $e->getCode(),
            );
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }

    // Store company permissions
    update_option('wfc_company_details_permissions', $companyInfoDetailPermissions);

    $companyIDResponse = array(
        'title'                        => '',
        'message'                      => esc_html__('Company id set successfully! ', WP_FATT_CLOUD_TEXTDOMAIN),
        'companyInfoDetailPermissions' => $companyInfoDetailPermissions,
        'code'                         => 200,
    );

    if (! $initSettings) {
        wp_send_json($companyIDResponse);
        die();
    }
}

/**
 * settings Payment Account
 *
 * @return void
 */
function settingsPaymentAccount()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $accountID   = filterInput($_POST, 'accountID', FILTER_VALIDATE_INT);
    $nonce       = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);
    $payAccounts = Api::getPaymentAccounts();

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    if (! $accountID || ! is_int($accountID)) {
        $response = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => esc_html__('Invalid Settings Payment account request', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => 501,
        );

        wp_send_json($response);
        die();
    }

    if (is_array($payAccounts) && ! empty($payAccounts) && count($payAccounts) >= 1) {
        $payAccountSettings = array();
        foreach ($payAccounts as $key => $payAccount) {
            if ($payAccount['id'] === $accountID) {
                $payAccountSettings[$key] = $payAccount;
                // Store payment account
                update_option('wfc_payment_account_active', $payAccountSettings);
                break;
            }
        }
    }

    if (! empty($payAccountSettings)) {
        $paymentAccountResponse = array(
            'title'   => '',
            'message' => esc_html__('Payment account set successfully! ', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => 200,
        );
    } else {
        $paymentAccountResponse = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => esc_html__('Payment account error', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => 501,
        );
    }

    wp_send_json($paymentAccountResponse);
    die();
}

/**
 * settingsMappingPayment
 *
 * @return void
 */
function settingsMappingPayment()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $mappingData = filterInput($_POST, 'mappingData', FILTER_UNSAFE_RAW);
    $idSelectKey = filterInput($_POST, 'idSelectKey', FILTER_UNSAFE_RAW);
    $nonce       = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    if (! $mappingData) {
        $response = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => esc_html__('Invalid Settings for Mapping Payment account whit method',
                WP_FATT_CLOUD_TEXTDOMAIN),
            'data'    => null,
            'code'    => 501,
        );

        wp_send_json($response);
        die();
    }

    $mappingDataArray = explode('__', $mappingData);
    $payMethodID      = $mappingDataArray[0] ?? null;
    $payAccountID     = $mappingDataArray[1] ?? null;

    $data = array();
    if ($payMethodID && $payAccountID) {
        $data = array(
            'mappingData'  => $mappingData,
            'payMethodID'  => $payMethodID,
            'payAccountID' => $payAccountID,
        );

        update_option($idSelectKey, $data);
    }

    $paymentAccountResponse = array(
        'title'   => '',
        'message' => esc_html__('Mapping Payment account whit method set successfully!', WP_FATT_CLOUD_TEXTDOMAIN),
        'data'    => $data,
        'code'    => 200,
    );

    wp_send_json($paymentAccountResponse);
    die();
}

/**
 * settingsMappingGateway
 *
 * @return void
 */
function settingsMappingGateway()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $mappingData = filterInput($_POST, 'mappingData', FILTER_UNSAFE_RAW);
    $idSelectKey = filterInput($_POST, 'idSelectKey', FILTER_UNSAFE_RAW);
    $nonce       = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    $data = array();
    if (! $mappingData) {

        update_option($idSelectKey, '');
    } else {
        $mappingDataArray = explode('__', $mappingData);
        $idSelectKeyArray = explode('__', $idSelectKey);

        $payGatewayCode = $mappingDataArray[0] ?? null;
        $payMethodID    = $idSelectKeyArray[1] ?? null;
        if ($payMethodID && $payGatewayCode) {
            $data = array(
                'payMethodID'    => $payMethodID,
                'payGatewayCode' => $payGatewayCode,
            );

            update_option($idSelectKey, $data);
        }
    }

    $paymentAccountResponse = array(
        'title'   => '',
        'message' => esc_html__('Mapping Payment Gateway whit method ID set successfully!', WP_FATT_CLOUD_TEXTDOMAIN),
        'data'    => $data,
        'code'    => 200,
    );

    wp_send_json($paymentAccountResponse);
    die();
}

/**
 * OAuth Exchange
 *
 * @return void
 */
function oAuthExchange()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $ajaxAction = filterInput($_POST, 'ajaxAction', FILTER_UNSAFE_RAW);

    // oauth_device flow
    $deviceCode = filterInput($_POST, 'device_code', FILTER_UNSAFE_RAW);
    $grantType  = filterInput($_POST, 'grant_type', FILTER_UNSAFE_RAW);

    // oauth_token flow
    $code   = filterInput($_POST, 'code', FILTER_UNSAFE_RAW);
    $cid    = filterInput($_POST, 'cid', FILTER_UNSAFE_RAW);
    $secret = filterInput($_POST, 'secret', FILTER_UNSAFE_RAW);
    $nonce  = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    if ('oauth_token' === Api::$oauthFlow || 'fcOAuthDeviceRequest' !== $ajaxAction) {
        if (! $cid && ! $secret && ! $code) {
            $response = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html__('Invalid OAuth Exchange request', WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 501,
            );

            wp_send_json($response);
            die();
        }
    }

    // Set api base uri
    \WooPoPToFattureInCloud\Api::setApiBaseUri(getActiveApi()->apiUrl);

    if ('oauth_token' === Api::$oauthFlow || 'fcOAuthDeviceRequest' !== $ajaxAction) {
        try {
            $baseUri = Api::getBaseUri();
            $uri     = $baseUri . \WooPoPToFattureInCloud\Api::$routes['oauth_token'];

            // Client
            $client = new \GuzzleHttp\Client(['base_uri' => $uri]);
            // Request authorization_code
            $response = $client->post($uri, [
                \GuzzleHttp\RequestOptions::FORM_PARAMS => [
                    'grant_type'    => 'authorization_code',
                    'client_id'     => $cid,
                    'client_secret' => $secret,
                    'redirect_uri'  => home_url(),
                    'code'          => $code,
                ],
            ]);

            $data = json_decode($response->getBody());

            if ($data && $data->token_type === 'bearer') {
                // Store access_token and refresh_token
                updateTokenData($data);

                // Access Token response
                $responseToken = array(
                    'title'         => '',
                    'body'          => $data,
                    'message'       => __('Access Token successful!', WP_FATT_CLOUD_TEXTDOMAIN),
                    'access_token'  => $data->access_token,
                    'refresh_token' => $data->refresh_token,
                    'code'          => 200,
                );

                /**
                 * Get FattureInCloud user info
                 *
                 * @see \FattureInCloud\Api\UserApi
                 */
                Api::getUserAndCompanyData();

                // Slack notify
                \WooPoPToFattureInCloud\Functions\slack(
                    WP_FATT_CLOUD_NAME . __(' oAuthExchange Response : ',
                        WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . json_encode($responseToken),
                    false,
                    ':information_source:'
                );

                // Log
                \WooPoPToFattureInCloud\Functions\log(print_r($responseToken, true));

                wp_send_json($responseToken);
                die();
            }

            $response = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html__('Invalid OAuth Exchange request', WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 501,
            );

            // Log
            \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

            wp_send_json($response);
            die();
        } catch (GuzzleException $e) {
            if (400 === $e->getCode()) {
                $response = array(
                    'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                    'message' => esc_html__('Error, The token is expired.', WP_FATT_CLOUD_TEXTDOMAIN),
                    'body'    => $e->getMessage(),
                    'code'    => 400,
                );

                resetApi(true);
                wp_send_json($response);
                die();
            }

            $dataException = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'type'    => __('GuzzleException Exception Message:', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => $e->getMessage(),
                'body'    => $e->getMessage(),
                'trace'   => $e->getTraceAsString(),
                'code'    => $e->getCode(),
            );

            // Log
            \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

            wp_send_json($dataException);
            die();
        }
    } elseif ('oauth_device' === Api::$oauthFlow) {

        $baseUri = Api::getBaseUri();
        $uri     = $baseUri . \WooPoPToFattureInCloud\Api::$routes['oauth_token'];

        // Client
        $client = new \GuzzleHttp\Client([
                'base_uri'        => $uri,
                'timeout'         => 15,
                'connect_timeout' => 5,
            ]
        );
        // Request authorization_code
        $response = $client->post($uri, [
            \GuzzleHttp\RequestOptions::FORM_PARAMS => [
                'grant_type'  => $grantType,
                'client_id'   => Api::getClientID(),
                'device_code' => $deviceCode,
            ],
        ]);

        $data = json_decode($response->getBody());

        if ($data && $data->token_type === 'bearer') {
            // Access Token response
            $responseToken = array(
                'title'         => '',
                'body'          => $data,
                'message'       => __('Access Token successful!', WP_FATT_CLOUD_TEXTDOMAIN),
                'access_token'  => $data->access_token,
                'refresh_token' => $data->refresh_token,
                'code'          => 200,
            );

            // Store access_token and refresh_token
            updateTokenData($data);

            /**
             * Get FattureInCloud user info
             *
             * @see \FattureInCloud\Api\UserApi
             */
            Api::getUserAndCompanyData();
            $responseToken['companyPermissions'] = getUserAndCompanies()->companyPermissions;

            // Slack notify
            \WooPoPToFattureInCloud\Functions\slack(
                WP_FATT_CLOUD_NAME . __(' oAuthExchange Response : ',
                    WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . json_encode($responseToken),
                false,
                ':information_source:'
            );

            // Log
            \WooPoPToFattureInCloud\Functions\log(print_r($responseToken, true));

            wp_send_json($responseToken);
            die();
        }

        $responseToken = array(
            'data' => $data,
            'code' => $response->getStatusCode(),
        );

        wp_send_json($responseToken);
        die();
    }
}

/**
 * Get Exchange Code
 *
 * - Exchange code and redirect to admin page
 *
 * @return void
 */
function getExchangeCode()
{
    $state = filterInput($_GET, 'state', FILTER_UNSAFE_RAW);
    $code  = filterInput($_GET, 'code', FILTER_UNSAFE_RAW);

    if ($state && $code) {
        update_option('wfc_oauth_state', $state);
        update_option('wfc_oauth_code', $code);
        wp_redirect(admin_url('admin.php?page=wc_el_inv-options-page&tab=general&oauth_exchange=true'));
        die();
    }
}

/**
 * Refreshing access token
 *
 * @return bool|void
 */
function refreshOAuthToken()
{
    $jobEvent          = filterInput($_POST, 'fcCreateInvoiceJobEvent', FILTER_UNSAFE_RAW);
    $refreshOAuthToken = filterInput($_POST, 'fcRefreshOAuthToken', FILTER_UNSAFE_RAW);

    // Set api base uri
    \WooPoPToFattureInCloud\Api::setApiBaseUri(getActiveApi()->apiUrl);

    try {
        $baseUri = Api::getBaseUri();
        $uri     = $baseUri . \WooPoPToFattureInCloud\Api::$routes['oauth_token'];

        // Client
        $client = new \GuzzleHttp\Client(['base_uri' => $uri]);

        // Params
        $params = [
            'grant_type'    => 'refresh_token',
            'redirect_uri'  => admin_url('admin.php?page=wc_el_inv-options-page&tab=general&oauth_access=true'),
            'refresh_token' => getActiveApi()->refreshToken,
        ];

        if ('oauth_token' === Api::$oauthFlow) {
            $params['client_id']     = getActiveApi()->apiUid;
            $params['client_secret'] = getActiveApi()->apiKey;
        } elseif ('oauth_device' === Api::$oauthFlow) {
            $params['client_id']     = Api::getClientID();
            $params['client_secret'] = '';
        }

        // Request refresh_token
        $response = $client->post($uri, [
            \GuzzleHttp\RequestOptions::FORM_PARAMS => $params,
        ]);

        $refreshData = json_decode($response->getBody());
        if ($refreshData && $refreshData->token_type === 'bearer') {

            // Store access_token and refresh_token
            $update = updateTokenData($refreshData);

            // Refreshing response
            $responseToken = array(
                'title'         => '',
                'body'          => $refreshData,
                'message'       => __('Refreshing Access Token successful!', WP_FATT_CLOUD_TEXTDOMAIN),
                'access_token'  => $refreshData->access_token,
                'refresh_token' => $refreshData->refresh_token,
                'update'        => $update,
                'code'          => 200,
            );

            // Slack notify
            \WooPoPToFattureInCloud\Functions\slack(
                WP_FATT_CLOUD_NAME . __(' Refreshing token Response : ',
                    WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . json_encode($responseToken),
                false,
                ':information_source:'
            );

            // Log
            \WooPoPToFattureInCloud\Functions\log(print_r($responseToken, true));

            if (! $jobEvent && ! $refreshOAuthToken) {
                wp_send_json($responseToken);
                die();
            } else {
                return true;
            }
        }

        $response = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => esc_html__('Invalid Refreshing token request', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => 501,
        );

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

        if (! $jobEvent) {
            wp_send_json($response);
            die();
        } else {
            return true;
        }
    } catch (GuzzleException $e) {

        $dataException = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'type'    => __('GuzzleException Exception Message:', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => $e->getMessage(),
            'body'    => $e->getMessage(),
            'trace'   => $e->getTraceAsString(),
            'code'    => $e->getCode(),
        );

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * createInvoice
 *
 * @return bool|void
 * @throws \Exception
 */
function createInvoice()
{
    $key = OptionPage::getK();
    if (! $key) {
        \WooPoPToFattureInCloud\Functions\log("POP secret-api-key undefined" . "\n");
        wp_send_json(array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'body'    => null,
            'message' => esc_html__('Warning: the POP license expired', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => 501,
        ));

        return;
    }

    // Log
    \WooPoPToFattureInCloud\Functions\log("createInvoice API > init" . "\n" . print_r($_POST, true) . "\n");

    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $jobEvent = filterInput($_POST, 'fcCreateInvoiceJobEvent', FILTER_UNSAFE_RAW);

    if (! Integrations::isInvoiceChannelActive(Integrations::CHANNEL_FIC)) {
        if (! $jobEvent) {
            wp_send_json(array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html__('Action not allowed: another integration channel is active.',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 422,
            ));
            die();
        }

        return;
    }

    // auto refreshOAuthToken
    if (! $jobEvent && ! getActiveApi()->expireAccessToken || (int)getActiveApi()->expireAccessToken < time()) {
        $_POST['fcRefreshOAuthToken'] = true;

        refreshOAuthToken();

        // Log
        \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
    }

    $nonce            = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);
    $choiceType       = filterInput($_POST, 'choiceType', FILTER_UNSAFE_RAW);
    $orderID          = filterInput($_POST, 'orderID', FILTER_UNSAFE_RAW);
    $orderType        = filterInput($_POST, 'orderType', FILTER_UNSAFE_RAW);
    $provider         = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW);
    $apiData          = getActiveApi();
    $userAndCompanies = getUserAndCompanies();
    $order            = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($orderID, $provider);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        wp_send_json(array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'body'    => null,
            'message' => sprintf(__("OrderID %s - wp_verify_nonce error"), $order->get_id()),
            'code'    => 501,
        ));
        // Log
        \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} - wp_verify_nonce error" . "\n" . $nonce . "\n");
        die();
    }

    $statusAllowed = in_array((string)$order->get_status(), ['processing', 'completed'], true);
    $statusAllowed = (bool)apply_filters(
        'wfc-create_invoice_status_allowed',
        $statusAllowed,
        $order,
        $choiceType,
        $orderType,
        $jobEvent,
        $provider
    );
    $disableInvoiceNumber = \WcElectronInvoice\Functions\disableInvoiceOnOrderTotalZero($order);
    $createDecision = evaluateFicCreateDecision(
        $statusAllowed,
        true === apply_filters('wfc-create-invoice_send_invoice_zero', $disableInvoiceNumber, $order->get_total())
    );

    if (! $createDecision['allowed'] && 'status_not_allowed' === $createDecision['reason']) {
        $createError = mapFicCreateDecisionError($createDecision['reason']);
        wp_send_json(array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'body'    => null,
            'message' => sprintf(__("OrderID %s - not completed or not processing status."), $order->get_id()),
            'code'    => $createError['code'],
        ));
        // Log
        \WooPoPToFattureInCloud\Functions\log(
            "OrderID {$order->get_id()} - not completed or not processing status." . "\nWC_Order: " . print_r($order,
                true) . "\n");
        die();
    }

    if (! $createDecision['allowed'] && 'total_zero_not_allowed' === $createDecision['reason']) {
        $createError = mapFicCreateDecisionError($createDecision['reason']);
        wp_send_json(array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'body'    => null,
            'message' => __('The order total is 0 (zero) you cannot send the invoice', WP_FATT_CLOUD_TEXTDOMAIN),
            'code'    => $createError['code'],
        ));
        // Log
        \WooPoPToFattureInCloud\Functions\log(
            "OrderID {$order->get_id()} - The order total is 0 (zero) you cannot send the invoice." . "\n");
        die();
    }

    $wcOrderClass       = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order');
    $wcOrderRefundClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order_Refund');

    if ($apiData->accessToken &&
        ((is_subclass_of($order, '\WcElectronInvoice\Providers\Order') ||
          is_subclass_of($order, '\WcElectronInvoice\Providers\OrderRefund')) ||
         ($order instanceof $wcOrderClass ||
          $order instanceof $wcOrderRefundClass))
    ) {
        // Document type
        $documentType = getDocType($orderType);
        $orderID      = $order->get_id();

        // Credit note data
        if ('credit_note' === $documentType) {
            $linkedOrderID = $order->get_parent_id();
        }

        // Order obj
        $order = 'shop_order' === $order->get_type() ? $order : \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($order->get_parent_id(),
            $provider);
        // Document type stored in order
        $orderChoiceType = $order->get_billing_choice_type() ?: 'invoice';

        // IssuedDocumentsApi
        $issuedDocumentsApi = Api::fattureInCloudIssuedDocumentsApi();

        // Credit note FIC invoice linked
        $creditNoteLinkedInvoiceNumber = null;
        if ('credit_note' === $documentType) {
            $documentLinkedID = \WcElectronInvoice\Functions\getPostMeta("wfc-issued_document_id", null,
                $linkedOrderID, true, 'order', $provider);
            if ($documentLinkedID && '' !== $documentLinkedID) {
                $issuedDocumentInvLinked = $issuedDocumentsApi->getIssuedDocument(
                    $userAndCompanies->companyID,
                    $documentLinkedID
                );
                if ($issuedDocumentInvLinked->valid()) {
                    $issuedDocumentInvLinkedData   = $issuedDocumentInvLinked->getData();
                    $creditNoteLinkedInvoiceNumber = $issuedDocumentInvLinkedData->getNumber() . $issuedDocumentInvLinkedData->getNumeration();
                }
            } else {
                // Return
                wp_send_json(array(
                    'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                    'body'    => null,
                    'message' => __("You cannot send the credit note because the reference to the linked document is
                    missing. (NB: before sending the credit note, the invoice must be sent).",
                        WP_FATT_CLOUD_TEXTDOMAIN),
                    'code'    => 501,
                ));
                die();
            }
        }

        if ('credit_note' === $documentType && 'receipt' === $orderChoiceType) {
            // Return
            wp_send_json(array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'body'    => null,
                'message' => __("You cannot send a credit note from an order set to receive a receipt",
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 501,
            ));
            die();
        }

        // Normalize type for XML (no XML format for receipt)
        if ('receipt' === $choiceType) {
            $choiceType = 'invoice';
        }

        $xml             = false;
        $isXmlFromRemoTe = false;
        $validXML        = false;
        if (! $jobEvent) {
            $url = esc_url_raw(add_query_arg([
                CreateXml::LIST_TYPE => $orderID,
                'provider'           => $provider,
                'key'                => $key,
                'format'             => 'xml',
                'choice_type'        => $choiceType,
                'addon'              => 'fattureincloud',
            ], site_url('/' . \WcElectronInvoice\EndPoint\Endpoints::ENDPOINT)));
            $xml = wp_remote_get($url, \WcElectronInvoice\Functions\getSessionCookieArgs('application/xml'));
            $xml = wp_remote_retrieve_body($xml);

            // Is valid XML ?
            $isXmlFromRemoTe = isXml($xml);
            $validXML        = $isXmlFromRemoTe->valid;
        }

        // Fallback for get XML
        if ((is_object($isXmlFromRemoTe) && ! $validXML) || ! $isXmlFromRemoTe) {
            // Get xml from order
            $xml = getXmlFromOrder($order);
        }

        // Is valid XML ?
        $isXml = isXml($xml);
        $overquotaPayload = \WcElectronInvoice\Functions\extractPopApiOverquotaUiPayload($xml);

        // Not valid ?
        if (! $isXml->valid && 'credit_note' !== $documentType) {
            if (is_array($overquotaPayload)) {
                wp_send_json($overquotaPayload, (int)($overquotaPayload['status'] ?? 500));
                die();
            }

            $noXmlResponse = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'errors'  => $isXml->errors,
                'xml'     => $isXml->xml,
                'message' => __('Request error: xml not valid', WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 501,
            );

            // Send error email
            $emailBody = __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                         __('Order: ') . '#' . $orderID . "\n" .
                         __('The invoice could not be created, we recommend that you check your order data and try again',
                             WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                         __('Message:', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . $noXmlResponse['message'] .
                         __('Errors:', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . print_r($noXmlResponse['errors'], true);
            sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);

            // Log
            \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($noXmlResponse, true));

            wp_send_json($noXmlResponse);
            die();
        }

        $parser = new \SimpleXMLElement($xml);

        $customerType = $order->get_invoice_type();
        // Vat number
        $pIva = $order->get_vat_number();
        // Type
        $type = $pIva && 'company' === $customerType ? 'company' : 'person';
        // Tax code
        $cf = $order->get_billing_tax_code();
        // Sdi or PEC
        $sdiType     = $order->get_sdi_type();
        $pec         = preg_match(Api::$regexPEC, $sdiType) ? $sdiType : null;
        $legalMail   = preg_match(Api::$regexLEGALMAIL, $sdiType) ? $sdiType : null;
        $generalMail = false !== filter_var($sdiType, FILTER_VALIDATE_EMAIL) ? $sdiType : null;
        $uniqueCode  = preg_match(Api::$regexWEBSERV, $sdiType) ? $sdiType : null;

        $pecEmail = null;
        if (null !== $pec && $generalMail) {
            $pecEmail = $generalMail;
        } elseif (null !== $legalMail && $generalMail) {
            $pecEmail = $generalMail;
        }

        // Personal data
        $customerName     = $order->get_billing_first_name();
        $customerLastName = $order->get_billing_last_name();
        $companyName      = $order->get_billing_company() ?: ($customerName . ' ' . $customerLastName);
        // Address
        $email       = $order->get_billing_email();
        $phone       = $order->get_billing_phone();
        $address     = $order->get_billing_address_1();
        $zip         = $order->get_billing_postcode();
        $city        = $order->get_billing_city();
        $state       = $order->get_billing_state();
        $countryCode = $order->get_billing_country();
        $countryFic  = normalizeFicCountry($countryCode);

        // Extra address
        $extraAddress           = '';
        $invoiceShippingAddress = get_option('wfc_invoice_shipping_address');
        if ('on' === $invoiceShippingAddress && 'disabled' !== GeneralFields::getGeneralShippingLocation()) {
            $shipCap       = $order->get_shipping_postcode();
            $shipState     = $order->get_shipping_state();
            $shipCity      = $order->get_shipping_city();
            $shipCountry   = $order->get_shipping_country();
            $shipAddress   = $order->get_shipping_address_1() ?: '';
            $shipFirstName = $order->get_shipping_first_name() ?? '';
            $shipLastName  = $order->get_shipping_last_name() ?? '';
            $shipCompany   = $order->get_shipping_company() ?? '';

            if ($shipCompany) {
                $extraAddress = __('Shipping: ', WP_FATT_CLOUD_TEXTDOMAIN) . $shipCompany . ' - ' . $shipAddress;
            } elseif ($shipFirstName && $shipLastName) {
                $extraAddress = __('Shipping: ',
                        WP_FATT_CLOUD_TEXTDOMAIN) . $shipFirstName . ' ' . $shipLastName . ' - ' . $shipAddress;
            } else {
                $extraAddress = __('Shipping: ', WP_FATT_CLOUD_TEXTDOMAIN) . $shipAddress;
            }

            if ($shipCap) {
                $extraAddress = $extraAddress . ' ' . $shipCap;
            }
            if ($shipCity) {
                $extraAddress = $extraAddress . ' ' . $shipCity;
            }
            if ($shipState) {
                $extraAddress = $extraAddress . ' (' . $shipState . ')';
            }
            if ($shipCountry) {
                $extraAddress = $extraAddress . ' ' . $shipCountry;
            }

            // Check if billing is equal shipping
            if ($zip === $shipCap &&
                $state === $shipState &&
                $city === $shipCity &&
                $countryCode === $shipCountry &&
                $address === $shipAddress
            ) {
                $extraAddress = '';
            }
        }

        // Normalize country string
        $country = $countryFic ?? \WC()->countries->get_countries()[$countryCode];

        // Define country - string or iso code for data entity
        $entityCountryKey   = null;
        $entityCountryValue = null;
        if (! $country && $countryCode) {
            $entityCountryKey   = 'country_iso';
            $entityCountryValue = $countryCode;
        } elseif ($country) {
            $entityCountryKey   = 'country';
            $entityCountryValue = $country;
        }

        // Items
        $items = $parser[0]->FatturaElettronicaBody->DatiBeniServizi->DettaglioLinee;

        // Get Payments method code
        $payCode = getPaymentMethodCodeByMethod($order->get_payment_method());

        /**
         * Disable by payment method
         */
        if (true === apply_filters('wfc-create-invoice_exclude_payment_method_code', false,
                $payCode,
                $order->get_payment_method()
            )
        ) {
            if (! $jobEvent) {
                wp_send_json(array(
                    'title'   => __('Payment method excluded for sending.', WP_FATT_CLOUD_TEXTDOMAIN),
                    'body'    => null,
                    'message' => sprintf('<b style="color: red">(*)</b> %s - %s (%s)',
                        __("The payment method of the order has been excluded from sending to Fatture in Cloud",
                            WP_FATT_CLOUD_TEXTDOMAIN),
                        $payCode,
                        $order->get_payment_method()
                    ),
                    'code'    => 501,
                ));
                die();
            } else {
                \WooPoPToFattureInCloud\Functions\log(sprintf('%s: %s (%s)',
                        __("Payment method excluded for sending", WP_FATT_CLOUD_TEXTDOMAIN),
                        $payCode,
                        $order->get_payment_method()) . "\n");

                return false;
            }
        }

        if ('shop_order_refund' === $orderType) {
            $payCodeRefund = $order->get_refund_payment_method();
            if ($payCodeRefund && '' !== $payCodeRefund) {
                $payCode = $payCodeRefund;
            }
        }

        // Get Payment name key
        $payNameKey = getKey(getPaymentMethodNameByCode($payCode));
        // Pay methods and accounts
        $payMethods       = Api::getPaymentMethods();
        $payAccounts      = Api::getPaymentAccounts();
        $payAccountActive = Api::getPaymentAccountActive();

        /**
         * Set default Payment Method
         */
        if (! $payMethods ||
            (is_array($payMethods) && empty($payMethods[$payNameKey])) ||
            ! $payAccounts ||
            (is_array($payAccounts) && empty($payAccounts[$payNameKey]))
        ) {
            // Default payment account
            Api::setPaymentsAccountsAndMethods();
            $payMethods = Api::getPaymentMethods();
        }

        /**
         * Shop Order from mapping data
         *
         * - Get Pay method ID and Name
         */
        $paymentMethodID   = null;
        $paymentMethodName = null;

        if ('shop_order' === $orderType) {
            foreach ($payMethods as $key => $method) {
                $methodID    = $method['id'];
                $methodName  = $method['name'];
                $gatewayData = (object)get_option("wfc_mapping_gateways__{$methodID}") ?? null;
                if (property_exists($gatewayData,
                        'payGatewayCode') && $order->get_payment_method() === $gatewayData->payGatewayCode) {
                    $paymentMethodID   = $gatewayData->payMethodID;
                    $paymentMethodName = $methodName;
                    break;
                }
            }
        }

        if (! $paymentMethodID) {
            $paymentMethodID = $payMethods[$payNameKey]['id'];
        }

        if (! $paymentMethodName) {
            $paymentMethodName = $payMethods[$payNameKey]['name'];
        }

        /**
         * Filter $payMethods
         * The payment method name
         */
        $paymentMethodName = apply_filters('wfc-create-invoice_payment_method_name', $paymentMethodName);

        /**
         * Filter $payMethods
         * The payment method id
         */
        $paymentMethodID = apply_filters('wfc-create-invoice_payment_method_id', $paymentMethodID);

        /**
         * Payment Account
         */
        if (empty($payAccounts) && empty($payAccountActive)) {
            $payAccounts = Api::getPaymentAccounts();
        }
        // take the account if the name is the same as that of the payment method.
        $payAccountName = ! empty($payAccounts[$payNameKey]) ? $payAccounts[$payNameKey]['name'] : reset($payAccounts)['name'];
        $payAccountID   = ! empty($payAccounts[$payNameKey]) ? $payAccounts[$payNameKey]['id'] : reset($payAccounts)['id'];

        // Selected and active payment account
        if (! empty($payAccountActive)) {
            $payAccountName = reset($payAccountActive)['name'];
            $payAccountID   = reset($payAccountActive)['id'];
        }

        // Refund order check isset method
        if (! $paymentMethodName || ! $paymentMethodID || ! $payAccountName || ! $payAccountID) {
            // Refund
            if ('shop_order' !== $orderType) {
                // check Method name
                $method = array(
                    'Contanti',
                    'Assegno',
                    'Assegno circolare',
                    'Bonifico',
                    'Carta di pagamento',
                );

                if (! in_array($paymentMethodName, $method)) {
                    $noPaymentsDataResponse = array(
                        'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                        'body'    => null,
                        'message' => __("Attenzione: Il metodo di pagamento &laquo; {$paymentMethodName} &raquo; impostato su Fatture in Cloud, non corrisponde ai seguenti nomi: <b>(Bonifico, Assegno, Contanti, Carta di pagamento)</b>."),
                        'code'    => 501,
                    );
                } else {
                    $noPaymentsDataResponse = array(
                        'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                        'body'    => null,
                        'message' => __('Error: Payments Account and Method is null', WP_FATT_CLOUD_TEXTDOMAIN),
                        'code'    => 501,
                    );
                }

                // Log
                \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($noPaymentsDataResponse,
                        true));

                wp_send_json($noPaymentsDataResponse);
                die();
            } else {
                // Order
                $noPaymentsDataResponse = array(
                    'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                    'body'    => null,
                    'message' => __('Error: Payments Account and Method is null', WP_FATT_CLOUD_TEXTDOMAIN),
                    'code'    => 501,
                );
                // Log
                \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($noPaymentsDataResponse,
                        true));

                wp_send_json($noPaymentsDataResponse);
                die();
            }
        }

        /**
         * Get account payment by account method id
         */
        $mappingPayment = (object)get_option("wfc_mapping_payment_methods_{$paymentMethodID}") ?? null;
        if ($mappingPayment && isset($mappingPayment->payAccountID) && ! empty($payAccounts)) {
            foreach ($payAccounts as $account) {
                if ($account['id'] === (int)$mappingPayment->payAccountID) {
                    $payAccountName = $account['name'];
                    $payAccountID   = $account['id'];
                    break;
                }
            }
        }

        /**
         * Filter $payAccountName
         * The payment account name
         */
        $payAccountName = apply_filters('wfc-create-invoice_account_payment_name', $payAccountName);

        /**
         * Filter $payAccountID
         * The payment account id
         */
        $payAccountID = apply_filters('wfc-create-invoice_account_payment_id', $payAccountID);

        if (! $payAccountName || ! $payAccountID) {
            $noPaymentsDataResponse = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'body'    => null,
                'message' => __('Error: Payments Account and Method is null', WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 501,
            );

            \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($noPaymentsDataResponse,
                    true));

            wp_send_json($noPaymentsDataResponse);
            die();
        }

        /**
         * IBAN
         */
        $iban        = null;
        $bankName    = null;
        $accountName = null;
        $bacs        = get_option('woocommerce_bacs_accounts');
        if ('MP05' === $payCode && ! empty($bacs) && is_array($bacs)) {
            // First bacs account
            $bankName    = $bacs[0]['bank_name'];
            $accountName = $bacs[0]['account_name'];
            $iban        = $bacs[0]['iban'];
        }

        /**
         * VAT types
         */
        $vatTypes = Api::getVatTypes();
        if (! $vatTypes) {
            $vatTypes = Api::setVatTypes();
        }

        // Dates handling
        $invoiceDateTime = CreateXml::dateInvoice(
            (object)[
                'id'         => $order->get_id(),
                'provider'   => $order->get_provider(),
                'order_type' => $order->get_type(),
            ],
            true
        );

        $timeZone = new TimeZone();
        $timeZone = new \DateTimeZone($timeZone->getTimeZone()->getName());
        if (is_subclass_of($invoiceDateTime, 'DateTime')) {
            $invoiceDateTime->setTimezone($timeZone);
        }

        // Data emissione documento: sempre adesso
        $docDateNow = new \DateTimeImmutable('now', $timeZone);

        // Data di creazione ordine in timezone locale — usata per i corrispettivi (data transazione economica).
        // WC_DateTime è UTC: impostiamo il timezone locale prima di formattare.
        $orderCreatedDate = null;
        $wcDateCreated    = $order->get_date_created();
        if ($wcDateCreated instanceof \DateTime) {
            $wcDateCreated->setTimezone($timeZone);
            $orderCreatedDate = $wcDateCreated->format('Y-m-d');
        }

        // Data fattura collegata (per note di credito)
        $linkedInvDate = null;
        if ('shop_order_refund' === $orderType && isset($parser[0]->FatturaElettronicaBody->DatiGenerali->DatiFattureCollegate->Data)) {
            $linkedInvDate = $parser[0]->FatturaElettronicaBody->DatiGenerali->DatiFattureCollegate->Data->__toString();
        }

        // Prev day safeguard kept for od_date only (SDI rifiuta date "indietro" se l'ora è troppo bassa)
        \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} - invoiceDate time: " . $invoiceDateTime->format('Y-m-d H:i'));
        if ((int)$invoiceDateTime->format('H') <= 2) {
            $invoiceDateTime->modify('- 1 day');
            \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} - Set Prev invoiceDate: " . $invoiceDateTime->format('Y-m-d'));
        }

        // od_date da usare nel body (operazione per fatture, data doc originale per NC)
        $odDateValue = ('shop_order_refund' === $orderType && $linkedInvDate)
            ? $linkedInvDate
            : $invoiceDateTime->format('Y-m-d');

        // Options
        $options = OptionPage::init();

        // Stamp duty value
        $addDuty   = $options->getOptions('add_stamp_duty');
        $dutyValue = '';
        if ('on' === $addDuty) {
            $dutyValue = $order->get_total() > floatval('77.47') ? 2 : 0;
        }

        $lockKey = null;

        try {
            $body = array(
                'data'    => array(
                    'subject'           => apply_filters(
                        'wfc-invoice_body_data_subject',
                        "WC Order: #{$orderID}",
                        $orderID
                    ),
                    'visible_subject'   => apply_filters(
                        'wfc-invoice_body_data_subject',
                        "WC Order: #{$orderID}",
                        $orderID
                    ),
                    // Entity
                    'entity'            => array(
                        'type'                       => $type,
                        'default_vat'                => array(
                            'editable' => true,
                        ),
                        'default_payment_terms'      => 0,
                        'default_payment_terms_type' => 'standard',
                        'default_payment_method'     => array(
                            'type'                    => 'standard',
                            'default_payment_account' => array(
                                'type' => 'standard',
                            ),
                        ),
                        // Billing data
                        'e_invoice'                  => true,
                        'name'                       => $companyName,
                        'first_name'                 => $customerName,
                        'last_name'                  => $customerLastName,
                        'address_street'             => $address,
                        'address_postal_code'        => $zip,
                        'address_city'               => $city,
                        'address_province'           => $state,
                        'address_extra'              => apply_filters('wfc-invoice_entity_address_extra', $extraAddress,
                            $order),
                        'email'                      => $email,
                        'certified_email'            => $pecEmail ?: '',
                        'phone'                      => $phone,
                        'ei_code'                    => $uniqueCode ?: '',
                    ),
                    'type'              => $documentType,
                    // Virtual duty
                    'stamp_duty'        => $dutyValue,
                    // Payment method data "payment_method"
                    'payment_method'    => array(
                        'type'                    => 'standard',
                        'default_payment_account' => array(
                            'type' => 'standard',
                            'name' => "{$payAccountName}",
                            'id'   => (int)$payAccountID,
                        ),
                        'id'                      => (int)$paymentMethodID,
                        'name'                    => "{$paymentMethodName}",
                    ),
                    'use_split_payment' => false,
                    'use_gross_prices'  => false,
                    'e_invoice'         => true,
                    'ei_data'           => array(
                        // VatKind: Vat kind [esigibilità IVA]
                        // Default: null
                        // Allowed: null - I (immediata) - D (differita) - S (split payment)
                        'vat_kind'               => apply_filters('wfc-invoice_body_ei_data_vat_kind', null),
                        'original_document_type' => 'ordine',
                        'od_number'              => "{$orderID}",
                        // invoice_date viene valorizzato più sotto per gestire NC e fatture
                        'payment_method'         => "{$payCode}",
                    ),
                ),
                // Fix totals
                // @see https://developers.fattureincloud.it/docs/guides/invoice-totals/
                'options' => array(
                    'fix_payments' => true,
                ),
            );

            $rcCenter     = apply_filters('wfc-invoice_body_data_rc_center', null, $orderID);
            $notes        = apply_filters('wfc-invoice_body_data_notes', null, $orderID);
            $rivalsa      = apply_filters('wfc-invoice_body_data_rivalsa', null, $orderID);
            $cassa        = apply_filters('wfc-invoice_body_data_cassa', null, $orderID);
            $cassaTaxable = apply_filters('wfc-invoice_body_data_cassa_taxable', null, $orderID);

            if ($rcCenter && is_string($rcCenter)) {
                $body['data']['rc_center'] = $rcCenter;
            }
            if ($notes && is_string($notes)) {
                $body['data']['notes'] = $notes;
            }
            if ($rivalsa && is_string($rivalsa)) {
                $body['data']['rivalsa'] = $rivalsa;
            }
            if ($cassa && is_string($cassa)) {
                $body['data']['cassa'] = $cassa;
            }
            if ($cassaTaxable && is_string($cassaTaxable)) {
                $body['data']['cassa_taxable'] = $cassaTaxable;
            }

            // Set country
            if ($entityCountryKey && $entityCountryValue) {
                $body['data']['entity'][$entityCountryKey] = $entityCountryValue;
            }

            if ('company' === $type) {
                $body['data']['entity']['vat_number'] = $pIva;

                if ('' !== $cf && $cf !== $pIva) {
                    $body['data']['entity']['tax_code'] = $cf;
                }
            } else {
                if ('' !== $cf) {
                    $body['data']['entity']['tax_code'] = $cf;
                }
            }

            // IBAN data if payment method is bacs
            if ($iban) {
                $body['data']['payment_method']['default_payment_account']['iban'] = $iban;
                $body['data']['payment_method']['bank_iban']                       = $iban;
            }

            $vatTypesForcedRefresh = false;

            // Items "items_list"
            if (! empty($items)) {
                foreach ($items as $item) {
                    $prodID        = 0000;
                    $prodName      = '';
                    $prodShortDesc = null;
                    $prodDesc      = null;
                    $prodCode      = '';

                    // Iva value (22, 10, 4, 0 etc...)
                    $val       = $item->AliquotaIVA->__toString() ?: 0;
                    $xmlNature = $item->Natura->__toString() ?: null;

                    // Discount percentage
                    $discountPercentage = 0;
                    if (property_exists($item, 'ScontoMaggiorazione')) {
                        $discountPercentage = floatval($item->ScontoMaggiorazione->Percentuale) ?: 0;
                    }

                    /**
                     * 1. Get Nature code data
                     * 2. remove "N" from code
                     * 3. Filter Nature number
                     */
                    $taxArgs = (object)array(
                        'id'         => $order->get_id(),
                        'parent_id'  => $order->get_parent_id(),
                        'order_type' => $order->get_type(),
                        'provider'   => $provider,
                    );
                    $refN    = TaxFields::getRefN($taxArgs) ?: null;
                    $nature  = TaxFields::getNature($taxArgs) ?: $xmlNature;

                    $natureNumber = $nature && '' !== $nature ? str_replace('N', '', $nature) : null;
                    $natureNumber = apply_filters('wfc-invoice_document-ei_type', $natureNumber, $order);

                    // Default data by only vat value
                    $vatID = ! empty($vatTypes[(int)$val]) ? $vatTypes[(int)$val]['id'] : null;

                    if (! $vatID) {
                        // Update VatTypes
                        $vatTypes = Api::setVatTypes();
                        $vatID    = ! empty($vatTypes[(int)$val]) ? $vatTypes[(int)$val]['id'] : null;
                    }

                    $giftProduct = false;
                    if ('IT' === $countryCode &&
                        0 === (int)$val &&
                        property_exists($item, 'CodiceArticolo')
                    ) {
                        $productForVat = wc_get_product((int)$item->CodiceArticolo->CodiceValore->__toString());
                        if ($productForVat instanceof \WC_Product) {
                            $giftProduct = 'yes' === getPostMeta('_active_gift_product', null, $productForVat->get_id());
                        }
                    }

                    $vatResolution = resolveFicVatTypeId(
                        is_array($vatTypes) ? $vatTypes : [],
                        $val,
                        $vatID ? (int)$vatID : null,
                        $natureNumber,
                        $refN,
                        GeneralFields::getGeneralInvoiceOptionTaxRegime(),
                        $countryCode,
                        $giftProduct
                    );

                    // Heal stale cached standard-rate ids by forcing a single live refresh.
                    if (! $vatTypesForcedRefresh && 'default_rate' === $vatResolution['source']) {
                        $refreshedVatTypes = Api::setVatTypes(true);
                        if (is_array($refreshedVatTypes) && ! empty($refreshedVatTypes)) {
                            $vatTypes = $refreshedVatTypes;
                            $vatID    = ! empty($vatTypes[(int)$val]) ? $vatTypes[(int)$val]['id'] : null;
                            $vatResolution = resolveFicVatTypeId(
                                $vatTypes,
                                $val,
                                $vatID ? (int)$vatID : null,
                                $natureNumber,
                                $refN,
                                GeneralFields::getGeneralInvoiceOptionTaxRegime(),
                                $countryCode,
                                $giftProduct
                            );
                        }
                        $vatTypesForcedRefresh = true;
                    }

                    $vatID = $vatResolution['id'];

                    if ($vatResolution['apply_n4_override']) {
                        /**
                         * Filter vat type N4 id
                         *
                         * $vatID Vat id
                         */
                        $vatID = apply_filters('wfc-vat_type_N4_fic_id', $vatID);
                    }

                    /**
                     * Filter vat types id after internal normalization so custom overrides see the final candidate.
                     *
                     * $vatID    Vat id
                     * $vatTypes VatTypes array
                     * $item     Xml line item
                     */
                    $vatID = apply_filters('wfc-vat_types_fic_id', $vatID, $vatTypes, $item);

                    if (property_exists($item, 'CodiceArticolo')) {
                        $prodCode = ($item->CodiceArticolo->CodiceTipo . '_' . $item->CodiceArticolo->CodiceValore);
                        // Filter product code
                        $prodCode = apply_filters('wfc-invoice_product_code', $prodCode, $item);

                        $prod = wc_get_product((int)$item->CodiceArticolo->CodiceValore->__toString());
                        if ($prod instanceof \WC_Product) {
                            $prodName      = $prod->get_name();
                            $prodShortDesc = $prod->get_short_description();
                            $prodDesc      = $prod->get_description();
                            $prodCode      = $prod->get_sku();
                            $prodID        = $prod->get_id();
                        }

                        // Fallback prod desc
                        if (! $prod) {
                            $prodName = trim($item->Descrizione->__toString());
                        }

                        if ('SHIPPING' === $item->CodiceArticolo->CodiceValore->__toString()) {
                            // Shipping name
                            $prodName = apply_filters('wfc-invoice_shipping_line_name',
                                esc_html__("Shipping cost", WP_FATT_CLOUD_TEXTDOMAIN)
                            );
                            $shipping = $order->get_items('shipping');
                            if (! empty($shipping)) {
                                foreach ($shipping as $ship) {
                                    if ($ship instanceof \WC_Order_Item_Shipping) {
                                        $prodID = $ship->get_id();
                                        break;
                                    }
                                }
                            }
                        }

                        if ('FEE' === $item->CodiceArticolo->CodiceValore->__toString()) {
                            $prodName = trim($item->Descrizione->__toString());
                            $prodID   = null;
                        }

                        // Stamp duty charged to the customer
                        if ('BOLLO' === $item->CodiceArticolo->CodiceValore->__toString()) {
                            // Stamp duty name
                            $prodName = apply_filters('wfc-invoice_stamp_duty_line_name', "Bollo in fattura");
                            // "Escl. art. 15 DPR 633/72"
                            $vatID  = 21;
                            $prodID = null;
                        }
                    } else {
                        $prodName = trim($item->Descrizione->__toString());
                    }

                    // Add product name with metadata on FIC integration
                    $productViewMeta = OptionPage::init()->getOptions('invoice_product_meta');
                    if ('on' === $productViewMeta) {
                        $prodName = trim($item->Descrizione->__toString());
                    }

                    // Vat Type data
                    $vatType = new VatType(array('id' => (int)$vatID));

                    /**
                     * Filter item product category
                     */
                    $category = apply_filters('wfc-invoice-items_list-product_category', null, $prodID);

                    /**
                     * Filter item product id
                     */
                    $prodID = apply_filters('wfc-invoice-items_list-product_id', $prodID, $prodName, $prodCode);

                    // Items data
                    $body['data']['items_list'][] = array(
                        'vat'                     => $vatType,
                        'not_taxable'             => false,
                        'apply_withholding_taxes' => false,
                        'discount'                => $discountPercentage,
                        'discount_highlight'      => false,
                        'in_ddt'                  => false,
                        'product_id'              => (int)$prodID ?: 000,
                        'category'                => $category ?: "",
                        'code'                    => $prodCode,
                        'name'                    => $prodName,
                        'description'             => apply_filters('wfc-invoice-items_list_description', '',
                            $prodShortDesc, $prodDesc, $prodID),
                        'net_price'               => floatval($item->PrezzoUnitario->__toString()),
                        'gross_price'             => floatval($item->PrezzoTotale->__toString()),
                        'qty'                     => apply_filters('wfc-invoice-items_list_qty',
                            (int)$item->Quantita->__toString(), $prodID),
                    );
                }
            }

            // Get order status
            $status = $order->get_status();
            // check status for scheduled
            $valueOrderStatus = get_option('wfc_scheduled_order_status', 'completed');

            // Paid
            $paid          = ('shop_order' === $orderType && ($valueOrderStatus === $status));
            $orderPaidDate = $order->get_date_paid();

            // Payment terms
            $payTerms         = apply_filters('wfc-payments_list_payment_terms', 8);
            $orderPaymentDate = $paid && $orderPaidDate ? $orderPaidDate->date('Y-m-d') : null;

            // No paid date set paid with completed date
            if (! $orderPaymentDate && 'completed' === $status) {
                $orderPaymentDate = $order->get_date_completed()->date('Y-m-d');
            }

            if ($order->has_status($valueOrderStatus)) {
                $doDateNoPaid = $orderPaymentDate;
                // set paid_date - completed order set completed date
                $orderNoPaymentDate = '';
            } else {
                // expired payment date
                $doDateNoPaid = apply_filters('wfc-payments_list_due_date',
                    $order->get_date_created()->modify('+8 days')->date('Y-m-d')
                );
                // set paid_date - no completed order set created date
                $orderNoPaymentDate = $order->get_date_created()->date('Y-m-d');
            }

            // Set paid on refund
            if ('credit_note' === $documentType) {
                $refund = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($orderID, $provider);
                if (is_subclass_of($refund, '\WcElectronInvoice\Providers\OrderRefund') ||
                    $refund instanceof $wcOrderRefundClass
                ) {
                    $paid             = true;
                    $orderPaymentDate = $refund->get_date_created()->date('Y-m-d');
                }
            }

            // Payment list "payments_list"
            $body['data']['payments_list'][] = array(
                'status'          => $paid ? 'paid' : 'not_paid',
                'payment_account' => array(
                    'type' => 'standard',
                    'name' => "{$payAccountName}",
                    'id'   => (int)$payAccountID,
                ),
                'due_date'        => $paid && $orderPaymentDate ? $orderPaymentDate : $doDateNoPaid,
                'amount'          => floatval($parser[0]->FatturaElettronicaBody->DatiPagamento->DettaglioPagamento->ImportoPagamento->__toString()),
                'paid_date'       => $paid && $orderPaymentDate ? $orderPaymentDate : $orderNoPaymentDate,
            );

            if ('completed' === $status) {
                $body['data']['payments_list'][0]['payment_terms'] = array(
                    'days' => 0,
                    'type' => 'standard',
                );
            } else {
                $body['data']['payments_list'][0]['payment_terms'] = array(
                    'days' => $payTerms,
                    'type' => 'standard',
                );
            }

            // IBAN data if payment method is bacs
            if ($iban) {
                $body['data']['payments_list'][0]['payment_account']['iban'] = $iban;
            }

            /**
             * Get numeration prefix
             */

            // Invoice
            $numeration = $options->getOptions('prefix_invoice_number');
            if ('receipt' === $orderChoiceType) {
                // Receipt
                $numeration = $options->getOptions('prefix_receipt_number');
            } else if ('shop_order_refund' === $orderType) {
                // Credit note
                $numeration = $options->getOptions('prefix_credit_note_number');
            }

            if ($numeration) {
                if (true === apply_filters('wfc-prefix_numeration_param_sanitize', true)) {
                    // Sanitize - Remove sep
                    $numeration = str_replace(array('-', '_'), '', $numeration);
                }

                /**
                 * Filter numeration
                 */
                $numeration = apply_filters("wfc-prefix_numeration", $numeration, $orderChoiceType, $orderType, $order);

                // Set numeration
                $body['data']['numeration'] = "{$numeration}";
            } else {

                /**
                 * Filter numeration
                 */
                $numeration = apply_filters("wfc-prefix_numeration", '', $orderChoiceType, $orderType, $order);

                $body['data']['numeration'] = "{$numeration}";
            }

            $body['data'] = array_merge(
                $body['data'],
                buildFicDocumentDisplayOptions()
            );
            if (false === apply_filters('wfc-disabled_data_date_document', false, $jobEvent)) {
                // Corrispettivi: data di creazione ordine in timezone locale (transazione economica — obbligo normativo).
                // Il safeguard -1 day di $invoiceDateTime è per fatture SDI; corrispettivi usano get_date_created().
                // Fatture e note di credito (SDI): data di emissione odierna — richiesto per numerazione sequenziale.
                $body['data']['date'] = ('receipt' === $orderChoiceType && $orderCreatedDate)
                    ? $orderCreatedDate
                    : $docDateNow->format('Y-m-d');
            }
            $invoiceDate = ('credit_note' === $documentType && $linkedInvDate) ? $linkedInvDate : $docDateNow->format('Y-m-d');
            $eiInvoiceNumber = 'credit_note' === $documentType ? (string)$creditNoteLinkedInvoiceNumber : null;
            $bankData        = $iban ? [
                'iban'        => $iban,
                'name'        => $bankName,
                'beneficiary' => $accountName,
            ] : null;

            $body['data']['ei_data'] = array_merge(
                $body['data']['ei_data'],
                buildFicEiDataPayload(
                    (string)$odDateValue,
                    (string)$invoiceDate,
                    $eiInvoiceNumber,
                    $bankData
                )
            );

            // Log
            if ('dev' === WP_FATT_CLOUD) {
                \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} - createIssuedDocument BODY: \n" . print_r($body,
                        true));
            }

            // Set body for create receipt
            if ('receipt' === $orderChoiceType) {
                if ('write' === $userAndCompanies->companyPermissions['issued_receipts']) {
                    /**
                     * createIssuedDocument receipt
                     */
                    $body['data']['type'] = 'receipt';
                } else {
                    $permissionError = array(
                        'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                        'message' => esc_html__('You don\'t have permission to create a receipt',
                            WP_FATT_CLOUD_TEXTDOMAIN),
                        'code'    => 422,
                    );

                    // Log
                    \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($permissionError,
                            true));

                    wp_send_json($permissionError);
                    die();
                }
            }

            $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('fic', 'create_invoice', (int)$orderID,
                (string)$provider, (string)$documentType);
            $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision(
                \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0),
                (bool)$jobEvent
            );
            if (! $lockDecision['continue']) {
                if ($lockDecision['respond_error']) {
                    $lockError = \WcElectronInvoice\Functions\mapLockBusyError('fic', 'create_invoice');
                    wp_send_json(array(
                        'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                        'message' => esc_html__('Action skipped: another create process is already running for this order.',
                            WP_FATT_CLOUD_TEXTDOMAIN),
                        'code'    => $lockError['code'],
                    ));
                    die();
                }

                return;
            }

            // Check issued document exist
            $documentID = \WcElectronInvoice\Functions\getPostMeta("wfc-issued_document_id", null, $orderID, true,
                'order', $provider);
            $createDecision = evaluateFicCreateDecision(true, false, $documentID);
            if (! $createDecision['allowed'] && 'already_created' === $createDecision['reason']) {
                if (! $jobEvent) {
                    \WcElectronInvoice\Functions\releaseDbLock($lockKey);
                    $createError = mapFicCreateDecisionError($createDecision['reason']);
                    wp_send_json(array(
                        'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                        'message' => esc_html__('The document has already been created', WP_FATT_CLOUD_TEXTDOMAIN),
                        'code'    => $createError['code'],
                    ));
                    \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} - The document has already been created: {$documentID}" . "\n");
                    die();
                } else {
                    \WcElectronInvoice\Functions\releaseDbLock($lockKey);
                    return;
                }
            }

            /**
             * Filter body
             */
            $body = apply_filters('wfc-create_document_body', $body);

            /**
             * Create client if you have permission
             */
            $messageClient = '';
            if ('write' === getUserAndCompanies()->companyPermissions['clients']) {
                $wcClient = null;
                if (method_exists($order, 'get_customer_id')) {
                    // Get customer
                    $wcClient = get_user_by('ID', $order->get_customer_id());
                }

                /**
                 * Get Client lists by vat_number or tax_code
                 */
                $clientID          = null;
                $wcClientEmail     = null;
                $ficClientIdentify = null;

                $ficClient = Api::fattureInCloudCreateClient();
                if ($pIva && '' !== $pIva || $cf && '' !== $cf) {
                    if ($pIva) {
                        $q = "vat_number = '{$pIva}'";
                    } else {
                        $q = "tax_code = '{$cf}'";
                    }

                    $listsClient = $ficClient->listClients($userAndCompanies->companyID,
                        null,
                        'detailed',
                        null,
                        1,
                        5,
                        $q
                    );
                    if ($listsClient->valid() && 1 === $listsClient->getTotal()) {
                        $clientData = $listsClient->getData();
                        // Fic client ID
                        $clientID = $clientData[0]->getId();
                        // fic_client_id, set var for update.
                        $ficClientIdentify = $clientID;
                    }
                }

                /**
                 * Prepare data for create or update
                 */
                // User exist ?
                if ($wcClient instanceof \WP_User) {
                    // WC-FIC client ID or null
                    $wcClientIdentify = get_user_meta($wcClient->ID, 'fic_client_id', true) ?? null;
                    // Use billing email
                    $wcClientEmail = $wcClient->user_email;
                } else {
                    // FIC client ID ou null
                    $wcClientIdentify = $clientID;
                }

                // Client Body request
                $clientBody = new CreateClientRequest(array(
                    'data' => array(
                        'name'                => $companyName ?: '',
                        'type'                => $pIva && in_array($customerType, ['company', 'freelance']) ? 'company' : 'person',
                        // Client Type
                        'first_name'          => $customerName,
                        'last_name'           => $customerLastName,
                        'vat_number'          => $pIva && 'company' === $customerType ? $pIva : '',
                        'tax_code'            => 'company' !== $customerType && $cf !== $pIva ? $cf : '',
                        'address_street'      => $address,
                        'address_postal_code' => $zip,
                        'address_city'        => $city,
                        'address_province'    => $state,
                        'address_extra'       => apply_filters('wfc-create-client_address_extra', $extraAddress,
                            $order),
                        'country'             => $country,
                        'email'               => $email ?: $wcClientEmail,
                        // billing email or user email
                        'certified_email'     => $pecEmail ?: '',
                        'phone'               => $phone ?: '',
                        'ei_code'             => $uniqueCode ?: '',
                    ),
                ));

                if (! $wcClientIdentify && ! $ficClientIdentify) {
                    // Create client on FIC
                    try {
                        $createdClient = $ficClient->createClient(
                            $userAndCompanies->companyID,
                            $clientBody
                        );

                        if ($createdClient->valid()) {
                            $wcClientIdentify = $createdClient->getData()->getId();
                            if ($wcClient instanceof \WP_User) {
                                update_user_meta($wcClient->ID, 'fic_client_id', $wcClientIdentify);
                            }
                            $issuedDocumentResponse['clientCreated'] = $wcClientIdentify;

                            $messageClient = '<br>' . __('Customer master data created successfully!',
                                    WP_FATT_CLOUD_TEXTDOMAIN
                                );
                        }
                    } catch (ApiException $e) {
                        $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

                        $dataException = array(
                            'title'       => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                            'type'        => __('Exception when calling createClient', WP_FATT_CLOUD_TEXTDOMAIN),
                            'message'     => $message,
                            'body'        => $e->getResponseBody(),
                            'bodyRequest' => $clientBody,
                            'code'        => $e->getCode(),
                            'trace'       => $e->getTraceAsString(),
                        );

                        // Log
                        \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($dataException,
                                true));

                        $messageClient = '<br>' . __('Error: Customer master data not created',
                                WP_FATT_CLOUD_TEXTDOMAIN
                            ) . '<br><small>(*)' . $message . '</small>';
                    }
                } elseif ($wcClientIdentify || $ficClientIdentify) {
                    // Update client FIC for only WC client
                    try {
                        $ficClient->modifyClient(
                            $userAndCompanies->companyID,
                            $wcClientIdentify ?? $ficClientIdentify,
                            $clientBody
                        );

                        $messageClient = '<br>' . __('Customer master changed successfully!',
                                WP_FATT_CLOUD_TEXTDOMAIN
                            );
                    } catch (\Exception $e) {
                        // Remove user meta if client not exist
                        if (404 === $e->getCode()) {
                            if ($wcClient instanceof \WP_User) {
                                delete_user_meta($wcClient->ID, 'fic_client_id');
                            }
                        }
                    }
                }

                // Set client entity id
                if ($wcClientIdentify || $clientID) {
                    $body['data']['entity']['id'] = $wcClientIdentify ?: $clientID;
                }
            }

            /**
             * createIssuedDocument invoice
             */
            $issuedDocumentInv = $issuedDocumentsApi->createIssuedDocument(
                $userAndCompanies->companyID,
                new CreateIssuedDocumentRequest($body)
            );

            if ($issuedDocumentInv->valid()) {
                // Store receipt id
                if ('receipt' === $orderChoiceType) {
                    if ('write' === $userAndCompanies->companyPermissions['issued_receipts']) {
                        $order->update_meta('wfc-receipt_document_id', $issuedDocumentInv->getData()->getId());
                    }
                }

                if ('credit_note' === $documentType) {
                    // Refund order
                    $orderRefund = wc_get_order($orderID);
                    if (is_object($orderRefund) && method_exists($orderRefund, 'get_id')) {
                        // Save document id and number
                        $order->update_meta("wfc-issued_credit_note_{$orderRefund->get_id()}_document_id",
                            $issuedDocumentInv->getData()->getId());
                        $order->update_meta("wfc-issued_credit_note_{$orderRefund->get_id()}_invoice_number",
                            $issuedDocumentInv->getData()->getNumber());
                        // Save PDF url
                        $order->update_meta("wfc-issued_credit_note_{$orderRefund->get_id()}_document_pdf_url",
                            $issuedDocumentInv->getData()->getUrl());
                    }
                } else {
                    // Save document id and number
                    $order->update_meta("wfc-issued_document_id", $issuedDocumentInv->getData()->getId());
                    $order->update_meta("wfc-issued_invoice_number", $issuedDocumentInv->getData()->getNumber());
                    // Save PDF url
                    $order->update_meta("wfc-issued_document_pdf_url", $issuedDocumentInv->getData()->getUrl());
                }

                $message = esc_html__('Issued Document created successfully!', WP_FATT_CLOUD_TEXTDOMAIN);
                // Set errors meta
                $order->update_meta('wfc-create_invoice_errors', false);
                $message = $message . $messageClient;
                $code    = 200;
            } else {
                $message = json_encode($issuedDocumentInv->getData()->listInvalidProperties());
                $code    = 422;

                // Send error email
                $emailBody = __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                             __('Order: ', WP_FATT_CLOUD_TEXTDOMAIN) . '#' . $orderID . "\n" .
                             __('The invoice could not be created, we recommend that you check your order data and try again',
                                 WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                             __('Errors:', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . $message;
                sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);
            }

            /**
             * After createIssuedDocument
             */
            do_action('wfc-create_invoice', $issuedDocumentInv, $body);

            /**
             * Filter message
             */
            $message = apply_filters('wfc-create_invoice_message', $message);

            /**
             * Filter code
             */
            $code = apply_filters('wfc-create_invoice_code', $code);

            // Slack notify invoice
            $slackMessage = str_replace('<br>', ' - ', $message);
            \WooPoPToFattureInCloud\Functions\slack(
                WP_FATT_CLOUD_NAME . ' ' . $slackMessage . "\n" . json_encode($issuedDocumentInv),
                false,
                ':information_source:'
            );

            $issuedDocumentResponse = array(
                'title'        => '',
                'message'      => $message,
                'result'       => json_encode($issuedDocumentInv),
                'extra_result' => apply_filters('wfc-create_invoice_extra_result', $extraResult = ''),
                'code'         => $code,
            );

            if ('dev' === WP_FATT_CLOUD) {
                $issuedDocumentResponse['bodyRequest'] = json_encode($body);
            }

            // Save
            $order->save();

            // Log
            \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($issuedDocumentResponse,
                    true));

            if (! $jobEvent) {
                \WcElectronInvoice\Functions\releaseDbLock($lockKey);
                wp_send_json($issuedDocumentResponse);
                die();
            } else {
                \WcElectronInvoice\Functions\releaseDbLock($lockKey);
                return true;
            }
        } catch (ApiException $e) {
            if (401 === $e->getCode()) {
                $dataException = array(
                    'title'     => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                    'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                        WP_FATT_CLOUD_TEXTDOMAIN),
                    'e-message' => $e->getMessage(),
                    'code'      => $e->getCode(),
                );
            } else {

                $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

                $dataException = array(
                    'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                    'orderID' => $order->get_id(),
                    'type'    => __('Exception when calling create:', WP_FATT_CLOUD_TEXTDOMAIN),
                    'message' => $message,
                    'body'    => $e->getResponseBody(),
                    'trace'   => $e->getTraceAsString(),
                    'code'    => $e->getCode(),
                );

                if ('dev' !== WP_FATT_CLOUD) {
                    $dataException['e-message'] = $e->getMessage();
                }
            }

            // Log
            \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($dataException, true));

            // Set errors meta
            $order->update_meta('wfc-create_invoice_errors', true);

            // Save
            $order->save();

            // Send error email
            $emailBody = __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                         __('Order: ', WP_FATT_CLOUD_TEXTDOMAIN) . '#' . $orderID . "\n" .
                         __('The invoice could not be created, we recommend that you check your order data and try again',
                             WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                         __('Errors:', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . $dataException['message'];
            sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);

            if (! empty($lockKey)) {
                \WcElectronInvoice\Functions\releaseDbLock($lockKey);
            }
            wp_send_json($dataException);
            die();
        }
    }

    $response = array(
        'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
        'body'    => null,
        'message' => __('CreateInvoice request error', WP_FATT_CLOUD_TEXTDOMAIN),
        'code'    => 501,
    );

    // Log
    \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} \n" . print_r($response, true));

    // Set errors meta
    $order->update_meta('wfc-create_invoice_errors', true);

    // Save
    $order->save();

    wp_send_json($response);
    die();
}

/**
 * verifyEInvoice
 *
 * @return bool|void
 */
function verifyEInvoice()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $jobEvent = filterInput($_POST, 'fcCreateInvoiceJobEvent', FILTER_UNSAFE_RAW);

    // auto refreshOAuthToken
    if (! $jobEvent && ! getActiveApi()->expireAccessToken || (int)getActiveApi()->expireAccessToken < time()) {
        $_POST['fcRefreshOAuthToken'] = true;

        refreshOAuthToken();

        // Log
        \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
    }

    $nonce    = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);
    $docID    = filterInput($_POST, 'docID', FILTER_UNSAFE_RAW);
    $id       = filterInput($_POST, 'id', FILTER_VALIDATE_INT);
    $provider = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    $userAndCompanies = getUserAndCompanies();

    // IssuedEInvoicesApi
    $issuedEInvoicesApi = Api::fattureInCloudIssuedEInvoicesApi();

    try {
        $verifyResult     = $issuedEInvoicesApi->verifyEInvoiceXml(
            (int)$userAndCompanies->companyID,
            (int)$docID
        );
        $verifyResultData = $verifyResult->getData();

        $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);
        if (is_wp_error($order)) {
            return;
        }

        if ($verifyResultData->valid()) {
            $order->update_meta('wfc-issued_valid_document', true);
            $message = esc_html__('The data in the document are formally correct.', WP_FATT_CLOUD_TEXTDOMAIN);
            $code    = 200;
        } else {
            $order->update_meta('wfc-issued_valid_document', false);
            $message = json_encode($verifyResultData->listInvalidProperties());
            $code    = 422;
        }

        // Save
        $order->save();

        $response = array(
            'title'   => '',
            'message' => $message,
            'code'    => $code,
        );

        // Slack notify
        \WooPoPToFattureInCloud\Functions\slack(
            WP_FATT_CLOUD_NAME . ' ' . $message . "\n" . json_encode($response),
            false,
            ':information_source:'
        );

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

        if (! $jobEvent) {
            wp_send_json($response);
            die();
        } else {
            return true;
        }
    } catch (ApiException $e) {
        if (401 === $e->getCode()) {
            $dataException = array(
                'title'     => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'e-message' => $e->getMessage(),
                'code'      => $e->getCode(),
            );
        } else {

            $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

            $dataException = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'orderID' => $id,
                'type'    => __('verifyEInvoice ApiException Message:', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => $message,
                'code'    => $e->getCode(),
                'trace'   => $e->getTraceAsString(),
                'e-body'  => $e->getResponseBody(),
            );

            // Send error email
            $emailBody = __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                         __('Order: ', WP_FATT_CLOUD_TEXTDOMAIN) . '#' . $id . "\n" .
                         __('The invoice could not be created, we recommend that you check your order data and try again',
                             WP_FATT_CLOUD_TEXTDOMAIN) . "\n" .
                         __('Message:',
                             WP_FATT_CLOUD_TEXTDOMAIN) . "\n" . $dataException['type'] . "\n" . $dataException['message'];
            sendEmailToAdminForInvoiceCreateError($emailBody, $id);
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * syncEInvoice
 *
 * @return void
 */
function syncEInvoice()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    // auto refreshOAuthToken
    if (! getActiveApi()->expireAccessToken || (int)getActiveApi()->expireAccessToken < time()) {
        $_POST['fcRefreshOAuthToken'] = true;

        refreshOAuthToken();

        // Log
        \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
    }

    $nonce    = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);
    $docID    = filterInput($_POST, 'docID', FILTER_UNSAFE_RAW);
    $id       = filterInput($_POST, 'id', FILTER_VALIDATE_INT);
    $provider = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    $userAndCompanies = getUserAndCompanies();

    // IssuedDocumentsApi
    $issuedDocumentsApi = Api::fattureInCloudIssuedDocumentsApi();

    try {
        $issuedDocumentInv     = $issuedDocumentsApi->getIssuedDocument(
            $userAndCompanies->companyID,
            $docID
        );
        $issuedDocumentInvData = $issuedDocumentInv->getData();

        $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);
        if (is_wp_error($order)) {
            return;
        }

        if ($issuedDocumentInv->valid()) {
            $prefix    = $issuedDocumentInvData->getNumeration();
            $invNumber = $issuedDocumentInvData->getNumber();
            $order->update_meta('wfc-issued_invoice_number', $invNumber);
            $message = esc_html__('Invoice number synchronized', WP_FATT_CLOUD_TEXTDOMAIN);
            $code    = 200;
        } else {
            $invNumber = '';
            $prefix    = '';
            $order->update_meta('wfc-issued_invoice_number', $invNumber);
            $message = json_encode($issuedDocumentInv->listInvalidProperties());
            $code    = 422;
        }

        // Save
        $order->save();

        $response = array(
            'title'     => '',
            'prefix'    => $prefix,
            'invNumber' => $invNumber,
            'message'   => $message,
            'code'      => $code,
        );

        // Slack notify
        \WooPoPToFattureInCloud\Functions\slack(
            WP_FATT_CLOUD_NAME . ' ' . $message . "\n" . json_encode($response),
            false,
            ':information_source:'
        );

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

        wp_send_json($response);
        die();
    } catch (ApiException $e) {
        if (401 === $e->getCode()) {
            $dataException = array(
                'title'     => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'e-message' => $e->getMessage(),
                'code'      => $e->getCode(),
            );
        } else {

            $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

            $dataException = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'type'    => __('IssuedEInvoicesApi ApiException Message:', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => $message,
                'code'    => $e->getCode(),
                'trace'   => $e->getTraceAsString(),
                'e-body'  => $e->getResponseBody(),
            );
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * sendInvoice
 *
 * @return bool|void
 */
function sendInvoice()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    if ('dev' === WP_FATT_CLOUD) {
        return;
    }

    $jobEvent = filterInput($_POST, 'fcCreateInvoiceJobEvent', FILTER_UNSAFE_RAW);

    // auto refreshOAuthToken
    if (! $jobEvent && ! getActiveApi()->expireAccessToken || (int)getActiveApi()->expireAccessToken < time()) {
        $_POST['fcRefreshOAuthToken'] = true;

        refreshOAuthToken();

        // Log
        \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
    }

    $nonce    = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);
    $docID    = filterInput($_POST, 'docID', FILTER_UNSAFE_RAW);
    $id       = filterInput($_POST, 'id', FILTER_VALIDATE_INT);
    $provider = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW);
    $docID    = (int)$docID;

    if (! Integrations::isInvoiceChannelActive(Integrations::CHANNEL_FIC)) {
        if (! $jobEvent) {
            wp_send_json(array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html__('Action not allowed: another integration channel is active.',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => 422,
            ));
            die();
        }

        return;
    }

    $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);
    if (! $order) {
        // Log
        \WooPoPToFattureInCloud\Functions\log('sendInvoice Order error: ' . "\n" . print_r(array(
                'order_id' => $id,
                'order'    => $order,
            ), true));

        return;
    }

    $validDoc = \WcElectronInvoice\Functions\getPostMeta('wfc-issued_valid_document', null, $id, true, 'order',
        $provider);
    if (! $validDoc) {
        return;
    }

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    $issuedDocumentID = (int)\WcElectronInvoice\Functions\getPostMeta('wfc-issued_document_id', null, $id, true,
        'order',
        $provider);
    $issuedSendDocument = \WcElectronInvoice\Functions\getPostMeta('wfc-issued_send_document', null, $id, true,
        'order',
        $provider);
    $checkSent          = \WcElectronInvoice\Functions\getPostMeta('_invoice_sent', null, $id, true, 'order',
        $provider);
    $sendDecision       = evaluateFicSendDecision(
        (int)$docID,
        (int)$issuedDocumentID,
        $issuedSendDocument,
        is_string($checkSent) ? $checkSent : null
    );
    $alreadySentOnFic   = \WcElectronInvoice\Functions\normalizeBooleanMeta($issuedSendDocument);

    if (! $sendDecision['allowed']) {
        $sendError = mapFicSendDecisionError($sendDecision['reason']);
        if ('doc_id_mismatch' === $sendDecision['reason']) {
            \WooPoPToFattureInCloud\Functions\log('sendInvoice - docID mismatch, abort send: ' . print_r([
                    'docID'            => $docID,
                    'issuedDocumentID' => $issuedDocumentID,
                    'orderID'          => $id,
                ], true));
        }

        if (! $jobEvent) {
            $message = esc_html__('Action not allowed.', WP_FATT_CLOUD_TEXTDOMAIN);
            if ('missing_issued_document' === $sendDecision['reason']) {
                $message = esc_html__('Missing issued document id, create the invoice first.', WP_FATT_CLOUD_TEXTDOMAIN);
            } elseif ('missing_doc_id' === $sendDecision['reason']) {
                $message = esc_html__('Missing document id parameter for send request.', WP_FATT_CLOUD_TEXTDOMAIN);
            } elseif ('doc_id_mismatch' === $sendDecision['reason']) {
                $message = esc_html__('Invalid document id for send request.', WP_FATT_CLOUD_TEXTDOMAIN);
            } elseif ('already_sent_fic' === $sendDecision['reason'] || 'already_sent_meta' === $sendDecision['reason']) {
                $message = esc_html__('The document has already been sent', WP_FATT_CLOUD_TEXTDOMAIN);
            }

            wp_send_json(array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => $message,
                'code'    => $sendError['code'],
            ));
            die();
        }

        return;
    }

    $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('fic', 'send_invoice', (int)$id, (string)$provider,
        (string)$docID);
    $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision(
        \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0),
        (bool)$jobEvent
    );
    if (! $lockDecision['continue']) {
        if ($lockDecision['respond_error']) {
            $lockError = \WcElectronInvoice\Functions\mapLockBusyError('fic', 'send_invoice');
            wp_send_json(array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => esc_html__('Action skipped: another send process is already running for this order.',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'code'    => $lockError['code'],
            ));
            die();
        }

        return;
    }

    $userAndCompanies = getUserAndCompanies();

    // IssuedEInvoicesApi
    $issuedEInvoicesApi = Api::fattureInCloudIssuedEInvoicesApi();

    try {
        $sendEInvoice = $issuedEInvoicesApi->sendEInvoice(
            (int)$userAndCompanies->companyID,
            (int)$docID
        );

        $sendEInvoiceData = $sendEInvoice->getData();

        $orderID = $order->get_id();
        if (canMarkInvoiceSentWithFicSend((bool)$sendEInvoice->valid(), (int)$docID,
                $alreadySentOnFic)) {
            // Log
            \WooPoPToFattureInCloud\Functions\log('sendInvoice Valid : ' . print_r($sendEInvoice->valid(), true));
            $order->update_meta('wfc-issued_send_document', true);
            $order->update_meta('_invoice_sent', 'sent');
            $message = esc_html__('Invoices taken over for sending to the SDI', WP_FATT_CLOUD_TEXTDOMAIN);
            $code    = 200;

            // Save
            $order->save();

            /**
             * Send the email with the invoice attached
             */
            // Send attachments via email ?
            $activeSendInvoiceEmail = OptionPage::init()->getOptions('invoice_via_email') ?: 'off';
            $sendingProforma        = \WcElectronInvoice\Functions\getPostMeta('wfc-sending_proforma', null,
                $id, true, 'order', $provider) ?: null;

            if (! $sendingProforma && 'off' !== $activeSendInvoiceEmail) {
                \WooPoPToFattureInCloud\Functions\log('Init sendingProforma');
                $useFicPdf = get_option('wfc_use_fic_pdf');

                // Initialized
                $response  = array();
                $pdfOutput = null;

                // Invoice or Receipt
                $choiceTypeModifier = $order->get_billing_choice_type();
                if ($order instanceof \WC_Order && 'receipt' === $choiceTypeModifier) {
                    $type = 'receipt';
                } else {
                    $type = 'invoice';
                }

                if ('on' === $useFicPdf) {
                    // Fic PDF
                    $documentPdfUrl = \WcElectronInvoice\Functions\getPostMeta('wfc-issued_document_pdf_url', null,
                        $order->get_id(), true, 'order', $provider) ?: null;
                    if ($documentPdfUrl) {
                        $pdfRemote = wp_remote_fopen(esc_url_raw($documentPdfUrl));
                        if ($pdfRemote) {
                            $pdfOutput = $pdfRemote;
                        }
                    }
                } else {
                    // Create PDF
                    $key   = OptionPage::getK();
                    $nonce = wp_create_nonce('wc_el_inv_invoice_pdf');

                    $url       = esc_url_raw(add_query_arg([
                        CreateXml::LIST_TYPE => $orderID,
                        'provider'           => $provider,
                        'key'                => OptionPage::getK(),
                        'format'             => 'pdf',
                        'nonce'              => $nonce,
                        'choice_type'        => $type,
                        'v'                  => time(),
                    ], site_url('/' . \WcElectronInvoice\EndPoint\Endpoints::ENDPOINT)));
                    $pdfOutput = wp_remote_get($url, \WcElectronInvoice\Functions\getSessionCookieArgs());
                }

                if (! $pdfOutput) {
                    // Set error PDH message
                    $message   = __('Error in generating the PDF, download it from your reserved area, or ask the seller for it',
                        WC_EL_INV_TEXTDOMAIN);
                    $pdfOutput = $message;
                }

                if ('woocommerce' === $provider) {
                    if ($pdfOutput) {
                        \WooPoPToFattureInCloud\Functions\log('Pdf exist');
                        if (! class_exists('WC_Email_Customer_Invoice')) {
                            \WooPoPToFattureInCloud\Functions\log('WC_Email_Customer_Invoice exist');
                            $WC   = \WooCommerce::instance();
                            $path = $WC->plugin_path();
                            require_once $path . '/includes/emails/class-wc-email.php';
                            require_once $path . '/includes/emails/class-wc-email-customer-invoice.php';

                            // Set attachments
                            add_filter('woocommerce_email_attachments',
                                function ($attachments, $emailID, $order, $wcEmail) use ($pdfOutput) {
                                    // Put PDF output
                                    $pdfPath         = \WooPoPToFattureInCloud\Plugin::getPluginDirPath('/tempPdf/invoice.pdf');
                                    $addedPdfContent = file_put_contents($pdfPath, $pdfOutput);
                                    if ($pdfPath && false !== $addedPdfContent) {
                                        $attachments[] = \WooPoPToFattureInCloud\Plugin::getPluginDirPath('/tempPdf') . '/invoice.pdf';
                                    }

                                    \WooPoPToFattureInCloud\Functions\log('WC Email attachment');

                                    return $attachments;
                                }, PHP_INT_MAX, 4);

                            if ($order instanceof \WC_Order) {
                                \WooPoPToFattureInCloud\Functions\log('Init WC Customer Invoice Email');
                                // Init WC Customer Invoice Email
                                $invoiceEmail = new \WC_Email_Customer_Invoice();
                                \WooPoPToFattureInCloud\Functions\log('WC Customer Invoice Email' . print_r($invoiceEmail,
                                        true));
                                $invoiceEmail->object = $order;
                                $invoiceEmail->trigger($order->get_id(), $order);
                                $order->update_meta_data('wfc-sending_proforma', true);
                                $response['emailSending'] = 1;
                                $response['message']      = __('Sent PDF Pro Forma', WP_FATT_CLOUD_TEXTDOMAIN);

                                // Save
                                $order->save();
                            }

                            if (1 === $response['emailSending']) {
                                $message = $message . '<br>' . $response['message'];
                            }
                        }
                    } else {
                        \WooPoPToFattureInCloud\Functions\log("Pdf not exist:\n" . print_r($pdfOutput, true));
                    }
                }
            }
        } else {
            // Log
            \WooPoPToFattureInCloud\Functions\log('sendInvoice Valid : ' . print_r($sendEInvoice->valid(), true));
            $order->update_meta('wfc-issued_send_document', false);
            $order->update_meta('_invoice_sent', 'no_sent');
            $message = json_encode($sendEInvoiceData->listInvalidProperties());
            $code    = 422;

            // Save
            $order->save();
        }

        $response = array(
            'title'   => '',
            'message' => $message,
            'code'    => $code,
        );

        // Slack notify
        $slackMessage = str_replace('<br>', ' - ', $message);
        \WooPoPToFattureInCloud\Functions\slack(
            WP_FATT_CLOUD_NAME . ' ' . $slackMessage . "\n" . json_encode($response),
            false,
            ':information_source:'
        );

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

        if (! $jobEvent) {
            \WcElectronInvoice\Functions\releaseDbLock($lockKey);
            wp_send_json($response);
            die();
        } else {
            \WcElectronInvoice\Functions\releaseDbLock($lockKey);
            return true;
        }
    } catch (ApiException $e) {

        if (401 === $e->getCode()) {
            $dataException = array(
                'title'     => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                'e-message' => $e->getMessage(),
                'code'      => $e->getCode(),
            );
        } else {

            $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

            $dataException = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
                'type'    => __('IssuedEInvoicesApi ApiException Message:', WP_FATT_CLOUD_TEXTDOMAIN),
                'message' => $message,
                'code'    => $e->getCode(),
                'trace'   => $e->getTraceAsString(),
                'e-body'  => $e->getResponseBody(),
            );
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        \WcElectronInvoice\Functions\releaseDbLock($lockKey);
        wp_send_json($dataException);
        die();
    }
}

/**
 * deleteInvoice
 *
 * @return void
 */
function deleteInvoice()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    // auto refreshOAuthToken
    if (getActiveApi()->expireAccessToken || (int)getActiveApi()->expireAccessToken < time()) {
        $_POST['fcRefreshOAuthToken'] = true;

        refreshOAuthToken();

        // Log
        \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
    }

    $nonce    = filterInput($_POST, 'nonce', FILTER_UNSAFE_RAW);
    $docID    = filterInput($_POST, 'docID', FILTER_UNSAFE_RAW);
    $id       = filterInput($_POST, 'id', FILTER_UNSAFE_RAW);
    $provider = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW);

    $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);

    if (is_wp_error($order)) {
        return;
    }

    $receiptID = \WcElectronInvoice\Functions\getPostMeta('wfc-receipt_document_id', null, $id, true, 'order',
        $provider);
    // Document type stored in order
    $orderChoiceType = \WcElectronInvoice\Functions\getPostMeta('_billing_choice_type', null, $id, true, 'order',
        $provider) ?: 'invoice';

    $response = array(
        'title'   => '',
        'message' => null,
        'code'    => null,
    );

    if (! wp_verify_nonce($nonce, 'wfc_scripts-ajax_nonce')) {
        return;
    }

    $userAndCompanies = getUserAndCompanies();

    // IssuedDocumentsApi
    $issuedDocumentsApi = Api::fattureInCloudIssuedDocumentsApi();

    try {
        if ('invoice' === $orderChoiceType) {
            // Delete invoice
            $issuedDocumentsApi->deleteIssuedDocument(
                (int)$userAndCompanies->companyID,
                (int)$docID
            );
            // Delete invoice document meta id
            $order->delete_meta("wfc-issued_document_id");
            $order->delete_meta("wfc-issued_invoice_number");
            $order->delete_meta("wfc-issued_document_pdf_url");

            if ('shop_order_refund' === $order->get_type()) {
                $refundOrderID = $order->get_id();
                $parentOrderID = $order->get_parent_id();
                $order         = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($parentOrderID,
                    $provider);
                $order->delete_meta("wfc-issued_credit_note_{$refundOrderID}_document_id");
                $order->delete_meta("wfc-issued_credit_note_{$refundOrderID}_invoice_number");
                $order->delete_meta("wfc-issued_credit_note_{$refundOrderID}_document_pdf_url");
            }

            $order->delete_meta("wfc-issued_valid_document");
            $order->delete_meta('wfc-issued_send_document');

            $response = array(
                'title'   => '',
                'message' => sprintf(esc_html__('Document ID %s successfully deleted', WP_FATT_CLOUD_TEXTDOMAIN),
                    $docID),
                'code'    => 200,
            );
        }

        if ('receipt' === $orderChoiceType) {
            // Delete receipt
            if ($receiptID && 'write' === $userAndCompanies->companyPermissions['issued_receipts']) {
                $issuedDocumentsApi->deleteIssuedDocument(
                    (int)$userAndCompanies->companyID,
                    (int)$receiptID
                );
                // Delete receipt document meta id
                $order->delete_meta("wfc-issued_document_id");
                $order->delete_meta("wfc-issued_invoice_number");
                $order->delete_meta("wfc-receipt_document_id");
                $order->delete_meta("wfc-issued_send_document");

                $response = array(
                    'title'   => '',
                    'message' => sprintf(esc_html__('Document ID %s successfully deleted', WP_FATT_CLOUD_TEXTDOMAIN),
                        $receiptID),
                    'code'    => 200,
                );
            }

            /**
             * After receipt deleteIssuedDocument
             */
            do_action('wfc-delete_receipt');

            /**
             * Filter message
             */
            apply_filters('wfc-delete_receipt_message', $response['message']);
        }

        $order->delete_meta('wfc-issued_document_pdf_url');
        $order->delete_meta('wfc-sending_proforma');

        // Slack notify
        \WooPoPToFattureInCloud\Functions\slack(
            WP_FATT_CLOUD_NAME . ' - ' . $response['message'],
            false,
            ':information_source:'
        );

        // Save
        $order->save();

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

        wp_send_json($response);
        die();
    } catch (ApiException $e) {
        // Not found
        if (404 === $e->getCode()) {
            if ('invoice' === $orderChoiceType) {
                // Delete invoice document meta id
                $order->delete_meta("wfc-issued_document_id");
                $order->delete_meta("wfc-issued_invoice_number");
                $order->delete_meta("wfc-issued_valid_document");
                $order->delete_meta('wfc-issued_send_document');
            } elseif ('receipt' === $orderChoiceType) {
                // Delete receipt document meta id
                $order->delete_meta("wfc-issued_document_id");
                $order->delete_meta("wfc-issued_invoice_number");
                $order->delete_meta("wfc-receipt_document_id");
                $order->delete_meta("wfc-issued_send_document");
            }

            $order->delete_meta('wfc-issued_document_pdf_url');

            // Save
            $order->save();
        }

        $message = getApiErrorMessageFromResponseBody($e->getResponseBody());

        $dataException = array(
            'title'   => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN),
            'type'    => __('Exception when calling delete:', WP_FATT_CLOUD_TEXTDOMAIN),
            'message' => $message,
            'body'    => $e->getResponseBody(),
            'trace'   => $e->getTraceAsString(),
            'code'    => $e->getCode(),
        );

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}
