<?php

namespace Sibs2;

use Exception;

defined('ABSPATH') || exit;

abstract class sibsPaymentGateway extends \WC_Payment_Gateway
{
    protected $payment_id;

    protected $payment_type;

    protected $payment_brand;

    protected $language;

    protected $payment_group;

    protected $plugins_url;

    protected $payment_display           = 'form';

    private static $added_meta_boxes     = false;

    private static $added_payment_method = false;

    private static $updated_meta_boxes   = false;

    protected $wc_order;

    private static $webhookRetryUrl = 'https://webhook.curta.ink/api/v1/webhook';

    public function __construct()
    {
        $this->payment_id         = $this->id;

        $payment_title            = sibsPaymentSettings::sibs_backend_payment_title($this->payment_id);
        $payment_desc             = sibsPaymentSettings::sibs_backend_payment_desc($this->payment_id);
        $payment_gateway          = sibsGeneralFunctions::sibs_get_payment_gateway_variable($this->payment_id);

        $this->plugins_url        = sibsFunctions::sibs_get_plugin_url();
        $this->form_fields        = sibsPaymentSettings::sibs_create_backend_payment_settings($this->payment_id);
        $this->method_title       = $payment_title;
        $this->method_description = $payment_desc;
        $this->payment_type       = $payment_gateway['payment_type'];
        $this->payment_brand      = $payment_gateway['payment_brand'];
        $this->payment_group      = $payment_gateway['payment_group'];
        $this->language           = $payment_gateway['language'];

        // Enable woocommerce refund for {payment gateway}.
        $this->supports = [
            'refunds',
            'products',
            'pre-orders',
        ];

        $this->init_settings();

        // shortcodes
        add_shortcode('sibs_shortcode_thankyou_page', [$this, 'sibs_shortcode_thankyou_page']);

        // Save admin configuration from woocomerce checkout tab.
        add_action("woocommerce_update_options_payment_gateways_{$this->payment_id}", [$this, 'process_admin_options']); // https://wp-kama.com/plugin/woocommerce/hook/woocommerce_update_options_payment_gateways_(gateway_id)

        // Frontend hook.
        add_action("woocommerce_receipt_{$this->payment_id}", [$this, 'sibs_receipt_page']); // https://wp-kama.com/plugin/woocommerce/hook/woocommerce_receipt_(orderpayment_method)
        add_action("woocommerce_thankyou_{$this->payment_id}", [$this, 'sibs_action_thankyou_page']); // https://wp-kama.com/plugin/woocommerce/hook/woocommerce_thankyou_(orderpayment_method)

        // Backend hook.
        add_action('woocommerce_admin_order_data_after_order_details', [$this, 'sibs_update_order_status']); // https://wp-kama.com/plugin/woocommerce/hook/woocommerce_admin_order_data_after_order_details
        add_action('woocommerce_admin_order_data_after_billing_address', [$this, 'sibs_add_payment_method']); // https://wp-kama.com/plugin/woocommerce/hook/woocommerce_admin_order_data_after_billing_address
        add_action('woocommerce_admin_order_data_after_shipping_address', [$this, 'sibs_add_additional_information']); // https://wp-kama.com/plugin/woocommerce/hook/woocommerce_admin_order_data_after_shipping_address

        //WebHook
        add_action('rest_api_init', [$this, 'sibs_rest_api_init']); // https://developer.wordpress.org/reference/hooks/rest_api_init/
        add_action('rest_api_init', [$this, 'sibs_rest_api_log']); // https://developer.wordpress.org/reference/hooks/rest_api_init/

        WC()->session->__unset('sibs_thank_you_page');
        WC()->session->__unset('sibs_receipt_page');
        WC()->session->__unset('sibs_confirmation_page');
    }

    /**
     * Get Order ID from URL.
     *
     * @return string|null.
     */
    public function getOrderIdFromURL()
    {
        if (isset($_GET['order-received'])) {
            return absint($_GET['order-received']);
        }

        // fallback to matching from URL path if necessary
        if (preg_match('/order-received\/(\d+)/', $_SERVER['REQUEST_URI'], $matches)) {
            return absint($matches[1]);
        }

        return null;
    }

    /**
     * SIBS Shortcode Thank You Page.
     *
     * @return string
     */
    public function sibs_shortcode_thankyou_page()
    {
        ob_start();

        if (! WC()->session->get('sibs_thank_you_page')) {
            sibsLogger::debug('[sibs_shortcode_thankyou_page] Shortcode Thank You Page', sibsConstants::SIBS_LOG);

            WC()->session->set('sibs_thank_you_page', true);
            $order_id = isset(WC()->session->order_id) ? WC()->session->order_id : $this->getOrderIdFromURL();

            $this->sibs_thankyou_page($order_id);
        }
        
        return ob_get_clean();
    }

    /**
     * SIBS Action Thank You Page.
     *
     * @param int $order_id
     * @return void
     */
    public function sibs_action_thankyou_page($order_id)
    {
        if (! WC()->session->get('sibs_thank_you_page')) {
            sibsLogger::debug('[sibs_action_thankyou_page] Action Thank You Page', sibsConstants::SIBS_LOG);

            WC()->session->set('sibs_thank_you_page', true);

            $this->sibs_thankyou_page($order_id);
            
        }
    }

    /**
     * SIBS Thank You Page.
     *
     * @param int $order_id
     * @return void
     */
    abstract public function sibs_thankyou_page($order_id);

    /**
     * Is Available.
     *
     * @return bool
     */
    public function is_available()
    {
        $is_available = parent::is_available();

        if ($is_available) {
            $recurring = 0;

            switch ($this->payment_id) {
                case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_CREDIT_CARD:
                case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_DIRECT_DEBIT:
                    if ($recurring && get_current_user_id() > 0) {
                        $is_available = false;
                    }

                    break;
                case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_APAY:
                    $browser = new sibsBrowser();

                    if ($browser->getBrowser() != sibsBrowser::BROWSER_SAFARI) {
                        $is_available = false;
                    }

                    break;
                case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_CREDIT_CARD_SAVED:
                case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_DIRECT_DEBIT_SAVED:
                    if (! $recurring || get_current_user_id() === 0) {
                        $is_available = false;
                    }

                    break;
                default:
                    $is_available = true;

                    break;
            }
        }

        return $is_available;
    }

    /**
     * Get Title.
     *
     * @return string
     */
    public function sibs_get_title()
    {
        return sibsGeneralFunctions::sibs_translate_backend_payment($this->payment_id);
    }

    /**
     * SIBS: Get Multi Icon
     *
     * @return string
     */
    abstract public function get_multi_icon();

    /**
	 * Get Icon
	 *
	 * @return mixed
	 */
	public function get_icon()
	{
		$icon_html = $this->get_multi_icon();

        if (!empty($icon_html)) {
            return apply_filters('woocommerce_gateway_icon', $icon_html, $this->id);
        } 
	}

