<?php
/**
 * Utils.php
 *
 * @since      1.0.0
 * @author     alfiopiccione <alfio.piccione@gmail.com>

 * @license    http://opensource.org/licenses/gpl-2.0.php GPL v2
 *
 * Copyright (C) 2025 alfiopiccione <alfio.piccione@gmail.com>
 *
 * 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 WooPoPToFattureInCloud\Plugin;
use function WcElectronInvoice\Functions\getPaymentMethodInvoiceCode;

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

/**
 * Is WooCommerce active
 *
 * @return bool If the function WC exists.
 * @since 1.0.0
 *
 */
function isWooCommerceActive()
{
    return function_exists('WC');
}

/**
 * Filter Input
 *
 * @param array  $data    The haystack of the elements.
 * @param string $key     The key of the element within the haystack to filter.
 * @param int    $filter  The filter to use.
 * @param array  $options The option for the filter var.
 *
 * @return bool|mixed The value filtered on success false if filter fails or key doesn't exists.
 * @uses  filter_var() To filter the value.
 *
 * @since 1.0.0
 *
 */
function filterInput($data, $key, $filter = FILTER_DEFAULT, $options = array())
{
    if ((phpversion() >= 8.1) && $filter === FILTER_UNSAFE_RAW) {
        $filter = FILTER_UNSAFE_RAW;
    }

    return isset($data[$key]) ? filter_var($data[$key], $filter, $options) : false;
}

/**
 * Filter Input Array
 *
 * @param array  $data An array with string keys containing the data to filter.
 * @param array  $args An array defining the arguments. A valid key is a string
 *                     containing a variable name and a valid value is either a
 *                     filter type, or an
 *                     array optionally specifying the filter, flags and options.
 *                     If the value is an array, valid keys are filter
 *                     which specifies the filter type,
 *                     flags which specifies any flags that apply to the
 *                     filter, and options which specifies any options that
 *                     apply to the filter. See the example below for a better understanding.
 * @param string $key  The key of the element within the haystack to filter.
 *
 * @return bool|mixed
 */
function filterInputArray($data, $args, $key)
{
    return isset($data[$key]) ? filter_var_array($data, $args) : false;
}

/**
 * Is Ajax Request
 *
 * Define that this is a request made via ajax.
 * It's not the traditional WordPress ajax request, infact the DOING_AJAX is not set.
 * It's a convention that the argument passed with the ajaxAction query are made by an ajax request.
 *
 * @return bool
 * @since 1.0.0
 *
 */
function isAjaxRequest()
{
    // @codingStandardsIgnoreLine
    $action = filterInput($_POST, 'ajaxAction', FILTER_UNSAFE_RAW);

    // No ajax request ?
    if (empty($_POST) && ! (bool)$action &&
        ! defined('DOING_AJAX') ||
        (defined('DOING_AJAX') && ! DOING_AJAX)
    ) {
        return false;
    }

    return true;
}

/**
 * Extract a readable API error message from a Fatture in Cloud response body.
 *
 * @param string $body Raw response body.
 *
 * @return string
 */
function getApiErrorMessageFromResponseBody($body)
{
    $json = json_decode($body, true);

    if (! is_array($json) || empty($json['error']) || ! is_array($json['error'])) {
        return $body;
    }

    $messages = array();

    if (! empty($json['error']['validation_result']) && is_array($json['error']['validation_result'])) {
        foreach ($json['error']['validation_result'] as $errors) {
            if (is_array($errors)) {
                foreach ($errors as $error) {
                    if (is_string($error) && '' !== trim($error)) {
                        $messages[] = trim($error);
                    }
                }
            } elseif (is_string($errors) && '' !== trim($errors)) {
                $messages[] = trim($errors);
            }
        }
    }

    if (! empty($messages)) {
        return implode('; ', array_unique($messages));
    }

    if (! empty($json['error']['message']) && is_string($json['error']['message'])) {
        return trim($json['error']['message']);
    }

    return $body;
}

/**
 * Is Associative Array
 *
 * @param array $array The array to know if is associative or not.
 *
 * @return bool         True if array is associative, false otherwise.
 * @since  1.0.0
 *
 */
function isArrayAssoc(array $array)
{
    return array_keys($array) !== range(0, count($array) - 1);
}

/**
 * Implode Assoc
 *
 * Implode an associative array
 *
 * @param string $glue    The string to use for separate the elements value.
 * @param string $assGlue The string to use to separate key and value.
 * @param array  $arr     The array to implode.
 *
 * @return string|bool The array imploded. False if $arr is not an array. False if $arr is not associative.
 * @since 1.0.0
 *
 */
function implodeAssoc($glue, $assGlue, $arr)
{
    if (wp_is_numeric_array($arr)) {
        return false;
    }

    // The string.
    $string = '';

    foreach ($arr as $k => $v) {
        if (! $v) {
            continue;
        }

        $string .= $k . $assGlue . $v . $glue;
    }

    // Remove the latest glue string.
    $string = rtrim($string, $glue);

    return $string;
}

/**
 * Insert an element into array in a specific position
 *
 * @param array $needle    The array to insert in.
 * @param array $haystack  The array to insert on.
 * @param mixed $pos       The key or index where the array should be inserted.
 * @param bool  $preserve  If the original value and positions should be preserved.
 * @param bool  $recursive To merge recursively or not the result array.
 *
 * @return array            The new merged array
 * @since  1.0.0
 *
 */
function arrayInsertInPos($needle, array &$haystack, $pos, $preserve = false, $recursive = false)
{
    $keys = array_filter(array_intersect(array_keys($needle), array_keys($haystack)), 'is_string');

    if (is_array($pos)) {
        list($key, $before) = $pos;
        $before = (isset($before) && true === $before) ? true : false;
        $pos    = array_search($key, array_keys($haystack), true);
        $pos    = $before ? $pos : $pos + 1;
    }

    if ($keys) {
        if ($preserve) {
            $arr =& $needle;
        } else {
            $arr =& $haystack;
        }

        foreach ($keys as $k => $v) {
            unset($arr[$v]);
        }
    }

    $start    = array_splice($haystack, 0, (int)$pos);
    $func     = $recursive ? 'array_merge_recursive' : 'array_merge';
    $haystack = call_user_func_array($func, array($start, (array)$needle, $haystack));

    return $haystack;
}

/**
 * Helper for get Wc Order class name
 *
 * @param $obj
 * @param $classname
 *
 * @return string
 *
 */
