<?php

namespace Hyperpay\Gateways\App;

if (!defined('ABSPATH')) exit;


use Exception;
use Hyperpay\Gateways\Helpers\Http;
use Hyperpay\Gateways\Helpers\Log;
use Hyperpay\Gateways\Helpers\SubscriptionsManager;
use Hyperpay\Gateways\Helpers\TokenManager;
use Hyperpay\Gateways\Helpers\View;
use Hyperpay\Gateways\Main;
use Hyperpay\Gateways\Traits\HyperpayBlocks;
use WC_Order;
use WC_Payment_Gateway;
use Hyperpay\Gateways\Traits\HasTokenization;


/**
 * Hyperpay main class created to extends from it
 * when create a new payments Gateways
 *
 */
class DefaultGateway extends WC_Payment_Gateway
{
    use HyperpayBlocks;

    /**
     * Gateway admin options
     */
    public $testMode, $title, $trans_type;
    public $accessToken, $entityId, $brands, $order_status;
    public $custom_style, $latin_validation;
    public $currency, $is_arabic, $supported_network;
    public $server_to_server = false;


    /**
     * connecter type on test mode
     *
     * @var string INTERNAL|EXTERNAL
     */

    public $trans_mode = 'EXTERNAL';
    public $description;
    public $instructions;
    public $form_fields = [];

    /**
     * if payments have direct fields on checkout page
     *
     * @var boolean
     */
    public $has_fields = false;

    /**
     * used to display the invoice id
     * at success order page
     * @var string|null
     */
    public $invoice_id;


    public $id;

    /**
     * Mada BlackBins
     *
     * @var array
     */
    protected $blackBins = [];

    /**
     * supported brands thats will showing on settings and checkout page
     *
     * @var array
     */
    protected $supported_brands = [];

    /**
     * displayed error msg
     * @var string
     */
    protected $failed_message = '';

    /**
     * displayed success msg
     * @var string
     */
    protected $success_message = '';

    public $NONCE = '';

    /**
     * regular expressions
     * @var string
     */

    public $successCodePattern = '/^(000\.000\.|000\.100\.1|000\.[36])/';
    public $successManualReviewCodePattern = '/^(000\.400\.0|000\.400\.100)/';
    public $pendingCodePattern = '/^(800\.400\.5|100\.400\.500)/';

    /**
     * CopyAndPay script URL
     *
     * @var string
     */
    public $script_url = "https://eu-prod.oppwa.com/v1/paymentWidgets.js?checkoutId=";

    /**
     * CopyAndPay prepare checkout link
     *
     * @method POST
     * @var string
     */
    protected $token_url = "https://eu-prod.oppwa.com/v1/checkouts";


    /**
     * get transaction status
     * @method GET
     * @var string
     *
     * ##TOKEN## will replace with transaction id when fire the request
     */
    protected $transaction_status_url = "https://eu-prod.oppwa.com/v1/checkouts/##TOKEN##/payment";

    /**
     * back-office end-point
     * @method GET
     * @var string
     *
     */
    protected $server_to_server_url = "https://eu-prod.oppwa.com/v1/payments";

    /**
     * Query transaction report
     *
     * @method GET
     * @var string
     */
    protected $query_url = "https://eu-prod.oppwa.com/v1/query";


    protected $ACI_base_url = "https://eu-prod.oppwa.com";
    /**
     * payment styles that will show in settings
     *
     * @var array
     *
     */
    protected $payment_style = [
        'card' => 'Card',
        'plain' => 'Plain'
    ];


    public function boot() {}