    /**
     * Process Payment.
     *
     * @param int $order_id
     * @return array
     */
    public function process_payment($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        WC()->session->__unset(sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor');
        $oneclick_customer_acceptor = ! empty($_POST[sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor']);
        sibsLogger::debug('Customer accept Oneclick/Tokenization: ' . (($oneclick_customer_acceptor) ? 'Yes' : 'No'));
        WC()->session->set(sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor', $oneclick_customer_acceptor);

        return [
            'result'   => 'success',
            'redirect' => $wc_order->get_checkout_payment_url(true),
        ];
    }

    /**
     * SIBS Receipt Page.
     *
     * @param int $order_id
     * @return void
     */
    public function sibs_receipt_page($order_id)
    {
        sibsLogger::debug("sibs_receipt_page(order_id): $order_id", sibsConstants::SIBS_LOG);

        $id = sibsGeneralFunctions::sibs_get_request_value('id');

        if (! empty($id) && empty($accept_payment)) {
            $this->sibs_response_page($id, $order_id);
        }

        if (! WC()->session->__isset('sibs_receipt_page')) {
            $this->sibs_set_payment_form($order_id);
            WC()->session->set('sibs_receipt_page', true);
        }
    }

    /**
     * SIBS Set Payment Form.
     *
     * @param int $order_id
     * @return void
     */
    private function sibs_set_payment_form($order_id)
    {
        $payment_parameters = $this->sibs_set_payment_parameters($order_id);
        $server_mode        = sibsConfig::getEnvironment();

        $transaction     = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
        $transaction_id  = null;
        //michel

        if ($this->sibs_need_new_transaction($transaction, $payment_parameters)) {
            $checkout_result    = \SibsPaymentCore::sibs_get_checkout_result($payment_parameters, $server_mode);

            if (! $checkout_result['is_valid'] || ! isset($checkout_result['response']['transactionID'])) {
                $this->sibs_do_error_payment($order_id, sibsConstants::WOOCOMMERCE_ORDER_STATUS_CANCELLED);
            }

            $checkoutData = $this->checkout_result_to_data($checkout_result['response']);
            $this->sibs_save_or_update_transactions($order_id, $checkoutData);
            $transaction_id = $checkout_result['response']['transactionID'];
            // echo "<pre>";var_dump($transaction);exit;
        } else {
            $transaction_id  = $transaction['reference_id'];
            $checkout_result = [
                'response' => [
                    'transactionID'        => $transaction_id,
                    'transactionSignature' => $transaction['signature'],
                    'formContext'          => $transaction['formContext'],
                ],
            ];
        }

        $url_config = self::get_url_config(
            $server_mode,
            $transaction_id,
            wc_get_checkout_url(),
            $this->plugins_url
        );

        $payment_widget_content = \SibsPaymentCore::sibs_get_payment_widget_content($url_config['payment_widget'], $server_mode);

        if (! $payment_widget_content['is_valid'] || str_contains($payment_widget_content['response'], 'errorDetail')) {
            $this->sibs_do_error_payment($order_id, sibsConstants::WOOCOMMERCE_ORDER_STATUS_CANCELLED);
        }

        wp_enqueue_script('sibs_form_payment_widget_script', $url_config['payment_widget'], ['jquery'], false, true);

        $args          = self::get_form_args($this->payment_id, $payment_parameters, $url_config, $checkout_result);
        $template_form = __DIR__ . '/../templates/checkout/template-payment-form.php';

        sibsGeneralFunctions::sibs_include_template($template_form, $args);
    }

    /**
     * SIBS Need New Transaction.
     *
     * @param array $transaction
     * @param array $payment_parameters
     * @return bool
     */
    private function sibs_need_new_transaction($transaction, $payment_parameters)
    {
        if (is_null($transaction) || $transaction === false) {
            return true;
        }

        if (empty($transaction['formContext'])) {
            return true;
        }

        if (empty($transaction['signature'])) {
            return true;
        }

        if (
            ! empty($transaction['payment_status']) &&
            $transaction['payment_status'] === sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED
        ) {
            return true;
        }

        if (! in_array($transaction['payment_brand'], $payment_parameters['transaction']['paymentMethod'])) {
            return true;
        }

        // TODO: Return true if form is expiry

        return false;
    }

    /**
     * Get Form args.
     *
     * @param int $payment_id
     * @param array $payment_parameters
     * @param string $url_config
     * @param array $checkout_result
     * @return array
     */
    private static function get_form_args($payment_id, $payment_parameters, $url_config, $checkout_result)
    {
        sibsLogger::debug("payment_id: $payment_id", sibsConstants::SIBS_LOG);

        $formConfig    = self::get_form_config($payment_id, $payment_parameters, $url_config, $checkout_result);
        $formContext   = $checkout_result['response']['formContext'];
        $formSignature = $checkout_result['response']['transactionSignature'];
        $formStyle     = self::get_form_style();

        sibsLogger::debug('formConfig: ' . sibsLogger::prettify($formConfig), sibsConstants::SIBS_LOG);
        sibsLogger::debug("formContext: $formContext", sibsConstants::SIBS_LOG);
        sibsLogger::debug("formSignature: $formSignature", sibsConstants::SIBS_LOG);
        sibsLogger::debug('formStyle: ' . sibsLogger::prettify($formStyle), sibsConstants::SIBS_LOG);

        return [
            'formConfig'  => $formConfig,
            'formContext' => $formContext,
            'signature'   => $formSignature,
            'spg_style'   => $formStyle,
        ];
    }

    /**
     * Get URL Config.
     *
     * @param string $server_mode
     * @param string $transaction_id
     * @param string $cancel_url
     * @param string $plugins_url
     * @return array
     */
    private static function get_url_config($server_mode, $transaction_id, $cancel_url, $plugins_url)
    {
        $environment_file = sibsFunctions::get_environment();
        $widget_endpoints = $environment_file['widget_endpoints'];

        $payment_widget = \SibsPaymentCore::sibs_get_payment_widget_url(
            $server_mode,
            $transaction_id,
            $widget_endpoints
        );

        $return_url = self::get_plugin_return_url();

        sibsLogger::debug("url_config[return_url]: {$return_url}", sibsConstants::SIBS_LOG);
        sibsLogger::debug("transaction_id: $transaction_id", sibsConstants::SIBS_LOG);
        sibsLogger::debug("url_config[payment_widget]: {$payment_widget}", sibsConstants::SIBS_LOG);

        return [
            'cancel_url'     => $cancel_url,
            'plugins_url'    => $plugins_url,
            'return_url'     => $return_url,
            'payment_widget' => $payment_widget,
        ];
    }

    /**
     * Get Plugin Return URL.
     *
     * @return string
     */
    private static function get_plugin_return_url()
    {
        global $wp;

        $key = sibsGeneralFunctions::sibs_get_request_value('key');
        $url = sibsConfig::getWoocommerceOption('permalink_structure') == '' ? '?' . $wp->query_string : $wp->request;

        if (isset($url)) {
            $url = home_url($url);

            return sibsConfig::getWoocommerceOption('permalink_structure') == '' ? "$url&key=$key" : "$url/?key=$key";
        } else {
            $url       = get_page_link();
            $order_pay = sibsGeneralFunctions::sibs_get_request_value('order-pay');

            return "$url&order-pay=$order_pay&key=$key&sibs=payments";
        }
    }

    /**
     * Get Form Style.
     *
     * @return string|false
     */
    private static function get_form_style()
    {
        $layout              = sibsConfig::getStyleOption('layout') ?? 'default';
        $theme               = sibsConfig::getStyleOption('theme') ?? 'default';
        $layout_primary      = sibsConfig::getStyleOption('layout_primary') ?? '';
        $layout_surface      = sibsConfig::getStyleOption('layout_surface') ?? '';
        $layout_text_color   = sibsConfig::getStyleOption('layout_text_color') ?? '';
        $layout_header_color = sibsConfig::getStyleOption('layout_header_color') ?? '';

        $theme_secondary  = sibsConfig::getStyleOption('theme_secondary_color') ?? '';
        $theme_background = sibsConfig::getStyleOption('theme_background_color') ?? '';
        $theme_border     = sibsConfig::getStyleOption('theme_border_color') ?? '';
        $theme_header     = sibsConfig::getStyleOption('theme_header_text') ?? '';

        $font = sibsConfig::getStyleOption('font') ?? '';

        $style           = [
            'layout' => $layout,
            'theme'  => $theme,
            'color'  => [
                'primary'   => $layout_primary,
                'secondary' => $theme_secondary,
                'border'    => $theme_border,
                'surface'   => $layout_surface,
                'header'    => [
                    'text'       => $theme_header,
                    'background' => $layout_header_color,
                ],
                'body' => [
                    'text'       => $layout_text_color,
                    'background' => $theme_background,
                ],
                'font' => $font,
            ],
        ];

        return json_encode($style);
    }

    /**
     * Get Form Config.
     *
     * @param int $payment_id
     * @param array $payment_parameters
     * @param string $url_config
     * @param array $checkout_result
     * @return string|false
     */
    private static function get_form_config($payment_id, $payment_parameters, $url_config, $checkout_result)
    {
        $form_customer_name   = $payment_parameters['customer']['customerInfo']['customerName'];
        $form_customer_email  = $payment_parameters['customer']['customerInfo']['customerEmail'];
        $form_amount          = $payment_parameters['transaction']['amount']['value'];
        $form_currency        = $payment_parameters['transaction']['amount']['currency'];
        $form_payment_methods = $payment_id;
        $form_language        = sibsFunctions::get_language();

        $formConfig = [
            'paymentMethodList' => [
                $form_payment_methods,
            ],
            'amount' => [
                'amount'   => $form_amount,
                'currency' => $form_currency,
            ],
            'language'     => $form_language,
            'redirectUrl'  => $url_config['return_url'] . '&id=' . $checkout_result['response']['transactionID'],
            'customerData' => [
                'customerName'  => $form_customer_name,
                'customerEmail' => $form_customer_email,
            ],
        ];

        return json_encode($formConfig);
    }

    /**
     * SIBS Response Page.
     *
     * @param int $id
     * @param int $order_id
     * @return void
     */
    private function sibs_response_page($id, $order_id)
    {
        sibsLogger::debug("sibs_response_page(order_id): $order_id", sibsConstants::SIBS_LOG);

        $is_testmode_available = false;
        $status_parameters     = sibsGeneralFunctions::sibs_get_credentials($this->payment_id, $is_testmode_available);
        sibsLogger::info('status_parameters: ' . sibsLogger::prettify($status_parameters), sibsConstants::SIBS_LOG);

        $payment_result = self::get_payment_result($id, $status_parameters);

        if (! $payment_result) {
            $this->sibs_do_error_payment($order_id, sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED);

            return;
        }

        $result_status  = \SibsPaymentCore::sibs_get_transaction_result($payment_result['returnStatus']['statusCode']);
        $payment_status = sibsGeneralFunctions::convertTransactionStatus(
            $payment_result['paymentStatus'],
            $payment_result['paymentType']
        );
        $payment_result['payment_status'] = $payment_status;

        sibsLogger::info("payment_status: $payment_status", sibsConstants::SIBS_LOG);

        if ($result_status !== sibsConstants::SIBS_ACK || $payment_status === sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED) {
            $this->sibs_failed_response($order_id, $payment_result);

            return;
        }

        $this->sibs_do_success_payment($order_id, $payment_result);
    }

    /**
     * Get Payment Result.
     *
     * @param int $id
     * @param array $status_parameters
     * @return mixed
     */
    private static function get_payment_result($id, $status_parameters)
    {
        $payment_result = \SibsPaymentCore::sibs_get_payment_status($id, $status_parameters);

        return $payment_result;
    }

    /**
     * SIBS Get Woocommerce Thank You order Received Text.
     *
     * @return string
     */
    public function sibs_get_woocommerce_thankyou_order_received_text()
    {
        return __('Order Review', sibsConstants::SIBS_TRANSLATE);
    }

    /**
     * Get Woocommerce Thank You.
     *
     * @return void
     */
    public function sibs_get_woocommerce_thankyou()
    {
        sibsLogger::debug('sibs_get_woocommerce_thankyou()', sibsConstants::SIBS_LOG);

        $key = sibsGeneralFunctions::sibs_get_request_value('key');
        $id  = sibsGeneralFunctions::sibs_get_request_value('id');

        if (isset(WC()->request)) {
            $url                 = home_url(WC()->request);
            $accept_payment_link = esc_attr(
                "$url/?key=$key&accept_payment=1&id=$id"
            );
        } else {
            $url                 = home_url(WC()->request);
            $order_pay           = sibsGeneralFunctions::sibs_get_request_value('order-pay');
            $accept_payment_link = esc_attr(
                "$url&order-pay=$order_pay&key=$key&accept_payment=1&id=$id"
            );
        }

        $wc_checkout_url = esc_attr(wc_get_checkout_url());
        $cancel          = esc_attr(__('Cancel', sibsConstants::SIBS_TRANSLATE));
        $place_order     = esc_attr(__('Place order', sibsConstants::SIBS_TRANSLATE));

        // Input hidden is for make the input have the shame padding according to css style.
        ?>
            <div id="payment" style="padding-top: 10px; border-top: 1px solid #aaaaaa">
                <input type="hidden"/>
                <input type="button" class="button alt" onclick="window.location = \<?= $wc_checkout_url ?>\" value="<?= $cancel ?>"/>
                <input type="submit" class="button alt"  id="place_order" onclick="window.location = \<?= $accept_payment_link ?>\" value="<?= $place_order ?>" />
            </div>
            <script>
                var title = document.getElementsByClassName("woocommerce-thankyou-order-received")[0].innerHTML;
                document.getElementsByClassName("sibs_entry-title")[0].innerHTML = title;
                var element = document.getElementsByClassName("order_details")[0];
                element.parentNode.removeChild(element);
            </script>
        <?php
    }

    /**
     * SIBS Do Success Payment.
     *
     * @param int $order_id
     * @param array $payment_result
     * @return void
     */
    private function sibs_do_success_payment($order_id, $payment_result)
    {
        sibsLogger::info('sibs_do_success_payment', sibsConstants::SIBS_LOG);
        $wc_order = new \WC_Order($order_id);

        sibsLogger::info($payment_result, sibsConstants::SIBS_LOG);

        try {
            $wc_order->update_status($payment_result['payment_status']);
            sibsLogger::info('Sibs_do_success_payment: ' . $payment_result['payment_status'], sibsConstants::SIBS_LOG);
        } catch (\Throwable $t) {
            sibsLogger::info('ERRO sibs_do_success_payment: ' . $t->getMessage(), sibsConstants::SIBS_LOG);
            sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
        }

        $order_awaiting_payment_session = WC()->session->get('order_awaiting_payment');

        // Empty awaiting payment session.
        if (! empty($order_awaiting_payment_session)) {
            WC()->session->__unset('order_awaiting_payment');
        }

        $this->sibs_save_or_update_transactions($order_id, $payment_result, true);

        WC()->cart->empty_cart();
        wp_safe_redirect($this->get_return_url($wc_order));

        exit();
    }

    /**
     * SIBS Failed Response.
     *
     * @param int $order_id
     * @param array $payment_result
     * @return void
     */
    private function sibs_failed_response($order_id, $payment_result)
    {
        $this->sibs_save_or_update_transactions($order_id, $payment_result);
        $this->sibs_do_error_payment($order_id, $payment_result['payment_status']);
    }

    /**
     * SIBS Do error payment.
     *
     * @param int $order_id
     * @param string $payment_status
     * @return void
     */
    private function sibs_do_error_payment($order_id, $payment_status)
    {
        $wc_order = new \WC_Order($order_id);

        sibsLogger::error("payment_status: $payment_status", sibsConstants::SIBS_LOG);

        // Cancel the order.
        try {
            $wc_order->update_status($payment_status);
        } catch (\Throwable $t) {
            sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
        }

        // To display failure messages from woocommerce session.
        wc_add_notice(__('Unfortunately, your transaction has failed. Please try again', sibsConstants::SIBS_TRANSLATE), 'error');

        wp_safe_redirect(wc_get_checkout_url());

        exit();
    }

    /**
     * SIBS is SIBS by order.
     *
     * @param int $order_id
     * @return bool
     */
    public static function sibs_is_sibs_by_order($order_id)
    {
        $transaction_log = sibsGeneralModels::sibs_get_db_transaction_log($order_id);

        return isset($transaction_log['payment_id']) ? self::sibs_is_sibs_by_payment_id($transaction_log['payment_id']) : false;
    }

    /**
     * SIBS is SIBS by Payment ID.
     *
     * @param string $payment_method
     * @return bool
     */
    private static function sibs_is_sibs_by_payment_id($payment_method)
    {
        if (str_contains($payment_method, 'sibs')) {
            return true;
        }

        return false;
    }

    /**
     * SIBS Update Payment Status.
     *
     * @param int $order_id
     * @param bool $original_post_status
     * @param bool $order_post_status
     * @return bool
     */
    private function sibs_update_payment_status($order_id, $original_post_status = false, $order_post_status = false)
    {
        $transaction_log = sibsGeneralModels::sibs_get_db_transaction_log($order_id);

        if (! $original_post_status) {
            $original_post_status = $transaction_log['payment_status'];
        }

        $backoffice_parameter = sibsGeneralFunctions::sibs_get_credentials($transaction_log['payment_id']);

        $backoffice_result = \SibsPaymentCore::sibs_update_status($transaction_log['reference_id'], $backoffice_parameter);
        $backoffice_status = \SibsPaymentCore::sibs_get_transaction_result($backoffice_result['result']['code']);
        $payment_status    = sibsGeneralFunctions::convertTransactionStatus(
            $backoffice_result['paymentStatus'],
            $backoffice_result['paymentType']
        );

        if ($backoffice_status === sibsConstants::SIBS_ACK && $payment_status !== sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED) {
            sibsGeneralModels::sibs_update_db_transaction_log_status($order_id, $payment_status);
            sibsGeneralModels::sibs_update_db_posts_status($order_id, $payment_status);

            if ($original_post_status !== $payment_status) {
                $unsuccess_Status = [
                    sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED,
                    sibsConstants::WOOCOMMERCE_ORDER_STATUS_CANCELLED,
                ];

                if ($order_post_status && (in_array($order_post_status, $unsuccess_Status))) {
                    wc_increase_stock_levels($order_id);
                }

                $wc_order_status         = wc_get_order_statuses();
                $status_name['original'] = $wc_order_status[$original_post_status];
                $status_name['new']      = $wc_order_status[$payment_status];
                $this->sibs_add_order_notes($order_id, $status_name);
            }

            return true;
        }

        return false;
    }

    /**
     * SIBS: Check Status.
     *
     * @param int $order_id
     * @return array $back_data
     */
    public static function check_status($order_id)
    {
        sibsLogger::debug("[check_status] order_id: $order_id", sibsConstants::SIBS_LOG);

        try {
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                throw new sibsException(
                    __('Order is not a SIBS payment', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $sibs_payment              = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
            $is_testmode_available     = false;
            $is_multichannel_available = true;
            $credentials               = sibsGeneralFunctions::sibs_get_credentials($sibs_payment['payment_id'], $is_testmode_available, $is_multichannel_available);

            sibsLogger::debug('[check_status] sibs_payment: ' . sibsLogger::prettify($sibs_payment), sibsConstants::SIBS_LOG);
            sibsLogger::debug('[check_status] credentials: ' . sibsLogger::prettify($credentials), sibsConstants::SIBS_LOG);

            $payment_result = self::get_payment_result($sibs_payment['reference_id'], $credentials);
            sibsLogger::debug('[check_status] payment_result: ' . sibsLogger::prettify($payment_result), sibsConstants::SIBS_LOG);

            $result_status = \SibsPaymentCore::sibs_get_transaction_result($payment_result['returnStatus']['statusCode'], $payment_result['paymentStatus']);
            sibsLogger::debug("[check_status] result_status: $result_status", sibsConstants::SIBS_LOG);

            if ($result_status === sibsConstants::SIBS_ACK) {
                $back_data = self::deal_ACK_status($order_id, $payment_result);
            } else {
                $error_message = sprintf(
                    __('Error on Status Inquiry for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                throw new sibsException($error_message);
            }
        } catch(sibsException $e) {
            $back_data['msg']   = $e->getErrorMessage();
            $back_data['error'] = 1;
        } catch(Exception $e) {
            $back_data['msg']   = $e->getMessage();
            $back_data['error'] = 1;
        }

        return $back_data;
    }

    /**
     * SIBS: Deal with acknologment status.
     *
     * @param int $order_id
     * @param array $payment_result
     * @return array $back_data
     */
    public static function deal_ACK_status($order_id, $payment_result)
    {
        $wc_order = new \WC_Order($order_id);

        $payment_type   = $payment_result['paymentType'];
        $payment_method = $payment_result['paymentMethod'];

        sibsLogger::debug('[dealACKStatus] payment_type: ' . esc_attr($payment_type), sibsConstants::SIBS_LOG);
        sibsLogger::debug('[dealACKStatus] payment_method: ' . esc_attr($payment_method), sibsConstants::SIBS_LOG);

        $payment_status = sibsGeneralFunctions::convertTransactionStatus(
            $payment_result['paymentStatus'],
            $payment_type
        );

        $back_data['msg'] = sprintf(
            __('SIBS status payment is %s for order #%s', sibsConstants::SIBS_TRANSLATE),
            esc_attr($payment_status),
            esc_attr($order_id)
        );
        $back_data['error'] = 0;

        sibsGeneralModels::sibs_update_db_transaction($order_id, [
            'payment_status' => $payment_status,
        ]);

        $wc_order->update_status($payment_status);

        return $back_data;
    }

    /**
     * SIBS: Get Message when ACK.
     *
     * @param int $order_id
     * @param array $payment_result
     * @param string $payment_type
     * @return array ($payment_status, $back_data)
     */
    public static function get_ACK_message($order_id, $payment_result, $payment_type)
    {
        $payment_status = sibsGeneralFunctions::convertTransactionStatus(
            $payment_result['paymentStatus'],
            $payment_type
        );

        switch ($payment_status) {
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_PENDING:
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_ON_HOLD:
                sibsLogger::debug('[getACKMessage] Status payment pending for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status payment pending for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                break;
            case sibsConstants::SIBS_ORDER_STATUS_AUTHORIZED:
                sibsLogger::debug('[getACKMessage] Status payment authorized for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status payment authorized for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                break;
            case sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_CAPTURED:
                sibsLogger::debug('[getACKMessage] Status payment partially captured for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status payment partially captured for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                break;
            case sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_REFUNDED:
                sibsLogger::debug('[getACKMessage] Status payment partially refunded for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status payment partially refunded for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                break;
            case sibsConstants::SIBS_ORDER_STATUS_PENDING_REFUND:
                sibsLogger::debug('[getACKMessage] Status payment pending refund for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status payment is pending for refund for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                break;
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING:
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_COMPLETED:
                sibsLogger::debug('[getACKMessage] Status payment complete (ACK) for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status complete (ACK) for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );

                break;
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED:
            default:
                sibsLogger::debug('[getACKMessage] Status payment declined (NOK) for order #' . esc_attr($order_id), sibsConstants::SIBS_LOG);
                $back_data['msg'] = sprintf(
                    __('Status declined (NOK) for order #%s', sibsConstants::SIBS_TRANSLATE),
                    esc_attr($order_id)
                );
        }

        return [$payment_status, $back_data];
    }

    /**
     * SIBS: Process Capture Amount.
     *
     * @param int $order_id
     * @return array $back_data
     */
    public static function process_capture($order_id)
    {
        $wc_order = new \WC_Order($order_id);
        sibsLogger::debug("[process_capture] order_id: $order_id", sibsConstants::SIBS_LOG);

        try {
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                throw new sibsException(
                    __('Order is not a SIBS payment', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $payment_status    = 'wc-' . $wc_order->get_status();
            $status_to_capture = [
                sibsConstants::SIBS_ORDER_STATUS_AUTHORIZED,
            ];

            if (! in_array($payment_status, $status_to_capture)) {
                throw new sibsException(
                    __('Order is cannot be captured in this status', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $sibs_payment  = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
            $payment_type  = sibsConstants::SPG_PAYMENT_TYPE_CAPT;
            $error_message = __('ERROR_GENERAL_CAPTURE_PAYMENT', sibsConstants::SIBS_TRANSLATE);
            $amount        = $wc_order->get_total();

            sibsLogger::debug('[process_capture] sibs_payment: ' . sibsLogger::prettify($sibs_payment), sibsConstants::SIBS_LOG);
            sibsLogger::debug("[process_capture] payment_type: $payment_type", sibsConstants::SIBS_LOG);
            sibsLogger::debug("[process_capture] amount: $amount", sibsConstants::SIBS_LOG);

            $backoffice_config = self::get_backoffice_config(
                $sibs_payment,
                $payment_type,
                $amount,
                $error_message
            );

            $backoffice_result = self::sibs_do_back_office_payment($backoffice_config);

            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'captured_id'    => $backoffice_result['response']['transactionID'],
                'capture_status' => $backoffice_result['response']['paymentStatus'],
            ]);

            if (! $backoffice_result['is_valid']) {
                throw new sibsException(
                    __('Capture request has an ERROR', sibsConstants::SIBS_TRANSLATE)
                );
            }

            switch($backoffice_result['response']['paymentStatus']) {
                case sibsConstants::SIBS_RESULT_SUCCESS:
                    $capture_status = $backoffice_config['order_status'];
                    sibsLogger::debug('[process_capture] SUCCESS', sibsConstants::SIBS_LOG);

                    break;
                default:
                    throw new sibsException(
                        __('Capture request denied by SIBS', sibsConstants::SIBS_TRANSLATE)
                    );
            }

            $amountMSG = "$amount {$wc_order->get_currency()}";

            $note_message = sprintf(
                __('Order was Captured for %s', sibsConstants::SIBS_TRANSLATE),
                $amountMSG
            );

            try {
                $wc_order->update_status($capture_status);
            } catch (\Throwable $t) {
                sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
            }

            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'payment_status'      => $capture_status,
                'captured_amount'     => $backoffice_result['response']['amount']['value'] + $sibs_payment['captured_amount'],
                'capture_information' => $note_message . (! empty($sibs_payment['capture_information']) ? ', ' . $sibs_payment['capture_information'] : ''),
            ]);

            // Add cpture note
            $wc_order->add_order_note($note_message);

            // Capture Success
            $back_data['error'] = 0;
            $back_data['msg']   = sprintf(
                __('Capture success for order #%s with %s', sibsConstants::SIBS_TRANSLATE),
                $wc_order->get_id(),
                $amountMSG
            );
        } catch(sibsException $e) {
            $back_data['error'] = 1;
            $back_data['msg']   = $e->getErrorMessage();
        } catch(Exception $e) {
            $back_data['error'] = 1;
            $back_data['msg']   = $e->getMessage();
        }

        return $back_data;
    }

    /**
     * SIBS: Process Refund.
     *
     * @param string $order_id
     * @param string|null $amount
     * @param string $reason
     */
    public function process_refund($order_id, $amount = null, $reason = '')
    {
        $wc_order = new \WC_Order($order_id);

        try {
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                throw new sibsException(
                    __('Order is not a SIBS payment', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $payment_status   = 'wc-' . $wc_order->get_status();
            $status_to_refund = [
                sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING,
                sibsConstants::WOOCOMMERCE_ORDER_STATUS_COMPLETED,
                sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_CAPTURED,
                sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_REFUNDED,
            ];

            if (! in_array($payment_status, $status_to_refund)) {
                throw new sibsException(
                    sprintf(
                        __('Order is cannot be refunded in status %s', sibsConstants::SIBS_TRANSLATE),
                        esc_attr($payment_status)
                    )
                );
            }

            $sibs_payment  = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
            $payment_type  = sibsConstants::SPG_PAYMENT_TYPE_RFND;
            $error_message = __('ERROR_GENERAL_REFUND_PAYMENT', sibsConstants::SIBS_TRANSLATE);

            $backoffice_config = self::get_backoffice_config(
                $sibs_payment,
                $payment_type,
                $amount,
                $error_message
            );

            $backoffice_result = self::sibs_do_back_office_payment($backoffice_config);
            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'refunded_id'   => $backoffice_result['response']['transactionID'],
                'refund_status' => $backoffice_result['response']['paymentStatus'],
            ]);

            if (! $backoffice_result['is_valid']) {
                throw new sibsException(
                    __('Refund request has an ERROR', sibsConstants::SIBS_TRANSLATE)
                );
            }

            switch($backoffice_result['response']['paymentStatus']) {
                case sibsConstants::SIBS_RESULT_SUCCESS:
                    $refund_status = $backoffice_config['order_status'];
                    sibsLogger::debug('[process_refund] SUCCESS', sibsConstants::SIBS_LOG);

                    break;
                case sibsConstants::SIBS_RESULT_PENDING:
                    $refund_status = sibsConstants::SIBS_ORDER_STATUS_PENDING_REFUND;
                    sibsLogger::debug('[process_refund] PENDING', sibsConstants::SIBS_LOG);

                    break;
                default:
                    throw new sibsException(
                        __('Refund request denied by SIBS', sibsConstants::SIBS_TRANSLATE)
                    );
            }

            $amountMSG = "$amount {$wc_order->get_currency()}";

            $note_message = sprintf(
                __('Order was Refunded for %s', sibsConstants::SIBS_TRANSLATE),
                $amountMSG
            );

            try {
                $wc_order->update_status($refund_status);
            } catch (\Throwable $t) {
                sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
            }

            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'payment_status'     => $refund_status,
                'refunded_amount'    => $backoffice_result['response']['amount']['value'] + $sibs_payment['refunded_amount'],
                'refund_information' => $note_message . (! empty($sibs_payment['refund_information']) ? ', ' . $sibs_payment['refund_information'] : ''),
            ]);

            if ($backoffice_result['response']['paymentStatus'] === sibsConstants::SIBS_RESULT_PENDING) {
                return new \WP_Error(
                    'refund_pending',
                    __('BLIK Refund is asyncronous, you have to make a refund inquiry to get the real refund status', sibsConstants::SIBS_TRANSLATE),
                    ['status' => 200]
                );
            }

            return true;
        } catch(sibsException $e) {
            return new \WP_Error(
                'not_refunded_error',
                $e->getErrorMessage(),
                ['status' => 400]
            );
        } catch(Exception $e) {
            return new \WP_Error(
                'not_refunded_error',
                $e->getMessage(),
                ['status' => 500]
            );
        }
    }

    /**
     * Get Order Refunded Status.
     *
     * @param int $order_id
     * @param string $payment_method
     * @return void
     */
    public static function get_order_refunded_status($order_id, $payment_method)
    {
        $async_refund = [
            sibsConstants::SIBS_PAYMENT_METHOD_ORDER_BLIK,
        ];

        if (in_array($payment_method, $async_refund)) {
            sibsLogger::debug('[process_refund] Order Fully Refunded (Async)', sibsConstants::SIBS_LOG);

            return sibsConstants::WOOCOMMERCE_ORDER_STATUS_REFUNDED;
        }

        if (self::is_fully_refunded($order_id)) {
            sibsLogger::debug('[process_refund] Order Fully Refunded', sibsConstants::SIBS_LOG);

            return sibsConstants::WOOCOMMERCE_ORDER_STATUS_REFUNDED;
        } elseif (self::is_partially_refunded($order_id)) {
            sibsLogger::debug('[process_refund] Order Partially Refunded', sibsConstants::SIBS_LOG);

            return sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_REFUNDED;
        }

        throw new sibsException(
            __('Order is not fully or partially refunded. Amount Value must be wrong', sibsConstants::SIBS_TRANSLATE)
        );
    }

    /**
     * Is Fully Refunded.
     *
     * @param int $order_id
     * @return bool
     */
    public static function is_fully_refunded($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        sibsLogger::debug('[is_fully_refunded] get_remaining_refund_items: ' . $wc_order->get_remaining_refund_items(), sibsConstants::SIBS_LOG);
        sibsLogger::debug('[is_fully_refunded] get_remaining_refund_amount: ' . $wc_order->get_remaining_refund_amount(), sibsConstants::SIBS_LOG);

        return
            $wc_order->get_remaining_refund_items() === 0 ||
            $wc_order->get_remaining_refund_amount() == 0;
    }

    /**
     * Is Partially Refunded.
     *
     * @param int $order_id
     * @return bool
     */
    public static function is_partially_refunded($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        sibsLogger::debug('[is_partially_refunded] get_item_count_refunded: ' . $wc_order->get_item_count_refunded(), sibsConstants::SIBS_LOG);
        sibsLogger::debug('[is_partially_refunded] get_total_refunded: ' . $wc_order->get_total_refunded(), sibsConstants::SIBS_LOG);

        $not_fully_refunded = ! self::is_fully_refunded($order_id);
        $refunded_something = (
            $wc_order->get_item_count_refunded() > 0 ||
            $wc_order->get_total_refunded() > 0
        );

        return $not_fully_refunded && $refunded_something;
    }

    /**
     * SIBS: Refund Inquiry.
     *
     * @param int $order_id
     * @return array $back_data
     */
    public static function refund_inquiry($order_id)
    {
        $wc_order = new \WC_Order($order_id);
        sibsLogger::debug("[refund_inquiry] order_id: $order_id", sibsConstants::SIBS_LOG);

        try {
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                throw new sibsException(
                    __('Order is not a SIBS payment', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $payment_status           = 'wc-' . $wc_order->get_status();
            $status_to_refund_inquiry = [
                sibsConstants::SIBS_ORDER_STATUS_PENDING_REFUND,
            ];

            if (! in_array($payment_status, $status_to_refund_inquiry)) {
                throw new sibsException(
                    __('Order is not on a status to refund inquiry', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $sibs_payment              = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
            $is_testmode_available     = false;
            $is_multichannel_available = true;
            $credentials               = sibsGeneralFunctions::sibs_get_credentials($sibs_payment['payment_id'], $is_testmode_available, $is_multichannel_available);

            $refund_result = self::get_payment_result($sibs_payment['refunded_id'], $credentials);

            if ($refund_result['paymentStatus'] === sibsConstants::SIBS_RESULT_SUCCESS) {
                $refund_inquiry_status = sibsConstants::WOOCOMMERCE_ORDER_STATUS_REFUNDED;

                try {
                    $wc_order->update_status($refund_inquiry_status);
                } catch (\Throwable $t) {
                    sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
                }

                $amount    = $sibs_payment['refunded_amount'];
                $amountMSG = "$amount {$wc_order->get_currency()}";

                // Add refund Inquiry note
                $note_message = sprintf(
                    __('Order was Refunded for %s', sibsConstants::SIBS_TRANSLATE),
                    $amountMSG
                );
                $wc_order->add_order_note($note_message);

                sibsGeneralModels::sibs_update_db_transaction($order_id, [
                    'payment_status'     => $refund_inquiry_status,
                    'refunded_amount'    => $sibs_payment['refunded_amount'],
                    'refund_information' => $note_message . (! empty($sibs_payment['refund_information']) ? ', ' . $sibs_payment['refund_information'] : ''),
                ]);
                sibsLogger::debug('[refund_inquiry] SUCCESS', sibsConstants::SIBS_LOG);
            } else {
                if ($refund_result['paymentStatus'] === sibsConstants::SIBS_RESULT_PENDING) {
                    $refund_inquiry_status = sibsConstants::SIBS_ORDER_STATUS_PENDING_REFUND;
                    $errorMessage          = __('Order refund still pending', sibsConstants::SIBS_TRANSLATE);
                    sibsLogger::debug('[refund_inquiry] PENDING', sibsConstants::SIBS_LOG);
                } else {
                    $refund_inquiry_status = sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING;
                    $errorMessage          = __('Order refund failed for #%s', sibsConstants::SIBS_TRANSLATE);
                    sibsLogger::debug('[refund_inquiry] FAILED', sibsConstants::SIBS_LOG);
                }

                try {
                    $wc_order->update_status($refund_inquiry_status);
                } catch (\Throwable $t) {
                    sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
                }

                sibsGeneralModels::sibs_update_db_transaction($order_id, [
                    'payment_status'     => $refund_inquiry_status,
                    'refunded_amount'    => $sibs_payment['refunded_amount'],
                    'refund_information' => $errorMessage . (! empty($sibs_payment['refund_information']) ? ', ' . $sibs_payment['refund_information'] : ''),
                ]);

                throw new sibsException($errorMessage);
            }

            // Refund Inquiry Success
            $back_data['error'] = 0;
            $back_data['msg']   = sprintf(
                __('Refund success for order #%s with %s', sibsConstants::SIBS_TRANSLATE),
                $wc_order->get_id(),
                $amountMSG
            );

            sibsLogger::debug('[refund_inquiry] SUCCESS', sibsConstants::SIBS_LOG);
        } catch(sibsException $e) {
            $back_data['error'] = 1;
            $back_data['msg']   = $e->getErrorMessage();
        }

        return $back_data;
    }

    /**
     * SIBS: Cancel Authorization.
     *
     * @param int $order_id
     * @return array $back_data
     */
    public static function cancel_authorization($order_id)
    {
        $wc_order = new \WC_Order($order_id);
        sibsLogger::debug("[cancel_authorization] order_id: $order_id", sibsConstants::SIBS_LOG);

        try {
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                throw new sibsException(
                    __('Order is not a SIBS payment', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $payment_status                 = 'wc-' . $wc_order->get_status();
            $status_to_cancel_authorization = [
                sibsConstants::SIBS_ORDER_STATUS_AUTHORIZED,
            ];

            if (! in_array($payment_status, $status_to_cancel_authorization)) {
                throw new sibsException(
                    __('Order is not on a status to cancel authorization', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $sibs_payment  = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
            $payment_type  = sibsConstants::SPG_PAYMENT_TYPE_CAUT;
            $error_message = __('ERROR_GENERAL_CANCEL_AUTH_PAYMENT', sibsConstants::SIBS_TRANSLATE);
            $amount        = $wc_order->get_total();

            $backoffice_config = self::get_backoffice_config(
                $sibs_payment,
                $payment_type,
                $amount,
                $error_message
            );

            $backoffice_result = self::sibs_do_back_office_payment($backoffice_config);

            if (! $backoffice_result['is_valid']) {
                throw new sibsException(
                    __('Cancel Authorization request has an ERROR', sibsConstants::SIBS_TRANSLATE)
                );
            }

            if ($backoffice_result['response']['paymentStatus'] !== sibsConstants::SIBS_RESULT_SUCCESS) {
                throw new sibsException(
                    __('Cancel Authorization request denied by SIBS', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $cancel_auth_status = $backoffice_config['order_status'];
            sibsLogger::debug('[cancel_authorization] SUCCESS', sibsConstants::SIBS_LOG);

            $note_message = __('Order was Canceled', sibsConstants::SIBS_TRANSLATE);

            try {
                $wc_order->update_status($cancel_auth_status);
            } catch (\Throwable $t) {
                sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
            }

            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'payment_status' => $cancel_auth_status,
            ]);

            // Add authorization cancel note
            $wc_order->add_order_note($note_message);

            // Authorization Canecled with Success
            $back_data['error'] = 0;
            $back_data['msg']   = sprintf(
                __('Authorization Canceled with success for order #%s', sibsConstants::SIBS_TRANSLATE),
                $wc_order->get_id()
            );
        } catch(sibsException $e) {
            $back_data['error'] = 1;
            $back_data['msg']   = $e->getErrorMessage();
        }

        return $back_data;
    }

    /**
     * SIBS: Refund Async.
     *
     * @param int $order_id
     * @return array $back_data
     */
    public static function async_refund($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        sibsLogger::debug("[async_refund] order_id: $order_id", sibsConstants::SIBS_LOG);

        try {
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                throw new sibsException(
                    __('Order is not a SIBS payment', sibsConstants::SIBS_TRANSLATE)
                );
            }

            $payment_status   = 'wc-' . $wc_order->get_status();
            $status_to_refund = [
                sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING,
                sibsConstants::WOOCOMMERCE_ORDER_STATUS_COMPLETED,
                sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_CAPTURED,
                sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_REFUNDED,
            ];

            if (! in_array($payment_status, $status_to_refund)) {
                throw new sibsException(
                    sprintf(
                        __('Order is cannot be refunded in status %s', sibsConstants::SIBS_TRANSLATE),
                        esc_attr($payment_status)
                    )
                );
            }

            $sibs_payment  = sibsGeneralModels::sibs_get_db_transaction_log($order_id);
            $payment_type  = sibsConstants::SPG_PAYMENT_TYPE_RFND;
            $error_message = __('ERROR_GENERAL_REFUND_PAYMENT', sibsConstants::SIBS_TRANSLATE);
            $amount        = $wc_order->get_total();

            $backoffice_config = self::get_backoffice_config(
                $sibs_payment,
                $payment_type,
                $amount,
                $error_message
            );

            $backoffice_result = self::sibs_do_back_office_payment($backoffice_config);
            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'refunded_id'   => $backoffice_result['response']['transactionID'],
                'refund_status' => $backoffice_result['response']['paymentStatus'],
            ]);

            if (! $backoffice_result['is_valid']) {
                throw new sibsException(
                    __('Refund request has an ERROR', sibsConstants::SIBS_TRANSLATE)
                );
            }

            switch($backoffice_result['response']['paymentStatus']) {
                case sibsConstants::SIBS_RESULT_SUCCESS:
                    $refund_status = $backoffice_config['order_status'];
                    sibsLogger::debug('[async_refund] SUCCESS', sibsConstants::SIBS_LOG);

                    break;
                case sibsConstants::SIBS_RESULT_PENDING:
                    $refund_status = sibsConstants::SIBS_ORDER_STATUS_PENDING_REFUND;
                    sibsLogger::debug('[async_refund] PENDING', sibsConstants::SIBS_LOG);

                    break;
                default:
                    throw new sibsException(
                        __('Refund request denied by SIBS', sibsConstants::SIBS_TRANSLATE)
                    );
            }

            $amountMSG = "$amount {$wc_order->get_currency()}";

            $note_message = sprintf(
                __('Order is Pending for Refund with %s', sibsConstants::SIBS_TRANSLATE),
                $amountMSG
            );

            try {
                $wc_order->update_status($refund_status);
            } catch (\Throwable $t) {
                sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_LOG);
            }

            sibsGeneralModels::sibs_update_db_transaction($order_id, [
                'payment_status'     => $refund_status,
                'refunded_amount'    => $backoffice_result['response']['amount']['value'] + $sibs_payment['refunded_amount'],
                'refund_information' => $note_message . (! empty($sibs_payment['refund_information']) ? ', ' . $sibs_payment['refund_information'] : ''),
            ]);

            if ($backoffice_result['response']['paymentStatus'] === sibsConstants::SIBS_RESULT_PENDING) {
                $back_data['msg'] = sprintf(
                    __('BLIK Refund is asyncronous, you have to make a refund inquiry to get the real refund status', sibsConstants::SIBS_TRANSLATE),
                    $wc_order->get_id()
                );
            } else {
                $back_data['msg'] = sprintf(
                    __('Refund success for order #%s with %s', sibsConstants::SIBS_TRANSLATE),
                    $wc_order->get_id(),
                    $amountMSG
                );
            }
            $back_data['error'] = 0;
        } catch(sibsException $e) {
            $back_data['error'] = 1;
            $back_data['msg']   = $e->getErrorMessage();
        }

        return $back_data;
    }

    /**
     * Get Backoffice Config.
     *
     * @param string $sibs_payment
     * @param string $payment_type
     * @param float $amount
     * @param string $error_message
     * @return array
     */
    private static function get_backoffice_config($sibs_payment, $payment_type, $amount, $error_message = 'Sibs Error')
    {
        $reference_id = $sibs_payment['reference_id'];

        switch($payment_type) {
            case sibsConstants::SPG_PAYMENT_TYPE_RVSL:
            case sibsConstants::SPG_PAYMENT_TYPE_CAUT:
                $order_status = sibsConstants::WOOCOMMERCE_ORDER_STATUS_CANCELLED;

                break;
            case sibsConstants::SPG_PAYMENT_TYPE_RFND:
                $order_status = self::get_order_refunded_status($sibs_payment['order_no'], $sibs_payment['payment_id']);

                if ($sibs_payment['payment_type'] === sibsConstants::SPG_PAYMENT_TYPE_AUTH) {
                    $reference_id = $sibs_payment['captured_id'];
                }

                break;
            default:
                $order_status = sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING;

                break;
        }

        return [
            'order_no'      => $sibs_payment['order_no'],
            'payment_id'    => $sibs_payment['payment_id'],
            'reference_id'  => $reference_id,
            'currency'      => $sibs_payment['currency'],
            'payment_type'  => $payment_type,
            'order_status'  => $order_status,
            'error_message' => $error_message,
            'amount'        => $amount,
        ];
    }

    /**
     * SIBS Do Backoffice Payment.
     *
     * @param array $backoffice_config
     * @return array
     */
    private static function sibs_do_back_office_payment($backoffice_config)
    {
        $is_testmode_available     = true;
        $is_multichannel_available = true;

        $backoffice_parameter = sibsGeneralFunctions::sibs_get_credentials(
            $backoffice_config['payment_id'],
            $is_testmode_available,
            $is_multichannel_available
        );

        $backoffice_parameter = array_merge_recursive($backoffice_parameter, [
            'currency'     => $backoffice_config['currency'],
            'payment_type' => $backoffice_config['payment_type'],
            'amount'       => $backoffice_config['amount'],
        ]);

        $backoffice_result = \SibsPaymentCore::sibs_back_office_operation(
            $backoffice_config['reference_id'],
            $backoffice_parameter,
            $backoffice_config['order_no']
        );

        return $backoffice_result;
    }

    /**
     * SIBS add additional information.
     *
     * @param mixed $order
     * @return void
     */
    public function sibs_add_additional_information($order)
    {
        if (self::$added_meta_boxes) {
            return;
        }

        $order_id        = $order->get_id();
        $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

        if (! $is_sibs_payment) {
            return;
        }

        $args = [
            'transaction_log'        => sibsGeneralModels::sibs_get_db_transaction_log($order_id),
            'additional_information' => '',
        ];

        sibsGeneralFunctions::sibs_include_template(__DIR__ . '/../templates/admin/meta-boxes/template-additional-information.php', $args);
        self::$added_meta_boxes = true;
    }

    /**
     * SIBS Update Order Status.
     *
     * @param mixed $order
     * @return void
     */
    public function sibs_update_order_status($order)
    {
        $post_type = false;

        if (isset($_GET['post_type'])) {
            $post_type = wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['post_type'])));
        }

        if (! self::$updated_meta_boxes && $post_type !== sibsConstants::WOOCOMMERCE_POST_TYPE_SHOP_ORDER) {
            $order_id        = $order->get_id();
            $is_sibs_payment = self::sibs_is_sibs_by_order($order_id);

            if (! $is_sibs_payment) {
                return;
            }

            if ($order->get_status() === sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING) {
                $request_section = sibsGeneralFunctions::sibs_get_request_value('section', false);

                if ($request_section === 'update-order') {
                    $this->sibs_update_payment_status($order_id, sibsConstants::WOOCOMMERCE_ORDER_STATUS_ON_HOLD);
                    $redirect = get_admin_url() . "post.php?post=$order_id&action=edit";
                    wp_safe_redirect($redirect);

                    exit;
                }

                $args = ['order_id' => $order_id];
                sibsGeneralFunctions::sibs_include_template(__DIR__ . '/../templates/admin/meta-boxes/template-update-order.php', $args);
            }
            self::$updated_meta_boxes = true;
        }
    }

    /**
     * SIBS REST API Init.
     *
     * @return void
     */
    public function sibs_rest_api_init()
    {
        register_rest_route(
            'sibs-stg',
            '/webhook',
            [
                'permission_callback' => '__return_true',
                'methods'             => 'POST',
                'callback'            => [&$this, 'sibs_webhook'],
            ]
        );
    }

    /**
     * SIBS REST API Log.
     *
     * @return void
     */
    public function sibs_rest_api_log()
    {
        register_rest_route(
            'sibs-stg',
            '/data/log',
            [
                'permission_callback' => '__return_true',
                'methods'             => 'POST',
                'callback'            => [&$this, 'sibs_data_log'],
            ]
        );
    }

    /**
     * SIBS Add Payment Method.
     *
     * @param mixed $order
     * @return void
     */
    public function sibs_add_payment_method($order)
    {
        if (! self::$added_payment_method) {
            $action   = sibsGeneralFunctions::sibs_get_request_value('action', 'false');
            $order_id = $order->get_id();

            if (! empty($order_id)) {
                $args = ['action' => $action];
                sibsGeneralFunctions::sibs_include_template(__DIR__ . '/../templates/admin/meta-boxes/template-add-payment-method.php', $args);
            }

            self::$added_payment_method = true;
        }
    }

    /**
     * SIBS Data Log.
     *
     * @return void
     */
    public function sibs_data_log()
    {
        try {
            $strRequest = file_get_contents('php://input');
            sibsLogger::debug('DATA LOG: ' . sibsLogger::prettify($strRequest), sibsConstants::SIBS_DATA_LOG);
        } catch (Exception $e) {
            sibsLogger::error('DATA LOG: ' . $e->getMessage(), sibsConstants::SIBS_DATA_LOG);
        }
    }

    /**
     * SIBS Webhook.
     *
     * @return \Wp_REST_Response
     */
    public function sibs_webhook()
    {
        // DECRYPT MESSAGE
        $webhookMerchantUrl        = $_SERVER['REQUEST_URI'];
        $webhookHeaderInitVec      = $_SERVER['HTTP_X_INITIALIZATION_VECTOR'];
        $webhookHeaderAuthTag      = $_SERVER['HTTP_X_AUTHENTICATION_TAG'];
        $webhookHeaderSibsWebhook  = $_SERVER['sibs-webhook'] ?? null;
        $webhookBody               = file_get_contents('php://input');

        try {
            $webhook_call = sibsGeneralModels::sibs_get_db_webhook_call_by_payload($webhookBody);

            $webhook_call_id = is_null($webhook_call) ?
                self::create_webhook_call($webhookMerchantUrl, $webhookHeaderInitVec, $webhookHeaderAuthTag, $webhookBody) :
                self::update_webhook_call($webhook_call);

            sibsLogger::debug('WEBHOOK URL: ' . $webhookMerchantUrl, sibsConstants::SIBS_WEBHOOK_LOG);
            $webhook_data = (new sibsWebhookDecryptor($webhookBody, $webhookHeaderInitVec, $webhookHeaderAuthTag))->execute();
            $webhook_body = $webhook_data->getContent();

            sibsLogger::debug('WEBHOOK BODY DECODED: ' . sibsLogger::prettify($webhook_body), sibsConstants::SIBS_WEBHOOK_LOG);

            $notificationID = $webhook_data->getNotificationId();

            $webhook_update_data = [
                'notification_id' => $notificationID,
                'decoded_payload' => $webhook_body ?? [],
            ];

            $webhook_updated = sibsGeneralModels::sibs_update_db_webhook_calls($webhook_call_id, $webhook_update_data);

            if ($webhook_updated === false) {
                sibsLogger::warning('WEBHOOK Partially saved on DB', sibsConstants::SIBS_WEBHOOK_LOG);

                throw new Exception('Error in update webhook on DB');
            }

            sibsLogger::debug('WEBHOOK Fully saved on DB', sibsConstants::SIBS_WEBHOOK_LOG);

            return self::webhook_success_response($notificationID);
        } catch (Exception $e) {
            sibsLogger::critical('Fatal Error: ' . $e->getMessage(), sibsConstants::SIBS_WEBHOOK_LOG);

            $this->send_webhook_to_retry($webhookHeaderInitVec, $webhookHeaderAuthTag, $webhookBody, $webhookHeaderSibsWebhook);

            return self::webhook_unknown_error_response($e->getMessage());
        }
    }

    /**
     * Send Webhook to Retry.
     *
     * @param string $webhookHeaderInitVec
     * @param string $webhookHeaderAuthTag
     * @param string $webhookBody
     * @param string $webhookHeaderSibsWebhook
     * @return void
     */
    public function send_webhook_to_retry($webhookHeaderInitVec, $webhookHeaderAuthTag, $webhookBody, $webhookHeaderSibsWebhook)
    {
        if (! is_null($webhookHeaderSibsWebhook)) {
            sibsLogger::warning('Webhook will NOT send a retry request');

            return;
        }
        sibsLogger::info('Webhook will send a retry request');
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL            => self::$webhookRetryUrl,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING       => '',
            CURLOPT_MAXREDIRS      => 10,
            CURLOPT_TIMEOUT        => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION   => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST  => 'POST',
            CURLOPT_POSTFIELDS     => [
                'x_initialization_vector' => $webhookHeaderInitVec,
                'x_authentication_tag'    => $webhookHeaderAuthTag,
                'url_client'              => sibsConfig::getWebhookUrl(),
                'body'                    => $webhookBody,
            ],
        ]);

        curl_exec($curl);
        curl_close($curl);
    }

    /**
     * Create Webhook Call.
     *
     * @param string $url
     * @param string $ini_vec
     * @param string $auth_tag
     * @param string $payload
     * @return int
     */
    private function create_webhook_call($url, $ini_vec, $auth_tag, $payload)
    {
        sibsLogger::debug('WEBHOOK CREATE', sibsConstants::SIBS_WEBHOOK_LOG);

        $webhook_create_data = [
            'url'     => $url,
            'headers' => [
                'HTTP_X_INITIALIZATION_VECTOR' => $ini_vec,
                'HTTP_X_AUTHENTICATION_TAG'    => $auth_tag,
            ],
            'payload' => $payload,
        ];

        $webhook_call_id = sibsGeneralModels::sibs_save_db_webhook_calls($webhook_create_data);

        if ($webhook_call_id === false) {
            throw new Exception('Error in save webhook on DB');
        }

        return $webhook_call_id;
    }

    /**
     * update Webhook Call.
     *
     * @param array $webhook_call
     * @return int
     */
    private function update_webhook_call($webhook_call)
    {
        sibsLogger::debug('WEBHOOK UPDATE', sibsConstants::SIBS_WEBHOOK_LOG);

        $webhook_call_id = $webhook_call['id'];

        $webhook_update_data = [
            'arrived' => ($webhook_call['arrived'] + 1),
        ];

        $webhook_updated = sibsGeneralModels::sibs_update_db_webhook_calls($webhook_call_id, $webhook_update_data);

        if ($webhook_updated === false) {
            throw new Exception('Error in update webhook on DB');
        }

        return $webhook_call_id;
    }

    /**
     * Webhook Payment.
     *
     * @param array $sibs_transaction
     * @param string $payment_status
     * @param string $payment_type
     * @return bool
     */
    public static function webhook_payment($sibs_transaction, $payment_status, $payment_type)
    {
        $order        = wc_get_order($sibs_transaction['order_no']);
        $order_status = sibsGeneralFunctions::convertTransactionStatus($payment_status, $payment_type);

        sibsGeneralModels::sibs_update_db_transaction_by_reference_id($sibs_transaction['reference_id'], [
            'payment_status' => $order_status,
        ]);

        sibsLogger::debug("WEBHOOK order_status: $order_status", sibsConstants::SIBS_CRON_LOG);

        if ($order->get_status() === false) {
            // code...
            return true;
        }

        if (sibsGeneralFunctions::isAlreadyPaidOrAuthorized('wc-' . $order->get_status())) {
            // Don't need to change the status
            return true;
        }

        switch($order_status) {
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING:
            case sibsConstants::SIBS_ORDER_STATUS_AUTHORIZED:
                try {
                    $order->update_status($order_status);
                } catch (\Throwable $t) {
                    sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_CRON_LOG);
                }

                sibsLogger::debug('Reduce Stock webhook', sibsConstants::SIBS_CRON_LOG);
                wc_reduce_stock_levels($order->get_id());

                break;
            case sibsConstants::WOOCOMMERCE_ORDER_STATUS_ON_HOLD:
                try {
                    $order->update_status($order_status);
                } catch (\Throwable $t) {
                    sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_CRON_LOG);
                }

                wc_increase_stock_levels($order->get_id());

                break;
            default:
                $errorMessage = 'WEBHOOK operation failed.';
                $order->add_order_note($errorMessage);
        }

        sibsLogger::debug('WEBHOOK Payment operation with success', sibsConstants::SIBS_CRON_LOG);

        return true;
    }

    /**
     * Webhook Capture.
     *
     * @param array $sibs_transaction
     * @param string $capture_status
     * @param float $amount_value
     * @return bool
     */
    public static function webhook_capture($sibs_transaction, $capture_status, $amount_value)
    {
        $order = wc_get_order($sibs_transaction['order_no']);

        if (sibsGeneralFunctions::isAlreadyCaptured('wc-' . $order->get_status())) {
            // Don't need to change the status
            return true;
        }

        $note_message = ($capture_status === sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED) ?
            __('WEBHOOK Capture operation failed', sibsConstants::SIBS_TRANSLATE) :
            sprintf(
                __('Order was Captured for %s', sibsConstants::SIBS_TRANSLATE),
                $amount_value . ' ' . $order->get_currency()
            );

        $orderRemainingCapture = $order->get_total() - $sibs_transaction['captured_amount'];
        sibsLogger::debug('WEBHOOK order remaining Capture: ' . $orderRemainingCapture, sibsConstants::SIBS_CRON_LOG);

        $order_status = ($amount_value >= $orderRemainingCapture) ?
            sibsConstants::WOOCOMMERCE_ORDER_STATUS_PROCESSING :
            sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_CAPTURED;

        try {
            $order->update_status($order_status);
        } catch (\Throwable $t) {
            sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_CRON_LOG);
        }

        $order->add_order_note($note_message);

        sibsGeneralModels::sibs_update_db_transaction($sibs_transaction['order_no'], [
            'payment_status'      => $order_status,
            'capture_status'      => sibsGeneralFunctions::convertTransactionStatusCaptRfnd($capture_status),
            'captured_amount'     => $amount_value + $sibs_transaction['captured_amount'],
            'capture_information' => $note_message .
                (! empty($sibs_transaction['capture_information']) ? ', ' . $sibs_transaction['capture_information'] : ''),
        ]);

        sibsLogger::debug('WEBHOOK Refund operation with success', sibsConstants::SIBS_CRON_LOG);

        return true;
    }

    /**
     * Webhook Refund.
     *
     * @param array $sibs_transaction
     * @param string $refund_status
     * @param float $amount_value
     * @return bool
     */
    public static function webhook_refund($sibs_transaction, $refund_status, $amount_value)
    {
        $order = wc_get_order($sibs_transaction['order_no']);

        if (sibsGeneralFunctions::isAlreadyRefunded('wc-' . $order->get_status())) {
            // Don't need to change the status
            return true;
        }

        $note_message = ($refund_status === sibsConstants::WOOCOMMERCE_ORDER_STATUS_FAILED) ?
            __('WEBHOOK Refund operation failed', sibsConstants::SIBS_TRANSLATE) :
            sprintf(
                __('Order was Refunded for %s', sibsConstants::SIBS_TRANSLATE),
                $amount_value . ' ' . $order->get_currency()
            );

        $orderRemainingRefund = $order->get_total() - $order->get_total_refunded();
        sibsLogger::debug('WEBHOOK order remaining Refund: ' . $orderRemainingRefund, sibsConstants::SIBS_CRON_LOG);

        $order_status = ($amount_value >= $orderRemainingRefund) ?
            sibsConstants::WOOCOMMERCE_ORDER_STATUS_REFUNDED :
            sibsConstants::SIBS_ORDER_STATUS_PARTIALLY_REFUNDED;

        try {
            $order->update_status($order_status);
        } catch (\Throwable $t) {
            sibsLogger::critical($t->getMessage(), sibsConstants::SIBS_CRON_LOG);
        }

        $order->add_order_note($note_message);

        sibsGeneralModels::sibs_update_db_transaction($sibs_transaction['order_no'], [
            'payment_status'     => $order_status,
            'refund_status'      => sibsGeneralFunctions::convertTransactionStatusCaptRfnd($refund_status),
            'refunded_amount'    => $amount_value + $sibs_transaction['refunded_amount'],
            'refund_information' => $note_message .
                (! empty($sibs_transaction['refund_information']) ? ', ' . $sibs_transaction['refund_information'] : ''),
        ]);

        sibsLogger::debug('WEBHOOK Refund operation with success', sibsConstants::SIBS_CRON_LOG);

        return true;
    }

    /**
     * Webhook Success Response.
     *
     * @param string $notificationId
     * @return \WP_REST_Response
     */
    private static function webhook_success_response($notificationId)
    {
        $success_code = 200;

        return new \WP_REST_Response([
            'status'         => $success_code,
            'response'       => __(sibsConstants::SIBS_RESULT_SUCCESS),
            'notificationID' => $notificationId,
        ], $success_code, [
            'Content-Type' => 'application/json; charset=utf-8',
        ]);
    }

    /**
     * Webhook Unknown Error Response.
     *
     * @param string $error_message
     * @param int $error_code
     * @return \WP_REST_Response
     */
    private static function webhook_unknown_error_response($error_message, $error_code = 500)
    {
        return self::webhook_error_response($error_message, $error_code);
    }

    /**
     * Webhook Error Response.
     *
     * @param string $error_message
     * @param int $error_code
     * @return \WP_REST_Response
     */
    private static function webhook_error_response($error_message, $error_code)
    {
        return new \WP_REST_Response([
            'status'   => $error_code,
            'response' => __($error_message),
        ], $error_code, [
            'Content-Type' => 'application/json; charset=utf-8',
        ]);
    }

    /**
     * SIBS Add Customer Note.
     *
     * @param int $payment_id
     * @param int $order_id
     * @return void
     */
    private function sibs_add_customer_note($payment_id, $order_id)
    {
        $wc_order      = new \WC_Order($order_id);
        $customer_note = $wc_order->get_customer_note();

        $new_line         = "\n";
        $payment_comments = sibsGeneralFunctions::sibs_translate_frontend_payment($payment_id) . $new_line;

        if (! empty($wc_order->get_customer_note())) {
            $customer_note .= $new_line;
        }

        $customer_note .= html_entity_decode($payment_comments, ENT_QUOTES, 'UTF-8');
        $order_notes = [
            'ID'           => $wc_order->get_id(),
            'post_excerpt' => $customer_note,
        ];
        wp_update_post($order_notes);
    }

    /**
     * SIBS Set Customer Parameters.
     *
     * @param int $order_id
     * @return array
     */
    private function sibs_set_customer_parameters($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        $customer = [
            'first_name' => $wc_order->get_billing_first_name(),
            'last_name'  => $wc_order->get_billing_last_name(),
            'full_name'  => $wc_order->get_billing_first_name() . ' ' . $wc_order->get_billing_last_name(),
            'email'      => $wc_order->get_billing_email(),
        ];

        $phone_payments = [
            sibsConstants::SIBS_PAYMENT_METHOD_ORDER_BLIK,
            sibsConstants::SIBS_PAYMENT_METHOD_ORDER_PAY_BY_LINK,
            sibsConstants::SIBS_PAYMENT_METHOD_ORDER_PAY_BY_LINK_KEVIN,
        ];

        if (in_array($this->payment_id, $phone_payments)) {
            $customer['phone'] = $wc_order->get_billing_phone();
        }

        return $customer;
    }

    /**
     * SIBS Set Billing Parameters.
     *
     * @param int $order_id
     * @return array
     */
    private function sibs_set_billing_parameters($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        if (! $wc_order->has_billing_address()) {
            return [
                'country_code' => $wc_order->get_shipping_country(),
                'city'         => $wc_order->get_shipping_city(),
                'zip'          => $wc_order->get_shipping_postcode(),
                'address_1'    => $wc_order->get_shipping_address_1(),
                'address_2'    => $wc_order->get_shipping_address_2(),
                'street'       => $wc_order->get_formatted_shipping_address(),
            ];
        }

        return [
            'country_code' => $wc_order->get_billing_country(),
            'city'         => $wc_order->get_billing_city(),
            'zip'          => $wc_order->get_billing_postcode(),
            'address_1'    => $wc_order->get_billing_address_1(),
            'address_2'    => $wc_order->get_billing_address_2(),
            'street'       => $wc_order->get_formatted_billing_address(),
        ];
    }

    /**
     * SIBS Set Shipping Parameters.
     *
     * @param int $order_id
     * @return array
     */
    private function sibs_set_shipping_parameters($order_id)
    {
        $wc_order = new \WC_Order($order_id);

        if (! $wc_order->has_shipping_address()) {
            return [
                'country_code' => $wc_order->get_billing_country(),
                'city'         => $wc_order->get_billing_city(),
                'zip'          => $wc_order->get_billing_postcode(),
                'address_1'    => $wc_order->get_billing_address_1(),
                'address_2'    => $wc_order->get_billing_address_2(),
                'street'       => $wc_order->get_formatted_billing_address(),
            ];
        }

        return [
            'country_code' => $wc_order->get_shipping_country(),
            'city'         => $wc_order->get_shipping_city(),
            'zip'          => $wc_order->get_shipping_postcode(),
            'address_1'    => $wc_order->get_shipping_address_1(),
            'address_2'    => $wc_order->get_shipping_address_2(),
            'street'       => $wc_order->get_formatted_shipping_address(),
        ];
    }

    /**
     * SIBS Set Payment Parameters.
     *
     * @param int $order_id
     * @return array
     */
    protected function sibs_set_payment_parameters($order_id)
    {
        $wc_order     = new \WC_Order($order_id);
        $order_number = $wc_order->get_order_number();

        //sibsLogger::info('-----------------------wc_order-----------------------', sibsConstants::SIBS_LOG);
        //sibsLogger::info(print_r($wc_order, true), sibsConstants::SIBS_LOG);
        //sibsLogger::info('-----------------------wc_order-----------------------', sibsConstants::SIBS_LOG);

        $terminalId             = intval(sibsConfig::getTerminalId());
        $transactionDescription = "Transaction for order number $order_number terminalId $terminalId";
        $shopURL                = site_url('/'); // get_permalink(wc_get_page_id('shop'));

        $this->payment_type = sibsGeneralFunctions::sibs_get_payment_type($this->payment_id);

        $customer_data     = $this->sibs_set_customer_parameters($order_id);
        $custumer_language = sibsFunctions::get_language();

        $custumer_info_shipping = $this->sibs_set_shipping_parameters($order_id);
        $custumer_info_billing  = $this->sibs_set_billing_parameters($order_id);

        $payment_parameters = [
            'merchant' => [
                'terminalId'             => $terminalId,
                'channel'                => sibsConfig::getChannel(),
                'merchantTransactionId'  => $order_number,
                'transactionDescription' => $transactionDescription,
                'shopURL'                => $shopURL,
            ],
            'transaction' => [
                'transactionTimestamp' => wp_date('Y-m-d\TH:i:s.vP'),
                'description'          => $transactionDescription,
                'moto'                 => sibsConfig::getMoto(),
                'paymentMethod'        => [
                    sibsGeneralFunctions::sibs_spg_get_payment_method($this->payment_id),
                ],
                'paymentType'          => $this->payment_type,
                'amount'               => [
                    'value'    => floatval(sibsFunctions::sibs_set_number_format($this->get_order_total())),
                    'currency' => get_woocommerce_currency(),
                ],
            ],

            'info' => [
                'deviceInfo' => [
                    'applicationName'    => sibsConfig::getEcommerceName() . ':' . sibsConfig::getEcommerceVersion(),
                    'applicationVersion' => sibsConfig::getPluginVersion(),
                ],
            ],
        ];
        $payment_parameters['customer']['customerInfo'] = [
            'customerName'     => $customer_data['full_name'],
            'customerEmail'    => $customer_data['email'],
            'customerLanguage' => $custumer_language,
        ];

        // Verifica se há informações de envio
        if (! empty($custumer_info_shipping['country_code']) && ! empty($custumer_info_shipping['city'])) {
            $payment_parameters['customer']['customerInfo']['shippingAddress'] = [
                'street1'  => $custumer_info_shipping['address_1'],
                'street2'  => $custumer_info_shipping['address_2'],
                'city'     => $custumer_info_shipping['city'],
                'postcode' => $custumer_info_shipping['zip'],
                'country'  => $custumer_info_shipping['country_code'],
            ];
        }

        // Verifica se há informações de cobrança
        if (! empty($custumer_info_billing['country_code']) && ! empty($custumer_info_billing['city'])) {
            $payment_parameters['customer']['customerInfo']['billingAddress'] = [
                'street1'  => $custumer_info_billing['address_1'],
                'street2'  => $custumer_info_billing['address_2'],
                'city'     => $custumer_info_billing['city'],
                'postcode' => $custumer_info_billing['zip'],
                'country'  => $custumer_info_billing['country_code'],
            ];
        }

        $payment_parameters = array_merge_recursive($payment_parameters, $this->sibs_set_payment_parameters_by_payment_method());

        if (get_current_user_id()) {
            $payment_parameters = array_merge_recursive($payment_parameters, $this->get_checkout_parameter_oneclick());
        }

        sibsLogger::info('payment_parameters: ' . sibsLogger::prettify($payment_parameters), sibsConstants::SIBS_LOG);

        return $payment_parameters;
    }

    /**
     * SIBS Set Payment Parameters By Payment Method.
     *
     * @return void
     */
    private function sibs_set_payment_parameters_by_payment_method()
    {
        $parameters = [];

        sibsLogger::info('sibs_set_payment_parameters_by_payment_method: ' . $this->payment_id, sibsConstants::SIBS_LOG);

        switch($this->payment_id) {
            case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_PAY_BY_LINK:
            case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_PAY_BY_LINK_KEVIN:
                $abs_number  = abs(intval(sibsConstants::SIBS_PAYMENT_DATE_VALUE));
                $date_offset = strtotime('+' . $abs_number . ' ' . sibsConstants::SIBS_PAYMENT_DATE_UNIT);
                // TODO: algo problema aqui
                $expirationDate = sibsConfig::getStoreCurrentTime("Y-m-d\TH:i:s.vP", false, false, 3, 1);

                $parameters = array_merge_recursive($parameters, [
                    'transaction' => [
                        'payByLink' => [
                            'expirationDateTime'     => $expirationDate,
                            'linkExpirationDateTime' => $expirationDate,
                        ],
                    ],
                ]);

                break;
            case sibsConstants::SIBS_PAYMENT_METHOD_ORDER_CREDIT_CARD:
                $credentials   = sibsGeneralFunctions::sibs_get_credentials($this->payment_id);
                $registrations = sibsGeneralModels::sibs_get_db_registered_payment($this->payment_group, $credentials);
                $parameters    = array_merge_recursive($parameters, $this->get_payment_registrations($registrations));

                break;
            default:
                $parameters = array_merge_recursive($parameters, []);

                break;
        }

        return $parameters;
    }

    /**
     * Get Payment Registrations.
     *
     * @param array $registrations
     * @return array
     */
    private function get_payment_registrations($registrations)
    {
        $parameters = [];

        if (! empty($registrations)) {
            foreach ($registrations as $key => $value) {
                $parameters["registrations[$key].id"] = $value['reg_id'];
            }
        }

        return $parameters;
    }

    /**
     * Get Checkout Parameter Oneclick.
     *
     * @return array
     */
    private function get_checkout_parameter_oneclick()
    {
        $oneclickParameters = [];
        $tokenMethods       = [
            sibsConstants::SIBS_PAYMENT_METHOD_CARD,
        ];

        if (in_array(sibsGeneralFunctions::sibs_spg_get_payment_method($this->payment_id), $tokenMethods)) {
            $oneclick = sibsConfig::getPaymentOption(sibsConstants::SIBS_PAYMENT_METHOD_ORDER_CREDIT_CARD, 'tokenization');

            switch($oneclick) {
                case sibsConstants::SIBS_TOKEN_TOKENIZATION:
                    $tokeniseCard       = true;
                    $customerAcceptance = false;
                    $paymentTokens      = sibsGeneralModels::sibs_get_current_user_card_tokens();

                    break;
                case sibsConstants::SIBS_TOKEN_ONECLICK:
                    if (WC()->session->__isset(sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor')) {
                        $tokeniseCard       = WC()->session->get(sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor');
                        $customerAcceptance = WC()->session->get(sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor');
                        $paymentTokens      = WC()->session->get(sibsConstants::SIBS_PLUGIN_PREFIX . '_oneclick_customer_acceptor') ?
                            sibsGeneralModels::sibs_get_current_user_card_tokens() :
                            [];

                        break;
                    }
                case sibsConstants::SIBS_TOKEN_NONE:
                default:
                    $tokeniseCard       = false;
                    $customerAcceptance = false;
                    $paymentTokens      = [];
            }

            $oneclickParameters = [
                'transaction' => [
                    'oneClick' => [
                        'customerAcceptance' => $customerAcceptance,
                    ],
                ],
                'tokenisation' => [
                    'tokenisationRequest' => [
                        'tokeniseCard' => $tokeniseCard,
                    ],
                    'paymentTokens' => $paymentTokens,
                ],
            ];
        }

        return $oneclickParameters;
    }

    /**
     * SIBS Save Or Update Transactions.
     *
     * @param int $order_id
     * @param array $payment_result
     * @param bool $is_confirm_page
     * @return void
     */
    protected function sibs_save_or_update_transactions($order_id, $payment_result, $is_confirm_page = false)
    {
        $local_payment_method = ! empty($payment_result['paymentMethod']) ?
                $payment_result['paymentMethod'] :
                $this->payment_brand;

        $wc_order = new \WC_Order($order_id);
        $this->sibs_add_customer_note($this->payment_id, $order_id);

        $payment_status = sibsGeneralFunctions::convertTransactionStatus(
            $payment_result['paymentStatus'],
            $this->payment_type
        );
        sibsLogger::debug("payment_status: {$payment_status}", sibsConstants::SIBS_LOG);

        $transaction = [
            'order_no'               => $wc_order->get_order_number(),
            'customer_id'            => ($wc_order->get_user_id()) ? $wc_order->get_user_id() : get_current_user_id(),
            'reference_id'           => $payment_result['transactionID'],
            'payment_id'             => $this->payment_id,
            'payment_type'           => $this->payment_type,
            'payment_brand'          => $local_payment_method,
            'payment_status'         => $payment_status,
            'date'                   => sibsConfig::getStoreCurrentTime(),
            'currency'               => get_woocommerce_currency(),
            'amount'                 => sibsFunctions::sibs_set_number_format($this->get_order_total()),
            'additional_information' => '',
        ];

        if (! empty($payment_result['formConfig'])) {
            $transaction['formConfig'] = $payment_result['formConfig'];
        }

        if (! empty($payment_result['signature'])) {
            $transaction['signature'] = $payment_result['signature'];
        }

        if (! empty($payment_result['spg_style'])) {
            $transaction['spg_style'] = $payment_result['spg_style'];
        }

        if (! empty($payment_result['formContext'])) {
            $transaction['formContext'] = $payment_result['formContext'];
        }

        sibsLogger::debug('transaction: ' . sibsLogger::prettify($transaction), sibsConstants::SIBS_LOG);

        $transaction_saved = sibsGeneralModels::sibs_create_or_update_db_transaction($transaction);
        sibsLogger::info('transaction_saved: ' . ($transaction_saved ? 'yes' : 'no'), sibsConstants::SIBS_LOG);

        if ($is_confirm_page) {
            // save credit card info (!This won't happen in SPG because card info is never sent!)
            $this->sibs_save_credit_card_info($payment_result, $local_payment_method);

            // save token info
            $this->sibs_token_info($payment_result, $local_payment_method);
        }
    }

    /**
     * SIBS Save Credit Card Info.
     *
     * @param array $payment_result
     * @param string $local_payment_method
     * @return void
     */
    protected function sibs_save_credit_card_info($payment_result, $local_payment_method)
    {
        if (isset($payment_result['card']) && isset($payment_result['registrationId'])) {
            //get cards from user
            $credentials = sibsGeneralFunctions::sibs_get_credentials($this->payment_id);

            $payment_parameters['registrations'] = sibsGeneralModels::sibs_get_db_registered_payment($this->payment_group, $credentials);

            $cardExist = false;

            //if there is cards for the user
            if ($payment_parameters['registrations'] !== null) {
                foreach ($payment_parameters['registrations'] as $value) {
                    // if the card dont exist
                    if ($value['last4digits'] == $payment_result['card']['last4Digits'] &&
                      $value['brand'] == $local_payment_method &&
                      $value['expiry_month'] == $payment_result['card']['expiryMonth'] &&
                      $value['expiry_year'] == $payment_result['card']['expiryYear'] &&
                      $value['holder'] == $payment_result['card']['holder']
                    ) {
                        $cardExist = true;

                        break;
                    }
                }

                // if the card does not exist, add
                if ($cardExist == false) {
                    $recurring_saved = $this->sibs_save_recurring_card_payment($payment_result);
                    sibsLogger::info('recurring_saved (1): ' . ($recurring_saved ? 'yes' : 'no'), sibsConstants::SIBS_LOG);
                }
            } else {
                $recurring_saved = $this->sibs_save_recurring_card_payment($payment_result);
                sibsLogger::info('recurring_saved (2): ' . ($recurring_saved ? 'yes' : 'no'), sibsConstants::SIBS_LOG);
            }
        }
    }

    /**
     * SIBS Save Recurring Card Payment.
     *
     * @param array $payment_result
     * @return bool
     */
    protected function sibs_save_recurring_card_payment($payment_result)
    {
        $is_registered_payment = sibsGeneralModels::sibs_is_registered_payment_db($payment_result['registrationId']);

        if (! $is_registered_payment) {
            $credentials                           = sibsGeneralFunctions::sibs_get_credentials($this->payment_id);
            $registered_payment['payment_group']   = $this->payment_group;
            $registered_payment['payment_brand']   = $payment_result['paymentBrand'];
            $registered_payment['server_mode']     = $credentials['server_mode'];
            $registered_payment['channel_id']      = $credentials['channel_id'];
            $registered_payment['registration_id'] = $payment_result['registrationId'];
            $registered_payment['payment_default'] = sibsGeneralModels::sibs_get_db_payment_default($this->payment_group, $credentials);

            // Credit Card
            $registered_payment['holder']          = $payment_result['card']['holder'];
            $registered_payment['email']           = $payment_result['customer']['email'];
            $registered_payment['last_4_digits']   = $payment_result['card']['last4Digits'];
            $registered_payment['expiry_month']    = $payment_result['card']['expiryMonth'];
            $registered_payment['expiry_year']     = $payment_result['card']['expiryYear'];

            sibsLogger::debug("registered_payment: $registered_payment", sibsConstants::SIBS_LOG);

            return sibsGeneralModels::sibs_save_db_registered_payment($registered_payment);
        }

        return true;
    }

    /**
     * SIBS Token Info.
     *
     * @param array $payment_result
     * @return void
     */
    protected function sibs_token_info($payment_result)
    {
        $hasToken = isset($payment_result['token']);
        sibsLogger::info('hasToken ' . var_export($hasToken, true), sibsConstants::SIBS_LOG);

        if ($hasToken) {
            $token = $payment_result['token'];
            sibsLogger::debug('token: ' . sibsLogger::prettify($token), sibsConstants::SIBS_LOG);

            $token_saved = sibsGeneralModels::sibs_save_or_update_db_token($token);
            sibsLogger::info('token_saved: ' . ($token_saved ? 'yes' : 'no'), sibsConstants::SIBS_LOG);
        }
    }

    /**
     * SIBS Add Order Notes.
     *
     * @param int $order_id
     * @param string $status_name
     * @return void
     */
    private function sibs_add_order_notes($order_id, $status_name)
    {
        $user = get_user_by('id', get_current_user_id());

        $comments['order_id'] = $order_id;
        $comments['author']   = $user->display_name;
        $comments['email']    = $user->user_email;
        $comments['content']  = sprintf(
            __('Order status changed from %s to %s', sibsConstants::SIBS_TRANSLATE),
            $status_name['original'],
            $status_name['new']
        );

        sibsGeneralModels::sibs_save_db_order_notes($comments);
    }

    /**
     * Checkout Result to Data.
     *
     * @param array $checkout_response
     * @return array
     */
    public function checkout_result_to_data($checkout_response)
    {
        return [
            'transactionID' => $checkout_response['transactionID'],
            'paymentStatus' => sibsConstants::SIBS_RESULT_PENDING,
            'paymentMethod' => $checkout_response['paymentMethodList'][0] ?? '',
            'formConfig'    => $checkout_response['formConfig'] ?? null,
            'signature'     => $checkout_response['transactionSignature'] ?? null,
            'spg_style'     => $checkout_response['spg_style'] ?? null,
            'formContext'   => $checkout_response['formContext'] ?? null,
        ];
    }

    /**
     * SIBS Status Inquiry Order Add Button.
     *
     * @param mixed $order
     * @return void
     */
    public static function sibs_status_inquiry_order_add_button($order)
    {
        $order_id = esc_attr($order->get_id());
        self::print_button($order_id, '1', esc_attr(__('SIBS Check Status', sibsConstants::SIBS_TRANSLATE)));
    }

    /**
     * SIBS Capture Order Add Button.
     *
     * @param mixed $order
     * @return void
     */
    public static function sibs_capture_order_add_button($order)
    {
        $order_id = esc_attr($order->get_id());
        self::print_button($order_id, '2', esc_attr(__('SIBS Capture', sibsConstants::SIBS_TRANSLATE)));
    }

    /**
     * SIBS Refund Inquiry Order Add Button.
     *
     * @param mixed $order
     * @return void
     */
    public static function sibs_refund_inquiry_order_add_button($order)
    {
        $order_id = esc_attr($order->get_id());
        self::print_button($order_id, '3', esc_attr(__('SIBS Refund Inquiry', sibsConstants::SIBS_TRANSLATE)));
    }

    /**
     * SIBS Cancel Authorization Order Add Button.
     *
     * @param mixed $order
     * @return void
     */
    public static function sibs_cancel_authorization_order_add_button($order)
    {
        $order_id = esc_attr($order->get_id());
        self::print_button($order_id, '4', esc_attr(__('SIBS Cancel Authorization', sibsConstants::SIBS_TRANSLATE)));
    }

    /**
     * SIBS Refund Async Order Add Button.
     *
     * @param mixed $order
     * @return void
     */
    public static function sibs_refund_async_order_add_button($order)
    {
        $order_id = esc_attr($order->get_id());
        self::print_button($order_id, '5', esc_attr(__('SIBS Async Refund', sibsConstants::SIBS_TRANSLATE)));
    }

    /**
     * Print Button.
     *
     * @param int $order_id
     * @param string $button_id
     * @param string $label
     * @return void
     */
    public static function print_button($order_id, $button_id, $label)
    {
        ?>
            <button
                type="button"
                style="margin-right: 2px"
                class="button sibs-button"
                data-order_id="<?= $order_id ?>"
                data-button_id="<?= $button_id ?>">
                <b><?= $label ?></b>
            </button>
        <?php
    }

    /**
     * Print Input.
     *
     * @param string $scenario
     * @param string $class
     * @param string $name
     * @param string $placeholder
     * @param string $type
     * @param string $value
     * @return void
     */
    public static function print_input($scenario, $class, $name, $placeholder = 'Enter Value', $type = 'text', $value = '')
    {
        ?>
            <input
                type="<?= $type ?>"
                style="margin-right: 2px"
                class="<?= $class ?>"
                name="<?= $name ?>"
                data-scenario="<?= $scenario ?>"
                placeholder="<?= $placeholder ?>"
                value="<?= $value ?>"/>
        <?php
    }
}