function wcOrderClassName($obj, $classname)
{
    if (isWooCommerceActive() && \WC()->version >= '4.0.1') {
        if ('\WC_Order' === $classname) {
            if ($obj instanceof \WC_Order) {
                return '\WC_Order';
            } elseif ($obj instanceof \Automattic\WooCommerce\Admin\Overrides\Order) {
                return '\Automattic\WooCommerce\Admin\Overrides\Order';
            }
        } elseif ('\WC_Order_Refund' === $classname) {
            if ($obj instanceof \WC_Order_Refund) {
                return '\WC_Order_Refund';
            } elseif ($obj instanceof \Automattic\WooCommerce\Admin\Overrides\OrderRefund) {
                return '\Automattic\WooCommerce\Admin\Overrides\OrderRefund';
            }
        } else {
            return $classname;
        }
    }

    return $classname;
}

/**
 * getLogConfig
 *
 * @return array
 */
function getLogConfig()
{
    $capability = property_exists(Plugin::class, 'capability') ? Plugin::$capability : 'manage_options';

    return array(
        'key'          => 'wfc',
        'enabled'      => defined('WP_FATT_CLOUD_LOG') && WP_FATT_CLOUD_LOG,
        'plugin_name'  => WP_FATT_CLOUD_NAME,
        'capability'   => $capability,
        'log_dir_slug' => 'pop-to-fattureincloud-log',
    );
}

/**
 * Check
 */