    public function __construct()
    {

        $this->init_settings(); // <== to get saved settings from database
        $this->init_form_fields(); // <== render form inside admin panel
        $this->is_arabic = substr(get_locale(), 0, 2) == 'ar'; // <== to get current locale

        $this->testMode = $this->get_option('testmode'); // <== check if payments on test mode
        $this->title = $this->get_option('title'); // <== get title from setting
        $this->trans_type = $this->get_option('trans_type'); // <== get transaction type [DB / Pre-Auth] from setting
        $this->accessToken = $this->get_option('accesstoken'); // <== get access toke from setting
        $this->entityId = $this->getEntity(); // <== get entityId from setting

        $this->brands = is_array($this->get_option('hyper_pay_brands')) ? $this->get_option('hyper_pay_brands') : [$this->get_option('hyper_pay_brands')]; // <== get brands from setting

        $this->payment_style = $this->get_option('payment_style'); // <== get style from setting

        $this->order_status = $this->get_option('order_status'); // <== get order status after success from setting
        $this->custom_style = $this->get_option('custom_style'); // <== get custom style from setting

        $this->description = __('All transactions are processed in a secure environment.', 'hyperpay-gateways');


        $this->latin_validation = $this->get_option('latin_validation'); // <== get custom style from setting
        $this->currency = get_woocommerce_currency();

        $this->NONCE = \md5(wp_rand(1111111111, 9999999999));


        /**
         * if test mode is one
         * overwrite currents URLs ti test URLs
         */
        if ($this->testMode) {
            $this->query_url = "https://eu-test.oppwa.com/v1/query";
            $this->token_url = "https://eu-test.oppwa.com/v1/checkouts";
            $this->script_url = "https://eu-test.oppwa.com/v1/paymentWidgets.js?checkoutId=";
            $this->transaction_status_url = "https://eu-test.oppwa.com/v1/checkouts/##TOKEN##/payment";
            $this->server_to_server_url = "https://eu-test.oppwa.com/v1/payments";
            $this->ACI_base_url = "https://eu-test.oppwa.com";
        }

        $this->query_url .= "?entityId=" . $this->entityId;
        $this->transaction_status_url .= "?entityId=" . $this->entityId;

        /**
         * default failed message
         * @var string
         */
        $this->failed_message = __('Your transaction not completed .', 'hyperpay-gateways');
        $this->success_message = __('Your payment has been processed successfully.', 'hyperpay-gateways');

        /**
         * overwrite default update function
         *
         * @param woocommerce_update_options_payment_gateways_<payment_id>
         * @param array[class,function_name]
         */

        if (!has_action("woocommerce_update_options_payment_gateways_{$this->id}")) {
            add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
        }


        /**
         * prepare checkout form
         *
         * @param string woocommerce_receipt_<payments_id>
         * @param array[class,function_name]
         */

        if (!has_action("woocommerce_receipt_{$this->id}")) {
            add_action("woocommerce_receipt_{$this->id}", [$this, 'receipt_page']);
        }


        /**
         * set payments icon from src/assets/images/BRAND-log.png
         *
         * make sure when add new image to rename image according this format BRAND_NAME-logo.svg
         *
         * @param string woocommerce_gateway_icon
         * @param array[class,function_name]
         *
         */
        add_filter('woocommerce_gateway_icon', [$this, 'set_icons'], 10, 2);

        /**
         * to include src/assets/js/admin.js <JavaScript>
         *
         * @param string admin_enqueue_scripts
         * @param array[class,function_name]
         *
         */
        add_action('admin_enqueue_scripts', [$this, 'admin_script']);

        add_action("woocommerce_thankyou_order_received_text", [$this, "order_received_text"], 10, 2);

        add_action('before_woocommerce_pay', [$this, 'action_before_woocommerce_pay'], 10, 0);

        if (!has_action("woocommerce_order_action_capture_payment")) {
            add_action('woocommerce_order_action_capture_payment', [$this, 'capture_payment']);
        }

        $this->boot();
        $this->bootTraits();
    }

    /**
     * Automatically calls all protected bootTraitName() methods from used traits.
     */
    protected function bootTraits(): void
    {
        $traits = class_uses($this, false); // false = don't autoload, if not needed

        foreach ($traits as $fullyQualifiedTraitName) {

            // Extract the short name of the trait
            $parts = explode('\\', $fullyQualifiedTraitName);
            $traitName = end($parts);

            // Construct the expected method name
            $bootMethod = 'boot' . $traitName;
            if (method_exists($this, $bootMethod)) {
                $this->$bootMethod();
            }
        }
    }

    public function getEntity()
    {
        $available_currencies = $this->get_option('currencies_ids');
        $current_currency = get_woocommerce_currency();

        if (isset($available_currencies[$current_currency])) {
            return $available_currencies[$current_currency];
        }

        return $this->get_option('entityId');
    }


    public function process_admin_options()
    {
        $this->init_settings();

        $post_data = $this->get_post_data();

        foreach ($this->get_form_fields() as $key => $field) {
            if ('title' !== $this->get_field_type($field)) {
                try {
                    $this->settings[$key] = $this->get_field_value($key, $field, $post_data);
                    if ('select' === $field['type'] || 'checkbox' === $field['type']) {
                        /**
                         * Notify that a non-option setting has been updated.
                         *
                         * @since 7.8.0
                         */
                        do_action(
                            'woocommerce_update_non_option_setting',
                            array(
                                'id' => $key,
                                'type' => $field['type'],
                                'value' => $this->settings[$key],
                            )
                        );
                    } elseif ('currencies_ids_field' === $key && isset($post_data['currencies_ids'])) {
                        $this->settings["currencies_ids"] = array_reduce($post_data['currencies_ids'], function ($result, $item) {
                            if ($item['value'] && $item['name'])
                                $result[$item['name']] = $item['value'];
                            return $result;
                        }, array());
                    }
                } catch (Exception $e) {
                    $this->add_error($e->getMessage());
                }
            }
        }

        $option_key = $this->get_option_key();
        do_action('woocommerce_update_option', array('id' => $option_key)); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
        return update_option($option_key, apply_filters('woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings), 'yes'); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
    }

    public function capture_payment($order)
    {


        $orderAmount = number_format($order->get_total(), 2, '.', '');
        $amount = number_format(round($orderAmount, 2), 2, '.', '');

        $gateways = WC()->payment_gateways->payment_gateways();

        $gateway = null;

        foreach ($gateways as $wc_gateway) {
            if ($wc_gateway->id == $order->get_payment_method()) {
                $gateway = $wc_gateway;
                break;
            }
        }

        if ($gateway) {
            $uniqueId = $order->get_meta('transaction_id');
            $url = $gateway->server_to_server_url . "/$uniqueId";

            $data = $gateway->getBasicData();
            $data['body'] = \array_merge($data['body'], [
                "amount" => $amount,
                "currency" => $gateway->currency,
                "paymentType" => 'CP'
            ]);

            $response = Http::post($url, $data);

            $resultCode = $response['result']['code'] ?? '';

            if (preg_match($gateway->successCodePattern, $resultCode) || preg_match($gateway->successManualReviewCodePattern, $resultCode)) {
                $order->add_order_note("Captured Successfully");
                $order->update_status($gateway->order_status);
            } else {
                $order->add_order_note("Captured Failed " . $response['result']['description'] ?? 'Unknown reason');
            }
        } else {
            $order->add_order_note("Captured Failed No gateway found");
        }

        // Safely check and unslash HTTP_REFERER
        $location = isset($_SERVER['HTTP_REFERER']) ? sanitize_url(wp_unslash($_SERVER['HTTP_REFERER'])) : home_url();
        wp_safe_redirect($location);
        exit();
    }

    public function auto_revisal($id)
    {

        $url = $this->server_to_server_url . "/$id";

        $data = $this->getBasicData();
        $data['body'] = \array_merge($data['body'], [
            "amount" => TokenManager::TOKENIZATION_AMOUNT,
            "currency" => $this->currency,
            "paymentType" => 'RV'
        ]);


        $response = Http::post($url, $data);
        $resultCode = $response['result']['code'] ?? '';

        if (preg_match($this->successCodePattern, $resultCode) || preg_match($this->successManualReviewCodePattern, $resultCode)) {
            return [
                'status' => 'success',
                'message' => "Auto-Reversal Successfully"
            ];
        }

        return [
            'status' => 'failed',
            'message' => "Auto-Reversal Failed . " . ($response['result']['description'] ?? 'Unknown reason')
        ];
    }

    public function action_before_woocommerce_pay()
    {
        global $wp;

        $order_id = absint($wp->query_vars['order-pay']); // The order ID

        $order = wc_get_order($order_id);

        if ($order->has_status('on-hold')) {
            $order->update_status('pending');
        } elseif ($order->has_status($this->order_status)) {
            wp_safe_redirect($this->get_return_url($order));
            exit();
        }
    }

    public function order_received_text($thanks_text, $order)
    {

        $msg = $order->get_meta('gateway_note');
        if ($order->get_payment_method() == $this->id && $order->get_status() == 'on-hold' && !empty($msg)) {
            wc_add_notice($msg, "notice");
            wc_print_notices();
        } else {
            return $thanks_text;
        }
    }

    /**
     * for validate settings form
     * @return void
     */

    public function admin_script(): void
    {
        global $current_tab, $current_section;

        /**
         * to make sure load admin.js just when currents payments opened
         *
         */
        if ($current_tab == 'checkout' && $current_section == $this->id) {

            $data = [
                'id' => $this->id,
                'url' => $this->token_url,
                'code_setting' => wp_enqueue_code_editor(['type' => 'text/css'])
            ];

            wp_enqueue_script('hyperpay_admin', HYPERPAY_PLUGIN_DIR . '/src/assets/js/admin.js', ['jquery'], '1.0.0', true);
            wp_localize_script('hyperpay_admin', 'hyperpay_data', $data);
        }
    }


    public function iconSrc()
    {
        $icons = [];
        foreach ($this->brands as $brand) {
            $img = HYPERPAY_PLUGIN_DIR . '/src/assets/images/default.png';
            if (file_exists(Main::ROOT_PATH . '/assets/images/' . esc_attr($brand) . "-logo.svg"))
                $img = HYPERPAY_PLUGIN_DIR . '/src/assets/images/' . esc_attr($brand) . "-logo.svg";

            $icons[] = $img;
        }
        return $icons;
    }

    /**
     * to set payment icon based on supported brands
     *
     * @param string $icon
     * @param string $id current payment id
     *
     * @return string  $icon new icon
     *
     */

    public function set_icons($icon, $id)
    {

        if ($id == $this->id) {
            $icons = "";
            foreach ($this->iconSrc() as $src) {
                $icons .= "<img  style='padding:2px ; ' src='$src' >";
            }
            return $icons;
        }

        return $icon;
    }

    /**
     * Here you can define all fields thats will showing in setting page
     * @return void
     */
    public function init_form_fields(): void
    {

        $this->form_fields = [
            'enabled' => [
                'title' => __('Enable/Disable', 'hyperpay-gateways'),
                'type' => 'checkbox',
                'label' => __('Enable Payment Module.', 'hyperpay-gateways'),
                'default' => 'no'
            ],
            'testmode' => [
                'title' => __('Test mode', 'hyperpay-gateways'),
                'type' => 'select',
                'options' => ['0' => __('Off', 'hyperpay-gateways'), '1' => __('On', 'hyperpay-gateways')]
            ],
            'title' => [
                'title' => __('Title:', 'hyperpay-gateways'),
                'type' => 'text',
                'description' => __('This controls the title which the user sees during checkout.', 'hyperpay-gateways'),
                'default' => $this->method_title ?? __('Credit Card', 'hyperpay-gateways')
            ],
            'trans_type' => [
                'title' => __('Transaction type', 'hyperpay-gateways'),
                'type' => 'select',
                'options' => $this->get_hyperpay_trans_type(),
            ],
            'accesstoken' => [
                'title' => __('Access Token', 'hyperpay-gateways'),
                'type' => 'text',
            ],
            'entityId' => [
                'title' => __('Entity ID', 'hyperpay-gateways'),
                'type' => 'text',
                'description' => __('This will used as default if multi-currency not configured', 'hyperpay-gateways'),
            ],
            'currencies_ids_field' => [
                'custom_attributes' => [
                    'data-currencies' => $this->get_option("currencies_ids") ? json_encode($this->get_option("currencies_ids")) : null,
                    'data-currencies_list' => json_encode(array_keys(get_woocommerce_currencies()))
                ],
                'title' => __('Multi-currency', 'hyperpay-gateways'),
                'type' => 'hidden',
                'description' => __('In case you have a multi-currency store', 'hyperpay-gateways'),
            ],
            'secret' => [
                'title' => __('Webhook Key', 'hyperpay-gateways'),
                'type' => 'text',
            ],
            '_webhock' => [
                'title' => __('Webhook URL', 'hyperpay-gateways'),
                'type' => 'text',
                'class' => 'disabled',
                'default' => get_site_url() . "/?rest_route=/hyperpay/v1/" . \str_replace("\\", "/", get_class($this))
            ],
            'hyper_pay_brands' => [
                'title' => __('Brands', 'hyperpay-gateways'),
                'class' => count($this->supported_brands) !== 1 ?: 'disabled',
                'type' => count($this->supported_brands) > 1 ? 'multiselect' : 'select',
                'options' => $this->supported_brands,
            ],
            'payment_style' => [
                'title' => __('Payment Style', 'hyperpay-gateways'),
                'type' => 'select',
                'class' => count($this->payment_style) !== 1 ?: 'disabled',
                'options' => $this->payment_style,
                'default' => 'plain'
            ],
            'custom_style' => [
                'title' => __('Custom Style', 'hyperpay-gateways'),
                'type' => 'textarea',
                'description' => 'Input custom css for payment (Optional)',
                'class' => 'hyperpay_custom_style'
            ],
            'latin_validation' => [
                'title' => __('Enable Input validation (Accept English Characters only)', 'hyperpay-gateways'),
                'type' => 'checkbox',
                'label' => __('Yes', 'hyperpay-gateways'),
                'default' => 'yes',
                'description' => __('Disable this option may cause transaction declined by bank due to 3DSecure', 'hyperpay-gateways'),
            ],
            'order_status' => [
                'title' => __('Status Of Order', 'hyperpay-gateways'),
                'type' => 'select',
                'options' => $this->get_order_status(),
                'description' => __("select order status after success transaction.", 'hyperpay-gateways')
            ],

        ];
    }


    /**
     *  to fill order_status select fields
     *
     * @return array
     */
    function get_order_status(): array
    {
        $order_status = [

            'processing' => __('Processing', 'hyperpay-gateways'),
            'completed' => __('Completed', 'hyperpay-gateways')
        ];

        return $order_status;
    }

    /**
     *  to fill trans_type select fields
     *
     * @return array
     */
    function get_hyperpay_trans_type(): array
    {
        $hyperpay_trans_type = [
            'DB' => 'Debit',
            'PA' => 'Pre-Authorization'
        ];

        return $hyperpay_trans_type;
    }


    /**
     * This function fire when click on Place order at checkout page
     * @param int $order_id
     *
     * @return void
     */
    function receipt_page($order_id)
    {

        $order = new WC_Order($order_id);

        // if we have id param that mean the page result ACI redirection
        if (isset($_GET['resourcePath'])) {
            // Verify nonce for security (Recommended)
            if (!isset($_GET['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'hyperpay_receipt_nonce')) {
                //TODO:
            }

            $resourcePath = sanitize_text_field(wp_unslash($_GET['resourcePath']));
            $url = $this->ACI_base_url . $resourcePath;
            // set header request to contain access token
            $auth = $this->getAuthData();

            $response = Http::get($url, $auth);


            //Dynamic fire function based on status
            $status = $this->check_status($response);
            return $this->$status($order, $response);
        } elseif ($result = $this->prepareCheckout($order_id)) { // process a new transaction
            return $this->renderPaymentForm($order, $result);
        }
    }

    private function isJson($string)
    {
        json_decode($string);
        return json_last_error() === JSON_ERROR_NONE;
    }

    /**
     *
     * render CopyAndPay form
     * @param WC_Order $order
     * @param string $token
     * @return void
     */
    public function renderPaymentForm(WC_Order $order, $result)
    {

        if ($this->server_to_server) {
            $redirect = $result['response']['redirect'];
            $extra_params = [];
            $query_url = wp_parse_url($redirect['url'], PHP_URL_QUERY);
            if ($query_url)
                parse_str(wp_parse_url($redirect['url'], PHP_URL_QUERY), $extra_params);


            return View::render('server-to-server.html', \compact('redirect', 'extra_params'));
        }

        $token = $result['response']['id'];
        $postBackURL = $result['postBackURL'];

        $payment_brands = $this->brands;

        if (is_array($this->brands))
            $payment_brands = implode(' ', $this->brands);


        $dataObj = [
            'is_arabic' => esc_js($this->is_arabic),
            'style' => esc_html($this->payment_style),
            'postBackURL' => ($postBackURL),
            'payment_brands' => esc_html($payment_brands),
            'custom_style' => esc_html($this->custom_style),
            'scriptURL' => esc_html($this->script_url),
            'checkoutId' => $token,
            'integrity' => esc_html($result['integrity'] ?? ''),
            'is_subscription' => SubscriptionsManager::orderContainsSubscription($order) ? 'yes' : 'no',
            'orderId' => $order->id,
            'hyperpay_update_nonce' => wp_create_nonce('hyperpay_update_checkout'),
            'hyperpay_process_checkout_nonce' => wp_create_nonce('hyperpay_process_checkout'),
            'nonce' => $this->NONCE,
            'id' => $this->id
        ];


        if ($this->supported_network) {
            $dataObj['supported_network'] = $this->supported_network;
        }


        add_action('wp_head', [$this, 'custom_add_to_head']);

        $scriptSrc = in_array('tokenization', $this->supports, true) ? "script.js" : "script_no_tokenization.js";

        return View::render('copy-and-pay.html', compact('dataObj', 'scriptSrc'));
    }

    public function custom_add_to_head()
    {

        $data = [
            'url' => $this->ACI_base_url,
            'home' => get_home_url(),
            'nonce' => $this->NONCE
        ];

        return View::render('header.html', \compact('data'));
    }


    /**
     * Process the payment and return the result
     * @param int $order_id
     * @return array[redirect,token,result]
     *
     */
    public function process_payment($order_id)
    {

        $order = new WC_Order($order_id);
        /**
         *
         * validate data to prevent arabic character
         */

        if ($this->latin_validation == 'yes') {
            $firstName = $order->get_billing_first_name();
            $family = $order->get_billing_last_name();
            $street = $order->get_billing_address_1();
            $city = $order->get_billing_city();
            $email = $order->get_billing_email();


            $data_to_validate = [
                'first name' => $firstName,
                'last name' => $family,
                'street' => $street,
                'city' => $city,
                'email' => $email,
            ];

            if ($order->get_billing_state()) {
                $data_to_validate['state'] = $order->get_billing_state();
            }


            // raise a validation error if validation valid
            $this->validate_form($data_to_validate);
        }


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

    protected function getAuthData()
    {
        return [
            'headers' => ["Authorization" => "Bearer {$this->accessToken}"],
            "body" => ["entityId" => $this->entityId]
        ];
    }

    protected function getBasicData()
    {
        $data = $this->getAuthData();

        if ($this->testMode) {
            $data['body']["testMode"] = $this->trans_mode;
            $data['body']["customParameters[3DS2_enrolled]"] = "true";
            $data['body']["customParameters[3DS2_flow]"] = 'challenge';
        }

        return $data;
    }

    protected function buildCheckoutParams(WC_Order $order)
    {

        $shipping_cost = number_format($order->get_shipping_total(), 2, '.', '');
        $amount = number_format($order->get_total(), 2, '.', '');
        $basicData = $this->getBasicData();

        $data = [
            "amount" => $amount,
            "currency" => $this->currency,
            "paymentType" => $this->trans_type,
            "customer.email" => $order->get_billing_email(),
            "notificationUrl" => $order->get_checkout_payment_url(true),
            "customParameters[bill_number]" => $order->get_id(),
            "customer.givenName" => $order->get_billing_first_name(),
            "customer.surname" => $order->get_billing_last_name(),
            "billing.street1" => $order->get_billing_address_1(),
            "billing.city" => $order->get_billing_city(),
            "billing.state" => $order->get_billing_state(),
            "billing.country" => $order->get_billing_country(),
            "billing.postcode" => $order->get_billing_postcode(),
            "shipping.postcode" => $order->get_billing_postcode(),
            "shipping.street1" => $order->get_billing_address_1(),
            "shipping.city" => $order->get_billing_city(),
            "shipping.state" => $order->get_billing_state(),
            "shipping.country" => $order->get_billing_country(),
            "shipping.cost" => $shipping_cost,
            "customParameters[branch_id]" => '1',
            "customParameters[teller_id]" => '1',
            "customParameters[device_id]" => '1',
            "customParameters[plugin]" => 'wordpress',
            "locale" => get_locale(),
        ];

        $basicData['body'] = \array_merge($basicData['body'], $data);
        return $basicData;
    }

    public function getCheckoutData($order_id)
    {
        $order = new WC_Order($order_id);

        $data = $this->buildCheckoutParams($order);
        $transactionKey = wp_rand(11111111, 99999999);
        $postBackURL = $order->get_checkout_payment_url(true);
        $postBackURL .= wp_parse_url($postBackURL, PHP_URL_QUERY) ? '&' : '?';
        $postBackURL .= 'callback=true';
        $postBackURL .= "&transaction-key=$transactionKey";

        $data['body']["merchantTransactionId"] = $order_id . "I" . $transactionKey;


        if ($this->server_to_server) {
            $data['body']['shopperResultUrl'] = $postBackURL;
            $data['body']['paymentBrand'] = $this->brands[0];
        } else {
            $data['body']['integrity'] = true;
        }


        // charge 0.00 orders with 0.01 to prevent declined transactions
        if ($order->get_total() == '0.00') {
            $data['body']['amount'] = TokenManager::TOKENIZATION_AMOUNT;
            $data['body']['customParameters[auto_revisal]'] = 'true';
        }

        // add extra parameters if exists
        return [
            "data" => array_replace_recursive($data, $this->setExtraData($order)),
            "postBackURL" => $postBackURL
        ];
    }

    public function prepareCheckout($order_id)
    {
        $url = $this->server_to_server
            ? $this->server_to_server_url
            : $this->token_url;

        $checkout = $this->getCheckoutData($order_id);

        $response = Http::post($url, $checkout['data']);

        $code = $response['result']['code'] ?? '';

        $isValid = $this->server_to_server
            ? preg_match('/^(800\.400\.5|100\.400\.500)/', $code)
            : preg_match('/^(000\.200)/', $code);

        if (!$isValid) {
            $this->handleError($response);
            return false;
        }

        return [
            'response' => $response,
            'postBackURL' => $checkout['postBackURL'],
        ];
    }


    /**
     * check if all data valid to post {English Characters}
     * @param array
     * @return void
     */
    function validate_form(array $data)
    {
        $errors = [];

        $translations = [
            'first name' => esc_html__('First Name', 'hyperpay-gateways'),
            'last name' => esc_html__('Last Name', 'hyperpay-gateways'),
            'street' => esc_html__('Street', 'hyperpay-gateways'),
            'state' => esc_html__('State', 'hyperpay-gateways'),
            'city' => esc_html__('City', 'hyperpay-gateways'),
            'email' => esc_html__('Email', 'hyperpay-gateways'),
        ];

        foreach ($data as $key => $field) {
            if (!preg_match("/^[a-zA-Z0-9-._!`'#%&,:;<>=@{}~\$\(\)\*\+\/\\\?\[\]\^\| +]+$/", $field) || strlen($field) < 2)
                $errors[$key] = ($translations[$key] ?? esc_html__('Unknown status', 'hyperpay-gateways')) . ' ' . __('format error', 'hyperpay-gateways');
        }

        if (!preg_match('/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/i', $data['email'])) {
            $errors['email'] = __('Email format not valid', 'hyperpay-gateways');
        }


        foreach ($errors as $msg) {
            if (self::has_checkout_block()) {
                throw new Exception(esc_html($msg));
            } else {
                wc_add_notice('<strong>' . $msg . '</strong>', 'error');
            }
        }

        // count equal to zero then no errors and it's valid
        return count($errors);
    }

    /**
     *
     * GET request to transaction report to check if transaction exists or not
     * @param int
     * @return array $response
     *
     */
    public function queryTransactionReport(string $merchantTrxId): array
    {
        $url = $this->query_url . "&merchantTransactionId=$merchantTrxId";
        return Http::get($url, ["headers" => ["Authorization" => "Bearer {$this->accessToken}"]]);
    }


    /**
     *
     * check the status
     *
     * @param array $resultJson
     * @return string
     */
    public function check_status(array $resultJson): string
    {
        $status = 'failed';
        $resultCode = $resultJson['result']['code'] ?? '';

        if (preg_match($this->successCodePattern, $resultCode) || preg_match($this->successManualReviewCodePattern, $resultCode)) {
            $status = 'success';
        } elseif (preg_match($this->pendingCodePattern, $resultCode)) {
            $status = "pending";
        } elseif (isset($resultJson['card']['bin']) && $resultJson['result']['code'] == '800.300.401' && in_array($resultJson['card']['bin'], $this->blackBins)) {
            $this->failed_message = __('Sorry! Please select "mada" payment option in order to be able to complete your purchase successfully.', 'hyperpay-gateways');
        }

        return $status;
    }


    /**
     * handel failed Payments
     * @param WC_Order $order
     * @param string $message
     * @return void
     */
    public function failed(WC_Order $order, $resultJson)
    {

        if (isset($_GET["callback"]) && isset($_GET['transaction-key'])) {
            // Verify nonce for security (Recommended)
            if (!isset($_GET['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'hyperpay_transaction_nonce')) {
                //TODO:
            }

            $hpOrderId = $order->get_id();
            $transactionKey = sanitize_text_field(wp_unslash($_GET['transaction-key']));
            $merchantTrxId = $hpOrderId . "I" . $transactionKey;
            $queryResponse = $this->queryTransactionReport($merchantTrxId);

            if (array_key_exists("payments", $queryResponse)) {
                return $this->processQueryResult($queryResponse, $order);
            }
        }

        return $this->handleError($resultJson);
    }

    public function handleError($resultJson)
    {
        $order = wc_get_order(wc_clean(get_query_var('order-pay')));

        $error_code = $resultJson["result"]["code"];
        $error_description = $resultJson["result"]["description"];
        $aci_msg = $error_description;
        $error_list = [];

        if ($error_code == "600.200.500") {
            $aci_msg = "configuration error";
            Log::write(["error" => $error_description, "response" => $resultJson]);
        }

        $order->add_order_note("{$this->failed_message} $error_code :  $error_description");


        $error_list = $this->getExtended($resultJson);

        if (empty($error_list)) {
            wc_add_notice($this->failed_message, "error");
            wc_add_notice($aci_msg, "error");
        }

        $flat_errors = [];
        array_walk_recursive($error_list, function ($item, $key) use (&$flat_errors) {
            if ($key === 'error') {
                $flat_errors[] = $item;
            }
        });

        foreach ($flat_errors as $error_message) {
            wc_add_notice($error_message, "error");
            $order->add_order_note("extended description : " . $error_message);
        }

        $order->update_status("failed");

        wc_print_notices();
    }

    public function getExtended($resultJson)
    {
        $error_list = [];
        if (isset($resultJson["resultDetails"]["ExtendedDescription"])) {
            $resultDetails = $resultJson["resultDetails"]["ExtendedDescription"];
            if ($this->isJson($resultDetails)) {
                $resultDetails = json_decode($resultDetails, true);

                if (array_key_exists("details", $resultDetails)) {
                    $error_list[] = $resultDetails["details"];
                } elseif (array_key_exists("message", $resultDetails)) {
                    $error_list[] = ["error" => $resultDetails['message']];
                }
            }
        }

        return $error_list;
    }

    /**
     * check the result of transaction if success of failed
     *
     * @param array $resultJson
     * @param WC_Order $order
     * @return void
     */
    public function processQueryResult(array $resultJson, WC_Order $order)
    {
        unset($_GET["callback"]);

        $payment = end($resultJson["payments"]); // get the last transaction

        if (isset($payment["result"]["code"])) {
            $status = $this->check_status($payment);
            return $this->$status($order, $payment);
        }
    }

    /**
     * set customParameters of requested data
     * @param WC_Order $order
     * @return array
     */
    public function setExtraData(WC_Order $order): array
    {
        return [];
    }

    /**
     * update success order
     * @param WC_Order $order
     * @param array $resultJson
     * @return void
     */
    public function success(WC_Order $order, $resultJson)
    {
        global $woocommerce;


        $woocommerce->cart->empty_cart();
        $uniqueId = $resultJson["id"];

        //to add action in order details to capture the pre authorization payments
        if (array_key_exists('paymentType', $resultJson) && $resultJson['paymentType'] == "PA") {
            $order->add_meta_data("is_pre_authorization", true);
            $order->add_meta_data("transaction_id", $uniqueId);
            $order->add_order_note("pre authorization transaction, need to capture");
            $this->order_status = "on-hold";
        }

        if (array_key_exists("invoice_id", $resultJson["resultDetails"] ?? [])) {
            $this->invoice_id = $resultJson["resultDetails"]["invoice_id"];
            $order->add_meta_data("invoice_id", $this->invoice_id);
            $order->add_order_note("invoice id : " . $this->invoice_id);
        }


        $order->add_order_note($this->success_message . __("Transaction ID: ", "hyperpay-gateways") . esc_html($uniqueId));
        $order->update_status($this->order_status);
        $order->payment_complete($uniqueId);
        $order->save();

        // preform auto-revisal
        if (($resultJson['customParameters']['auto_revisal'] ?? null) == 'true') {
            $revisal_status = $this->auto_revisal($uniqueId);
            $order->add_order_note($revisal_status['message']);
        }

        do_action('hyperpay_payment_success_' . $this->id, $order, $resultJson);

        wp_safe_redirect($this->get_return_url($order));
        exit();
    }

    /**
     * update pending order
     * @param WC_Order $order
     * @param array $resultJson
     * @return void
     */
    public function pending(WC_Order $order, $resultJson)
    {
        global $woocommerce;


        $order->update_status("on-hold");
        $order->add_meta_data("gateway_note", __("Transaction is pending confirmation from ", "hyperpay-gateways") . str_replace("hyperpay_", "", $order->get_payment_method()));
        $order->save();

        $woocommerce->cart->empty_cart();
        $uniqueId = $resultJson["id"];

        $order->add_order_note("the order waiting gateway confirmation" . __("Transaction ID: ", "hyperpay-gateways") . esc_html($uniqueId));
        wp_safe_redirect($this->get_return_url($order));
        exit();
    }

    protected function is_successful_response($response)
    {
        $result_code = $response['result']['code'] ?? '';
        return preg_match($this->successCodePattern, $result_code);
    }
}