function addonOptions()
{
    $page = filterInput($_GET, 'page', FILTER_UNSAFE_RAW);
    $tab  = filterInput($_GET, 'tab', FILTER_UNSAFE_RAW);

    if ('wc_el_inv-options-page' === $page && 'addon' === $tab) {
        add_action('wc_el_inv-before_closing_settings_form', function () {
            if (! current_user_can(Plugin::$capability)) {
                echo sprintf('<p style="color:red">%s</p>',
                    esc_html__('POP Addon: You do not have the permissions to manage the add-on', WP_FATT_CLOUD_TEXTDOMAIN)
                );

                return;
            }

            $valueOrderStatus       = get_option('wfc_scheduled_order_status', 'completed');
            $valueCronJob           = get_option('wfc_scheduled_events');
            $valueCronJobTwiceDaily = get_option('wfc_scheduled_twicedaily_events');
            $valueReceiptCronJob    = get_option('wfc_scheduled_receipt_events');
            $disabledSending        = get_option('wfc_disabled_sending_sdi');
            $useFicPdf              = get_option('wfc_use_fic_pdf');
            $invoiceNotes           = get_option('wfc_invoice_notes');
            $invoiceShippingAddress = get_option('wfc_invoice_shipping_address');
            $sendErrorsEmail        = get_option('wfc_error_sending_invoice');
            $valueCronJobTime       = get_option('wfc_scheduled_events_time') ?: 12;
            $valueRevenueCenter     = get_option('wfc_revenue_center') ?: '';
            $valueVatKind           = get_option('wfc_data_vat_kind') ?: '';

            $useFicPdf              = 'on' === $useFicPdf ? 'checked="checked"' : '';
            $sendErrorsEmail        = 'on' === $sendErrorsEmail ? 'checked="checked"' : '';
            $valueCronJob           = 'on' === $valueCronJob ? 'checked="checked"' : '';
            $disabledSending        = 'on' === $disabledSending ? 'checked="checked"' : '';
            $valueCronJobTwiceDaily = 'on' === $valueCronJobTwiceDaily ? 'checked="checked"' : '';
            $valueReceiptCronJob    = 'on' === $valueReceiptCronJob ? 'checked="checked"' : '';
            $invoiceNotes           = 'on' === $invoiceNotes ? 'checked="checked"' : '';
            $invoiceShippingAddress = 'on' === $invoiceShippingAddress ? 'checked="checked"' : '';

            $out = '';
            $out .= '<!-- addon-header --><div class="addon-header">';
            $out .= sprintf('<h2>%s - v:%s</h2>',
                __('POP Addon: Fatture In Cloud', WP_FATT_CLOUD_TEXTDOMAIN),
                WP_FATT_CLOUD_VERSION
            );
            $out .= '</div><!--/ addon-header -->';
            $out .= '<!-- addon --><div class="addon">';

            // POP FIC connect info
            $out .= '<!-- info --><div class="info config">';
            $out .= sprintf('<h3><strong>%s</strong></h3>',
                __('How to connect Fatture In Cloud with POP ', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p class="color-red"><strong>%s</strong> <small>%s</small></p>',
                __('Preliminary settings:', WP_FATT_CLOUD_TEXTDOMAIN),
                __('Make sure that they have been created in your Fatture In Cloud account', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<ol><li>%s</li><li>%s</li></ol>',
                __('Go to <b>settings > balance account</b> and create a balance account, eg: <b>"Standard account"</b>',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                __('Go to <b>settings > payment methods</b> and create these basic methods: <b>(Wire transfer, Check, Cash, Payment card)</b>',
                    WP_FATT_CLOUD_TEXTDOMAIN)
            );
            $out .= '<hr>';
            $out .= sprintf('<p><strong>%s</strong></p>',
                __('Connect POP to Fatture In Cloud:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<ol><li>%s</li><li>%s</li></ol>',
                __('<b>By clicking on Connect</b>, a popup will appear with a <b>code and a link</b> to authorize the connection. When you click, a page will open where you will have to authorize the connection by entering the code in the popup <u>(do not close the popup until the procedure is complete)</u>',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                __('Once the connection procedure with Fatture In Cloud is finished, <b>go back to this page</b> where you will find the answer of <b>"Access Token successful!"</b>',
                    WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= '<hr>';

            // General info
            $out .= sprintf('<p class="notification_error"><span class="dashicons dashicons-info"></span> <strong>%s</strong></p>',
                __('Si consiglia di verificare con molta attenzione che i dati per la Fatturazione elettronica inviati a
                        Fattura in Cloud siano corretti. L\'autore del plugin declina ogni responsabilità per errori o mancanze nella generazione della Fattura elettronica.',
                    WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= '</div><!--/ info config -->';

            $out .= '<!-- info --><div class="info">';
            // Authentication data
            $out .= sprintf('<h4>%s</h4>',
                __('Fatture In Cloud Authentication data:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p>%s - %s</p><p><code style="word-break: break-all;">define("WP_FATT_CLOUD_DELETE_DATA_WHEN_DEACTIVATE", false);</code><br><small>%s</small></p>',
                __('Every time the plugin is deactivated, the authentication data is removed.',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                __('Do you want to keep data in memory?', WP_FATT_CLOUD_TEXTDOMAIN),
                __('Place this code in your wp-config.php file', WP_FATT_CLOUD_TEXTDOMAIN)
            );

            // Automations
            $out .= sprintf('<h4>%s</h4>',
                __('Automations:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= '<input type="hidden" name="wfc_options" value="1">';

            // ---- Fatture ----------------------------------------- //
            $out .= sprintf('<h5>%s</h5>', __('Fatture', WP_FATT_CLOUD_TEXTDOMAIN));

            $statuses = apply_filters('wfc-cron_job_statuses', [
                'completed'  => __('Completed', WP_FATT_CLOUD_TEXTDOMAIN),
                'processing' => __('Processing', WP_FATT_CLOUD_TEXTDOMAIN),
            ]);
            $selectedValue = $valueOrderStatus ?? 'completed';
            $out .= '<p><label for="wc_el_inv-settings-wfc_scheduled_order_status">' .
                    __('Enable auto-send for status:', WP_FATT_CLOUD_TEXTDOMAIN) .
                    '<select id="wc_el_inv-settings-wfc_scheduled_order_status" name="wc_el_inv_options[wc_el_inv-settings-wfc_scheduled_order_status]">';
            foreach ($statuses as $slug => $label) {
                $selected_attr = selected($slug, $selectedValue, false);
                $out           .= '<option value="' . esc_attr($slug) . '" ' . $selected_attr . '>' . esc_html($label) . '</option>';
            }
            $out .= '</select></label></p>';

            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_scheduled_events">' .
                            '<input id="wc_el_inv-settings-wfc_scheduled_events" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_scheduled_events]" value="on" %s>' .
                            '%s </label></p>', $valueCronJob,
                __('Enable automatic sending', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_scheduled_twicedaily_events">' .
                            '<input id="wc_el_inv-settings-wfc_scheduled_twicedaily_events" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_scheduled_twicedaily_events]" value="on" %s>' .
                            '%s </label></p>', $valueCronJobTwiceDaily,
                __('Enable automatic sending of invoices for completed orders not sent in the last 7 days. (work 2 times a day)',
                    WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_disabled_sending_sdi">' .
                            '<input id="wc_el_inv-settings-wfc_disabled_sending_sdi" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_disabled_sending_sdi]" value="on" %s>' .
                            '%s </label><br><small>%s</small></p>', $disabledSending,
                __('Disables the automatic sending of invoices to the SDI', WP_FATT_CLOUD_TEXTDOMAIN),
                __('if active, the invoice / receipt documents will be created but the invoices will not be sent to the SDI',
                    WP_FATT_CLOUD_TEXTDOMAIN)
            );
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_scheduled_events_time"> %s<br>' .
                            '<input id="wc_el_inv-settings-wfc_scheduled_events_time" type="number" name="wc_el_inv_options[wc_el_inv-settings-wfc_scheduled_events_time]" value="%s">' .
                            '</label></p>',
                __('Time in hours for automatic sending of the invoice', WP_FATT_CLOUD_TEXTDOMAIN),
                $valueCronJobTime
            );

            // ---- Ricevute/Corrispettivi --------------------------- //
            $companyPermissions  = get_option('wfc_company_details_permissions', []);
            $receiptsPermission  = isset($companyPermissions['receipts']) ? $companyPermissions['receipts'] : 'none';
            $issuedRecPermission = isset($companyPermissions['issued_receipts']) ? $companyPermissions['issued_receipts'] : 'none';
            if ('none' !== $receiptsPermission || 'none' !== $issuedRecPermission) {
                $out .= sprintf('<h5>%s</h5>', __('Ricevute/Corrispettivi', WP_FATT_CLOUD_TEXTDOMAIN));

                $receiptStatuses = apply_filters('wfc-receipt_cron_job_statuses', [
                    'completed'  => __('Completed', WP_FATT_CLOUD_TEXTDOMAIN),
                    'processing' => __('Processing', WP_FATT_CLOUD_TEXTDOMAIN),
                    'on-hold'    => __('On hold', WP_FATT_CLOUD_TEXTDOMAIN),
                    'pending'    => __('Pending payment', WP_FATT_CLOUD_TEXTDOMAIN),
                ]);
                $selectedReceiptValue = get_option('wfc_scheduled_receipt_order_status', 'completed');
                $out .= '<p><label for="wc_el_inv-settings-wfc_scheduled_receipt_order_status">' .
                        __('Enable auto-send for status:', WP_FATT_CLOUD_TEXTDOMAIN) .
                        '<select id="wc_el_inv-settings-wfc_scheduled_receipt_order_status" name="wc_el_inv_options[wc_el_inv-settings-wfc_scheduled_receipt_order_status]">';
                foreach ($receiptStatuses as $slug => $label) {
                    $selected_attr = selected($slug, $selectedReceiptValue, false);
                    $out           .= '<option value="' . esc_attr($slug) . '" ' . $selected_attr . '>' . esc_html($label) . '</option>';
                }
                $out .= '</select></label></p>';

                $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_scheduled_receipt_events">' .
                                '<input id="wc_el_inv-settings-wfc_scheduled_receipt_events" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_scheduled_receipt_events]" value="on" %s>' .
                                '%s </label></p>', $valueReceiptCronJob,
                    __('Enable automatic sending', WP_FATT_CLOUD_TEXTDOMAIN));
                $out .= sprintf('<p><small>%s</small></p>',
                    __('When enabled, receipts/corrispettivi are sent immediately when the order reaches the selected status.',
                        WP_FATT_CLOUD_TEXTDOMAIN));
            }
            // ------------------------------------------------------ //

            $out .= '</div><!--/ info -->';

            $out .= '<!-- info --><div class="info">';
            // Email
            $out .= sprintf('<h4>%s</h4>',
                __('Email:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_error_sending_invoice">' .
                            '<input id="wc_el_inv-settings-wfc_error_sending_invoice" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_error_sending_invoice]" value="on" %s>' .
                            '%s <b>(%s)</b> %s</label></p>',
                $sendErrorsEmail,
                __('Send an email to the WordPress installation administrator', WP_FATT_CLOUD_TEXTDOMAIN),
                get_bloginfo('admin_email'),
                __('to report the failure to create the document on Fatture in Cloud', WP_FATT_CLOUD_TEXTDOMAIN)
            );
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_use_fic_pdf">' .
                            '<input id="wc_el_inv-settings-wfc_use_fic_pdf" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_use_fic_pdf]" value="on" %s>' .
                            '%s </label></p>',
                $useFicPdf,
                __('Use the "Fatture in Cloud" PDF document as an attachment to the Invoice email',
                    WP_FATT_CLOUD_TEXTDOMAIN)
            );

            // Invoice note
            $out .= sprintf('<h4>%s</h4>',
                __('Invoice notes:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_invoice_notes">' .
                            '<input id="wc_el_inv-settings-wfc_invoice_notes" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_invoice_notes]" value="on" %s>' .
                            '%s </label></p>',
                $invoiceNotes,
                __('Enables the insertion of the notes inserted during the checkout phase on the invoice',
                    WP_FATT_CLOUD_TEXTDOMAIN)
            );

            // Invoice shipping
            $out .= sprintf('<h4>%s</h4>',
                __('Shipping address:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_invoice_shipping_address">' .
                            '<input id="wc_el_inv-settings-wfc_invoice_shipping_address" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_invoice_shipping_address]" value="on" %s>' .
                            '%s </label></p>',
                $invoiceShippingAddress,
                __('Enable the inclusion of the shipping address in the PDF invoice',
                    WP_FATT_CLOUD_TEXTDOMAIN)
            );

            // Revenue center
            $out .= sprintf('<h4>%s</h4>',
                __('Revenue center:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_revenue_center"> %s<br>' .
                            '<input id="wc_el_inv-settings-wfc_revenue_center" type="text" name="wc_el_inv_options[wc_el_inv-settings-wfc_revenue_center]" value="%s">' .
                            '</label></p>',
                __('Revenue centers can be used to group revenue related to a particular activity. (for example "e-commerce")',
                    WP_FATT_CLOUD_TEXTDOMAIN),
                $valueRevenueCenter
            );

            // Vat kind
            $out .= sprintf('<h4>%s</h4>',
                __('Vat kind:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_data_vat_kind">' .
                            '<select id="wc_el_inv-settings-wfc_data_vat_kind" name="wc_el_inv_options[wc_el_inv-settings-wfc_data_vat_kind]">
                                    <option ' . selected($valueVatKind, '', false) . ' value="">%s</option>
                                    <option ' . selected($valueVatKind, 'I', false) . ' value="I">%s</option>
                                    <option ' . selected($valueVatKind, 'D', false) . ' value="D">%s</option>
                                    <option ' . selected($valueVatKind, 'S', false) . ' value="S">%s</option>
                                </select></label></p>',
                __('Not defined (default)', WP_FATT_CLOUD_TEXTDOMAIN),
                __('Immediate', WP_FATT_CLOUD_TEXTDOMAIN),
                __('Deferred', WP_FATT_CLOUD_TEXTDOMAIN),
                __('Split payment', WP_FATT_CLOUD_TEXTDOMAIN)
            );
            $out .= '</div><!--/ info -->';

            $out .= '<!-- info --><div class="info">';
            // Event log
            if (WP_FATT_CLOUD_LOG) {
                $logConfig = getLogConfig();
                $logStatus = \WcElectronInvoice\Functions\prepareDiagnosticLog($logConfig, true);
                $out     .= sprintf('<h4>%s: <span style="color:#46b450">%s</span></h4>',
                    __('Event log ', WP_FATT_CLOUD_TEXTDOMAIN),
                    __('Activated', WP_FATT_CLOUD_TEXTDOMAIN)
                );

                $out .= sprintf('<p><code style="word-break: break-all;">%s</code></p>',
                    esc_html($logStatus['file_path'])
                );

                if (! empty($logStatus['error'])) {
                    $out .= sprintf('<p><small style="color:#b32d2e">%s</small></p>',
                        esc_html($logStatus['error'])
                    );
                }

                if ($logStatus['ready']) {
                    $out .= sprintf('<p><a target="_blank" class="log-link" href="%s"><span class="dashicons dashicons-text-page"></span> %s</a><hr></p>',
                        esc_url(\WcElectronInvoice\Functions\getDiagnosticLogViewerUrl($logConfig['key'])),
                        __('View the event log ', WP_FATT_CLOUD_TEXTDOMAIN)
                    );
                }
            } else {
                $out .= sprintf('<h4>%s:</h4>',
                    __('Event log ', WP_FATT_CLOUD_TEXTDOMAIN));
                $out .= sprintf('<p><code style="word-break: break-all;">define("WP_FATT_CLOUD_LOG", true);</code><br><small>%s</small></p>',
                    __('Enter this code in wp-config.php to activate the event log.', WP_FATT_CLOUD_TEXTDOMAIN)
                );
            }
            // Slack integration
            $valueCheck   = get_option('wfc_slack_integration');
            $valueToken   = get_option('wfc_slack_integration_token');
            $valueChannel = get_option('wfc_slack_integration_channel');
            $valueCheck   = 'on' === $valueCheck ? 'checked="checked"' : '';

            $out .= sprintf("<h4>%s</h4>", __('Slack configuration:', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf("<p>%s<br><strong>%s</strong><br></p>",
                __('Create Slack APP and set OAuth & Permissions Scopes', WP_FATT_CLOUD_TEXTDOMAIN),
                __('Required scopes: chat:write - chat:write.customize - incoming-webhook', WP_FATT_CLOUD_TEXTDOMAIN)
            );
            $out .= sprintf('<p><input type="hidden" name="wfc_options" value="1"><label for="wc_el_inv-settings-wfc_slack_integration">' .
                            '<input id="wc_el_inv-settings-wfc_slack_integration" type="checkbox" name="wc_el_inv_options[wc_el_inv-settings-wfc_slack_integration]" value="on" %s>' .
                            '%s </label></p>', $valueCheck,
                __('Enable slack integration notify', WP_FATT_CLOUD_TEXTDOMAIN));
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_slack_integration_token">%s<br>' .
                            '<input id="wc_el_inv-settings-wfc_slack_integration_token" type="text" name="wc_el_inv_options[wc_el_inv-settings-wfc_slack_integration_token]" value="%s">' .
                            '</label></p>', __('Slack API Token', WP_FATT_CLOUD_TEXTDOMAIN), $valueToken);
            $out .= sprintf('<p><label for="wc_el_inv-settings-wfc_slack_integration_channel">%s<br>' .
                            '<input id="wc_el_inv-settings-wfc_slack_integration_channel" type="text" name="wc_el_inv_options[wc_el_inv-settings-wfc_slack_integration_channel]" value="%s">' .
                            '</label></p>', __('Slack Channel', WP_FATT_CLOUD_TEXTDOMAIN), $valueChannel);
            $out .= '</div><!--/ info -->';

            $out .= '</div><!--/ addon -->';

            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo $out;
        }, 50);
    }
}

/**
 * Log
 *
 * @param $message
 */
function log($message)
{
    if (\WcElectronInvoice\Functions\writeDiagnosticLog($message, getLogConfig())) {
        \WooPoPToFattureInCloud\Functions\slack("DEBUG: " . trim($message));
    }
}


/**
 * Slack notify
 *
 * Slack configuration:
 * Create Slack APP
 *
 * - OAuth & Permissions - Scopes
 *
 * 1. chat:write
 * 2. chat:write.customize
 * 3. incoming-webhook
 *
 * Use: \POPToFattureInCloud\Functions\slack('message ....', 'channel_name');
 *
 * @param string $message      The message
 * @param string $slackChannel The Slack channel (debug is a developer channel)
 * @param string $emoji        The emoji
 * @param        $debugMessage
 *
 * @return bool|string
 */
function slack($message, $slackChannel = 'debug', $emoji = ':robot_face:', $debugMessage = '')
{
    $active  = get_option('wfc_slack_integration');
    $token   = get_option('wfc_slack_integration_token');
    $channel = get_option('wfc_slack_integration_channel');

    if ('dev' === WP_FATT_CLOUD) {
        $message = $message . "\n\n------------\n" . "Debug" . "\n------------\n" . $debugMessage;
    }

    if ($slackChannel) {
        $channel = $slackChannel;
    }
    $siteUrl = site_url('/');

    if ($channel && 'on' === $active && '' !== $token) {
        $ch   = curl_init("https://slack.com/api/chat.postMessage");
        $data = http_build_query([
            "username"   => WP_FATT_CLOUD_NAME . " Bot",
            "token"      => $token,
            // "#mychannel" channel ID,
            "channel"    => $channel,
            "text"       => $emoji . " WebSite: {$siteUrl} - " . date_i18n('d/m/Y H:i') . "\n" . $message,
            // :robot_face: - :warning: - :no_entry_sign: - :dart: - :boom: - :white_check_mark: - :information_source:
            "icon_emoji" => ":robot_face:",
        ]);

        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $result = curl_exec($ch);
        curl_close($ch);

        return $result;
    }

    return false;
}

/**
 * getPaymentMethodNameByCode
 *
 * @param $code
 *
 * @return mixed|void
 */
function getPaymentMethodNameByCode($code)
{
    // The payment methods must necessarily be created respecting these names as
    // they are used for the recovery of fundamental data for the XML invoice
    $method = array(
        'MP01' => esc_html__('Contanti', WP_FATT_CLOUD_TEXTDOMAIN),
        'MP02' => esc_html__('Assegno', WP_FATT_CLOUD_TEXTDOMAIN),
        'MP03' => esc_html__('Assegno circolare', WP_FATT_CLOUD_TEXTDOMAIN),
        'MP05' => esc_html__('Bonifico', WP_FATT_CLOUD_TEXTDOMAIN),
        'MP08' => esc_html__('Carta di pagamento', WP_FATT_CLOUD_TEXTDOMAIN),
    );

    return $method[$code] ?: apply_filters('wfc-payment_method',
        esc_html__('Bonifico', WP_FATT_CLOUD_TEXTDOMAIN)
    );
}

/**
 * getPaymentMethodCodeByMethod
 *
 * @param $method
 *
 * @return string
 */
function getPaymentMethodCodeByMethod($method)
{
    $code = getPaymentMethodInvoiceCode($method);

    return apply_filters('wfc-payment_method_code', $code, $method);
}

/**
 * getWCActiveGatewayCode
 *
 * @return array
 */
function getWCActiveGatewayCode()
{
    $gateways        = WC()->payment_gateways()->get_available_payment_gateways();
    $enabledGateways = array();
    if ($gateways) {
        foreach ($gateways as $code => $gateway) {
            if ($gateway->enabled == 'yes') {
                $enabledGateways[$code] = $gateway->title;
            }
        }
    }

    return $enabledGateways;
}

/**
 * getDocType
 *
 * @param $orderType string The order type
 *
 * @return mixed|null
 */
function getDocType(string $orderType)
{
    $scopes = get_option('wfc_api_scopes');

    switch ($orderType) {
        case 'shop_order_refund':
            $documentType = 'credit_note';
            break;
        default:
            // shop_order
            $documentType = 'invoice';
            break;
    }

    if (false !== strpos($scopes, 'issued_documents.orders:a')) {
        $documentType = 'order';
    }

    return apply_filters('wfc-document_type', $documentType, $orderType);
}

/**
 * Get key
 *
 * @param $string
 *
 * @return array|string|string[]|null
 */
function getKey($string)
{
    return preg_replace('/\s+/', '', strtolower($string));
}

/**
 * Normalize Fic Country
 *
 * @param $countryCode
 *
 * @return string|null
 */
function normalizeFicCountry($countryCode)
{
    $country = null;
    switch ($countryCode) {
        case 'AS':
            $country = "Saoma Americane";
            break;
        case 'AQ':
            $country = "Antartide";
            break;
        case 'AG':
            $country = "Antigua e Barbuda";
            break;
        case 'BA':
            $country = "Bosnia ed Erzegovina";
            break;
        case 'BV':
            $country = "Isola Bouvet";
            break;
        case 'BR':
            $country = "Brasile";
            break;
        case 'BE':
            $country = "Belgio";
            break;
        case 'IO':
            $country = "Terr. Britannico dell'Oc. Ind.";
            break;
        case 'KH':
            $country = "Cambogia";
            break;
        case 'CV':
            $country = "Capo Verde";
            break;
        case 'KY':
            $country = "Isole Cayman";
            break;
        case 'CF':
            $country = "Repubblica Centrafricana";
            break;
        case 'TD':
            $country = "Ciad";
            break;
        case 'CL':
            $country = "Cile";
            break;
        case 'CN':
            $country = "Cina";
            break;
        case 'CX':
            $country = "Isola Christmas";
            break;
        case 'CC':
            $country = "Isole Cocos e Keeling";
            break;
        case 'KM':
            $country = "Comore";
            break;
        case 'CG':
        case 'CD':
            $country = "Repubblica del Congo";
            break;
        case 'CK':
            $country = "Isole Cook";
            break;
        case 'HR':
            $country = "Croazia";
            break;
        case 'CY':
            $country = "Cipro";
            break;
        case 'CZ':
            $country = "Repubblica Ceca";
            break;
        case 'DK':
            $country = "Danimarca";
            break;
        case 'DJ':
            $country = "Gibuti";
            break;
        case 'DO':
            $country = "Repubblica Dominicana";
            break;
        case 'EG':
            $country = "Egitto";
            break;
        case 'GQ':
            $country = "Guinea Equatoriale";
            break;
        case 'ET':
            $country = "Etiopia";
            break;
        case 'FK':
            $country = "Isole Falkland";
            break;
        case 'FO':
            $country = "Isole Fær Øer";
            break;
        case 'FI':
            $country = "Finlandia";
            break;
        case 'FR':
            $country = "Francia";
            break;
        case 'GF':
            $country = "Guyana Francese";
            break;
        case 'PF':
            $country = "Polinesia Francese";
            break;
        case 'TF':
            $country = "Terre australi e ant. francesi";
            break;
        case 'DE':
            $country = "Germania";
            break;
        case 'GI':
            $country = "Gibilterra";
            break;
        case 'GR':
            $country = "Grecia";
            break;
        case 'GL':
            $country = "Groenlandia";
            break;
        case 'GP':
            $country = "Guadalupa";
            break;
        case 'GG':
            $country = "Guernsey - Channel Islands";
            break;
        case 'HM':
            $country = "Heard Island e McDonald Islands";
            break;
        case 'HU':
            $country = "Ungheria";
            break;
        case 'IS':
            $country = "Islanda";
            break;
        case 'IE':
            $country = "Irlanda";
            break;
        case 'IM':
            $country = "Isola di Man";
            break;
        case 'IL':
            $country = "Israele";
            break;
        case 'IT':
            $country = "Italia";
            break;
        case 'CI':
            $country = "Costa d'Avorio";
            break;
        case 'JM':
            $country = "Giamaica";
            break;
        case 'JP':
            $country = "Giappone";
            break;
        case 'JE':
            $country = "Jersey - Channel Islands";
            break;
        case 'JO':
            $country = "Giordania";
            break;
        case 'KZ':
            $country = "Kazakistan";
            break;
        case 'KE':
            $country = "Kenia";
            break;
        case 'KG':
            $country = "Kirghizistan";
            break;
        case 'LV':
            $country = "Lettonia";
            break;
        case 'LB':
            $country = "Libano";
            break;
        case 'LY':
            $country = "Libia";
            break;
        case 'LT':
            $country = "Lituania";
            break;
        case 'LU':
            $country = "Lussemburgo";
            break;
        case 'MK':
            $country = "Macedonia del Nord";
            break;
        case 'MV':
            $country = "Maldive";
            break;
        case 'MH':
            $country = "Isole Marshall";
            break;
        case 'MQ':
            $country = "Martinica";
            break;
        case 'MX':
            $country = "Messico";
            break;
        case 'MD':
            $country = "Moldavia";
            break;
        case 'MA':
            $country = "Marocco";
            break;
        case 'MZ':
            $country = "Mozambico";
            break;
        case 'NL':
            $country = "Paesi Bassi";
            break;
        case 'NC':
            $country = "Nuova Caledonia";
            break;
        case 'NZ':
            $country = "Nuova Zelanda";
            break;
        case 'NF':
            $country = "Isola Norfolk";
            break;
        case 'MP':
            $country = "Isole Marianne Settentrionali";
            break;
        case 'KP':
            $country = "Corea del Nord";
            break;
        case 'NO':
            $country = "Norvegia";
            break;
        case 'PS':
            $country = "Stato di Palestina";
            break;
        case 'PG':
            $country = "Papua Nuova Guinea";
            break;
        case 'PE':
            $country = "Perù";
            break;
        case 'PH':
            $country = "Filippine";
            break;
        case 'PN':
            $country = "Isole Pitcairn";
            break;
        case 'PL':
            $country = "Polonia";
            break;
        case 'PT':
            $country = "Portogallo";
            break;
        case 'PR':
            $country = "Porto Rico";
            break;
        case 'RE':
            $country = "Riunione";
            break;
        case 'RW':
            $country = "Ruanda";
            break;
        case 'BL':
            $country = "Saint Barthelemy";
            break;
        case 'SH':
            $country = "Sant'Elena";
            break;
        case 'KN':
            $country = "Saint Kitts e Nevis";
            break;
        case 'LC':
            $country = "Santa Lucia";
            break;
        case 'PM':
            $country = "Saint Pierre e Miquelon";
            break;
        case 'VC':
            $country = "Saint Vincent e Grenadine";
            break;
        case 'ST':
            $country = "São Tomé e Príncipe";
            break;
        case 'SA':
            $country = "Arabia Saudita";
            break;
        case 'SK':
            $country = "Slovacchia";
            break;
        case 'SB':
            $country = "Isole Salomone";
            break;
        case 'ZA':
            $country = "Sudafrica";
            break;
        case 'GS':
            $country = "Georgia del Sud e isole Sandwich";
            break;
        case 'KR':
            $country = "Corea del Sud";
            break;
        case 'SS':
            $country = "Sud Sudan";
            break;
        case 'ES':
            $country = "Spagna";
            break;
        case 'SZ':
            $country = "Swaziland";
            break;
        case 'SE':
            $country = "Svezia";
            break;
        case 'CH':
            $country = "Svizzera";
            break;
        case 'SY':
            $country = "Siria";
            break;
        case 'TJ':
            $country = "Tagikistan";
            break;
        case 'TH':
            $country = "Thailandia";
            break;
        case 'TL':
            $country = "Timor Est";
            break;
        case 'TT':
            $country = "Trinidad and Tobago";
            break;
        case 'TR':
            $country = "Turchia";
            break;
        case 'TC':
            $country = "Turks e Caicos";
            break;
        case 'UA':
            $country = "Ucraina";
            break;
        case 'AE':
            $country = "Emirati Arabi Uniti";
            break;
        case 'GB':
            $country = "Regno Unito";
            break;
        case 'US':
            $country = "Stati Uniti";
            break;
        case 'UM':
            $country = "Isole Minori Esterne degli USA";
            break;
        case 'VA':
            $country = "Città del Vaticano";
            break;
        case 'VG':
            $country = "Isole Vergini Britanniche";
            break;
        case 'VI':
            $country = "Isole Vergini Americane";
            break;
        case 'WF':
            $country = "Wallis e Futuna";
            break;
        case 'EH':
            $country = "Sahara Occidentale";
            break;
    }

    return $country;
}

/**
 * sendEmailToAdminForInvoiceCreateError
 *
 * @param $emailBody
 * @param $orderID
 *
 * @return void
 */
function sendEmailToAdminForInvoiceCreateError($emailBody, $orderID)
{
    $sendErrorsEmail = get_option('wfc_error_sending_invoice');
    $adminEmail      = get_bloginfo('admin_email');
    if ('on' === $sendErrorsEmail && function_exists('wp_mail')) {
        wp_mail($adminEmail,
            sprintf(__('Error creating invoice for order: #%s', WP_FATT_CLOUD_TEXTDOMAIN), $orderID),
            $emailBody
        );
    }
}

/**
 * percentageDiscount
 *
 * @param $initial
 * @param $discounted
 *
 * @return float|int|void
 */
function percentageDiscount($initial, $discounted)
{
    if ($discounted) {
        $percent = (($initial - $discounted) / $initial) * 100;

        if (0 === (int)$percent) {
            return 100;
        } else {
            return $percent;
        }
    }
}

/**
 * Normalize FIC VAT descriptions for stable comparisons.
 *
 * @param string|null $value
 *
 * @return string
 */
function normalizeFicVatDescription(?string $value): string
{
    $value = trim((string)$value);
    if ('' === $value) {
        return '';
    }

    $value = strtolower(remove_accents($value));
    $value = preg_replace('/[^a-z0-9]+/', '-', $value);

    return trim((string)$value, '-');
}

/**
 * Try to resolve a FIC VAT type id using VAT rate, nature and optional reference.
 *
 * @param array<int|string,mixed> $vatTypes
 * @param mixed                   $vatValue
 * @param string|null             $natureNumber
 * @param string|null             $refNormative
 *
 * @return int|null
 */
function findFicVatTypeIdByNature(
    array $vatTypes,
    $vatValue,
    ?string $natureNumber,
    ?string $refNormative = null
): ?int {
    $natureNumber = trim((string)$natureNumber);
    if ('' === $natureNumber) {
        return null;
    }

    $candidates = [];
    foreach ($vatTypes as $vat) {
        if (! is_array($vat)) {
            continue;
        }

        if (! isset($vat['id'], $vat['value'], $vat['ei_type'])) {
            continue;
        }

        if ((float)$vatValue !== (float)$vat['value']) {
            continue;
        }

        if ($natureNumber !== trim((string)$vat['ei_type'])) {
            continue;
        }

        $candidates[] = $vat;
    }

    if (empty($candidates)) {
        return null;
    }

    $normalizedRef = normalizeFicVatDescription($refNormative);
    if ('' !== $normalizedRef) {
        foreach ($candidates as $candidate) {
            if ($normalizedRef === normalizeFicVatDescription((string)($candidate['ei_description'] ?? ''))) {
                return (int)$candidate['id'];
            }
        }

        return null;
    }

    if (1 === count($candidates)) {
        return (int)$candidates[0]['id'];
    }

    return null;
}

/**
 * Try to resolve a FIC VAT type id by nature when the normative reference is missing.
 *
 * @param array<int|string,mixed> $vatTypes
 * @param mixed                   $vatValue
 * @param string|null             $natureNumber
 *
 * @return int|null
 */
function findFicVatTypeIdByNatureFallback(array $vatTypes, $vatValue, ?string $natureNumber): ?int
{
    $natureNumber = trim((string)$natureNumber);
    if ('' === $natureNumber) {
        return null;
    }

    $candidates = [];
    foreach ($vatTypes as $vat) {
        if (! is_array($vat) || ! isset($vat['id'], $vat['value'], $vat['ei_type'])) {
            continue;
        }

        if ((float)$vatValue !== (float)$vat['value']) {
            continue;
        }

        if ($natureNumber !== trim((string)$vat['ei_type'])) {
            continue;
        }

        $candidates[] = $vat;
    }

    if (empty($candidates)) {
        return null;
    }

    if (1 === count($candidates)) {
        return (int)$candidates[0]['id'];
    }

    $preferredDescriptions = [
        '2.2' => [
            'fuori-campo-iva',
        ],
    ];

    if (! empty($preferredDescriptions[$natureNumber])) {
        foreach ($preferredDescriptions[$natureNumber] as $preferredDescription) {
            foreach ($candidates as $candidate) {
                if ($preferredDescription === normalizeFicVatDescription((string)($candidate['ei_description'] ?? ''))) {
                    return (int)$candidate['id'];
                }
            }
        }
    }

    return null;
}

/**
 * Build the cached FIC VAT type map from a fresh API payload.
 *
 * The first row for a VAT value keeps the numeric key used by legacy lookups.
 * Additional rows for the same value are stored under a composite key so
 * nature-aware resolution can still inspect every candidate.
 *
 * @param array<int,mixed> $vatTypeRows
 *
 * @return array<int|string,array{id:int|null,value:mixed,ei_type:mixed,ei_description:mixed}>
 */
function buildFicVatTypeListCache(array $vatTypeRows): array
{
    $vatTypeList = [];

    foreach ($vatTypeRows as $vatData) {
        if (! is_object($vatData) || ! method_exists($vatData, 'getValue') || ! method_exists($vatData, 'getId')) {
            continue;
        }

        $value = $vatData->getValue();
        $key   = $value;

        if (! empty($vatTypeList[$key])) {
            $key = $vatData->getId() . '-' . $value . '__' . $vatData->getEiType();
        }

        $vatTypeList[$key] = array(
            'id'             => $vatData->getId(),
            'value'          => $value,
            'ei_type'        => $vatData->getEiType(),
            'ei_description' => $vatData->getEiDescription(),
        );
    }

    return $vatTypeList;
}

/**
 * Resolve the FIC VAT type id for an issued-document line.
 *
 * @param array<int|string,mixed> $vatTypes
 * @param mixed                   $vatValue
 * @param int|null                $defaultVatId
 * @param string|null             $natureNumber
 * @param string|null             $refNormative
 * @param string|null             $taxRegime
 * @param string|null             $countryCode
 * @param bool                    $isGiftProduct
 *
 * @return array{id:int|null,source:string,apply_n4_override:bool}
 */
function resolveFicVatTypeId(
    array $vatTypes,
    $vatValue,
    ?int $defaultVatId,
    ?string $natureNumber = null,
    ?string $refNormative = null,
    ?string $taxRegime = null,
    ?string $countryCode = null,
    bool $isGiftProduct = false
): array {
    $normalizedRef = trim((string)$refNormative);
    $matchedVatId  = '' !== $normalizedRef
        ? findFicVatTypeIdByNature($vatTypes, $vatValue, $natureNumber, $normalizedRef)
        : null;
    if (null !== $matchedVatId) {
        return [
            'id'                => $matchedVatId,
            'source'            => 'nature',
            'apply_n4_override' => false,
        ];
    }

    if ('IT' === $countryCode && 0.0 === (float)$vatValue) {
        if ('RF19' === $taxRegime) {
            return [
                'id'                => 66,
                'source'            => 'rf19_zero_vat',
                'apply_n4_override' => false,
            ];
        }

        if ('RF02' === $taxRegime) {
            return [
                'id'                => 7,
                'source'            => 'rf02_zero_vat',
                'apply_n4_override' => false,
            ];
        }

        if ('RF11' === $taxRegime) {
            return [
                'id'                => 23,
                'source'            => 'rf11_zero_vat',
                'apply_n4_override' => false,
            ];
        }

        $natureFallbackVatId = findFicVatTypeIdByNatureFallback($vatTypes, $vatValue,
            $natureNumber);
        if (null !== $natureFallbackVatId) {
            return [
                'id'                => $natureFallbackVatId,
                'source'            => 'nature_fallback',
                'apply_n4_override' => false,
            ];
        }

        if ($isGiftProduct) {
            return [
                'id'                => 9,
                'source'            => 'gift_zero_vat',
                'apply_n4_override' => false,
            ];
        }

        return [
            'id'                => 6,
            'source'            => 'domestic_zero_vat_n4_fallback',
            'apply_n4_override' => true,
        ];
    }

    $natureFallbackVatId = findFicVatTypeIdByNatureFallback($vatTypes, $vatValue, $natureNumber);
    if (null !== $natureFallbackVatId) {
        return [
            'id'                => $natureFallbackVatId,
            'source'            => 'nature_fallback',
            'apply_n4_override' => false,
        ];
    }

    return [
        'id'                => $defaultVatId,
        'source'            => 'default_rate',
        'apply_n4_override' => false,
    ];
}

/**
 * Evaluate whether FIC send action can continue.
 *
 * @param int         $docID
 * @param int         $issuedDocumentID
 * @param mixed       $issuedSendDocument
 * @param string|null $invoiceSent
 *
 * @return array{allowed:bool,reason:string}
 */
function evaluateFicSendDecision(
    int $docID,
    int $issuedDocumentID,
    $issuedSendDocument,
    ?string $invoiceSent
): array {
    if ($issuedDocumentID <= 0) {
        return ['allowed' => false, 'reason' => 'missing_issued_document'];
    }

    if (\WcElectronInvoice\Functions\normalizeBooleanMeta($issuedSendDocument)) {
        return ['allowed' => false, 'reason' => 'already_sent_fic'];
    }

    if ($docID <= 0) {
        return ['allowed' => false, 'reason' => 'missing_doc_id'];
    }

    if ($docID !== $issuedDocumentID) {
        return ['allowed' => false, 'reason' => 'doc_id_mismatch'];
    }

    if ('sent' === (string)$invoiceSent) {
        return ['allowed' => false, 'reason' => 'already_sent_meta'];
    }

    return ['allowed' => true, 'reason' => 'ok'];
}

/**
 * Evaluate whether FIC create action can continue.
 *
 * @param bool  $statusAllowed
 * @param bool  $invoiceBlockedOnZero
 * @param mixed $existingDocumentID
 *
 * @return array{allowed:bool,reason:string}
 */
function evaluateFicCreateDecision(
    bool $statusAllowed,
    bool $invoiceBlockedOnZero,
    $existingDocumentID = null
): array {
    if (! $statusAllowed) {
        return ['allowed' => false, 'reason' => 'status_not_allowed'];
    }

    if ($invoiceBlockedOnZero) {
        return ['allowed' => false, 'reason' => 'total_zero_not_allowed'];
    }

    if (! empty($existingDocumentID)) {
        return ['allowed' => false, 'reason' => 'already_created'];
    }

    return ['allowed' => true, 'reason' => 'ok'];
}

/**
 * Map FIC send decision reason to normalized error metadata.
 *
 * @param string $reason
 *
 * @return array{code:int,message_key:string}
 */
function mapFicSendDecisionError(string $reason): array
{
    $map = [
        'missing_issued_document' => ['code' => 422, 'message_key' => 'missing_issued_document'],
        'missing_doc_id'          => ['code' => 422, 'message_key' => 'missing_doc_id'],
        'doc_id_mismatch'         => ['code' => 422, 'message_key' => 'doc_id_mismatch'],
        'already_sent_fic'        => ['code' => 422, 'message_key' => 'already_sent'],
        'already_sent_meta'       => ['code' => 422, 'message_key' => 'already_sent'],
    ];

    if (isset($map[$reason])) {
        return $map[$reason];
    }

    return ['code' => 422, 'message_key' => 'action_not_allowed'];
}

/**
 * Map FIC create decision reason to normalized error metadata.
 *
 * @param string $reason
 *
 * @return array{code:int,message_key:string}
 */
function mapFicCreateDecisionError(string $reason): array
{
    $map = [
        'status_not_allowed'     => ['code' => 501, 'message_key' => 'status_not_allowed'],
        'total_zero_not_allowed' => ['code' => 501, 'message_key' => 'total_zero_not_allowed'],
        'already_created'        => ['code' => 422, 'message_key' => 'already_created'],
    ];

    if (isset($map[$reason])) {
        return $map[$reason];
    }

    return ['code' => 422, 'message_key' => 'action_not_allowed'];
}

/**
 * Build stable FIC document display options.
 *
 * @return array<string,mixed>
 */
function buildFicDocumentDisplayOptions(): array
{
    return [
        'show_totals'              => 'all',
        'show_paypal_button'       => false,
        'show_notification_button' => false,
        'accompanying_invoice'     => false,
        'show_payment_method'      => true,
        'show_payments'            => true,
    ];
}

/**
 * Build FIC ei_data payload with optional fields.
 *
 * @param string      $odDate
 * @param string      $invoiceDate
 * @param string|null $creditNoteInvoiceNumber
 * @param array|null  $bankData ['iban' => string, 'name' => string, 'beneficiary' => string]
 *
 * @return array<string,mixed>
 */
function buildFicEiDataPayload(
    string $odDate,
    string $invoiceDate,
    ?string $creditNoteInvoiceNumber = null,
    ?array $bankData = null
): array {
    $payload = [
        'od_date'      => $odDate,
        'invoice_date' => $invoiceDate,
    ];

    if (is_string($creditNoteInvoiceNumber) && '' !== trim($creditNoteInvoiceNumber)) {
        $payload['invoice_number'] = $creditNoteInvoiceNumber;
    }

    if (is_array($bankData) && ! empty($bankData['iban'])) {
        $payload['bank_iban']        = (string)$bankData['iban'];
        $payload['bank_name']        = (string)($bankData['name'] ?? '');
        $payload['bank_beneficiary'] = (string)($bankData['beneficiary'] ?? '');
    }

    return $payload;
}

/**
 * Minimal consistency check for marking invoice as sent in FIC flow.
 *
 * @param bool  $responseValid
 * @param int   $docID
 * @param mixed $alreadySentOnFic
 *
 * @return bool
 */
function canMarkInvoiceSentWithFicSend(bool $responseValid, int $docID, $alreadySentOnFic = false): bool
{
    return $responseValid &&
           $docID > 0 &&
           ! \WcElectronInvoice\Functions\normalizeBooleanMeta($alreadySentOnFic);
}
