<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

abstract class BobPay_Abstract_Payment_Method extends WC_Payment_Gateway
{
    public $version;
    public $url;

    protected $data_to_send;
    protected $send_debug_email;
    protected $debug_email;
    protected $available_currencies;
    protected $available_countries;
    protected $account_code;
    protected $response_url;
    protected $passphrase;
    protected $logger;
    protected $validate_url;
    protected $api_endpoint;
    protected $dev;
    protected $api_key;
    protected $enable_logging;

    abstract public function receipt_page($order);

    abstract protected function get_method_description_string(): string;

    public function __construct()
    {
        $this->version = WC_BOBPAY_PLUGIN_VERSION;
        if (empty($this->icon)) {
            $this->icon = $this->get_option('icon');
        }
        $this->has_fields = false;
        $this->send_debug_email = false;
        $this->available_countries = array('ZA');

        $this->init_settings();
        $this->setup_constants();

        // Add support for refunds
        $this->supports = array('products', 'refunds');

        // Get settings from the main Bob Pay gateway for account credentials
        $bobpay_settings = get_option('woocommerce_bobpay_settings', array());

        $this->debug_email = isset($bobpay_settings['debug_email']) ? $bobpay_settings['debug_email'] : $this->get_option('debug_email');
        $this->account_code = isset($bobpay_settings['account_code']) ? $bobpay_settings['account_code'] : '';
        $this->passphrase = isset($bobpay_settings['passphrase']) ? $bobpay_settings['passphrase'] : '';
        $this->enabled = 'yes' === $this->get_option('enabled') ? 'yes' : 'no';
        $this->title = $this->get_option('title');
        $this->description = $this->get_option('description');
        $this->dev = isset($bobpay_settings['dev']) ? $bobpay_settings['dev'] : $this->get_option('dev');
        $this->api_key = isset($bobpay_settings['api_key']) ? $bobpay_settings['api_key'] : $this->get_option('api_key');
        $this->enable_logging = isset($bobpay_settings['enable_logging']) ? $bobpay_settings['enable_logging'] : $this->get_option('enable_logging');

        $this->set_api_urls();

        /* translators: %1$s: opening link tag, %2$s: closing link tag */
        $this->method_description = sprintf(
            $this->get_method_description_string(),
            '<a href="' . esc_url($this->url) . '">',
            '</a>'
        );

        if ('yes' === $this->dev && !empty($this->debug_email)) {
            $this->send_debug_email = true;
        }

        $this->init_form_fields();

        add_action('admin_notices', array($this, 'admin_notices'));

        add_action('admin_enqueue_scripts', array($this, 'admin_scripts'));
    }

    /**
     * Set API URLs based on the current dev mode setting.
     *
     * @param string|null $dev_mode Optional 'yes'/'no' override. When null, uses the currently saved value.
     */
    protected function set_api_urls($dev_mode = null)
    {
        if ($dev_mode !== null) {
            $this->dev = $dev_mode;
        }
        $this->url = 'https://my.bobpay.co.za';
        $this->validate_url = 'https://api.bobpay.co.za/payments/intents/validate';
        $this->api_endpoint = 'https://api.bobpay.co.za';

        if ('yes' === $this->dev) {
            $this->url = 'https://sandbox.bobpay.co.za';
            $this->validate_url = 'https://api.sandbox.bobpay.co.za/payments/intents/validate';
            $this->api_endpoint = 'https://api.sandbox.bobpay.co.za';

            if (strpos(site_url(), 'localhost') !== false || strpos(site_url(), 'ngrok') !== false || strpos(site_url(), 'woodemo3.dev.bobpay') !== false) {
                $this->url = 'https://dev.bobpay.co.za';
                $this->validate_url = 'https://api.dev.bobpay.co.za/payments/intents/validate';
                $this->api_endpoint = 'https://api.dev.bobpay.co.za';
            }
        }
    }

    public function init_form_fields()
    {
        $this->form_fields = array(
            'enabled' => array(
                'title' => __('Enable/Disable', 'bobpay-woo'),
                'label' => __('Enable Bob Pay', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Bob Pay as a payment method', 'bobpay-woo'),
                'default' => 'yes',
                'desc_tip' => true,
            ),
            'api_key' => array(
                'title' => __('API Key', 'bobpay-woo'),
                'type' => 'password',
                /* translators: %1$s: opening link tag, %2$s: closing link tag */
                'description' => sprintf(__('* Required. Your Bob Pay API key. Get it from %1$sBob Pay%2$s.', 'bobpay-woo'), '<a href="' . esc_url($this->url . '/integrations') . '" target="_blank">', '</a>'),
                'default' => '',
            ),
            'enable_multiple_payment_methods'=>array(
                'title' => __('Payment methods display', 'bobpay-woo'),
                'label' => __('Enable multiple payment methods', 'bobpay-woo'),
                'type' => 'select',
                'description' => __('Choose how payment methods are displayed at checkout', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_enable_multiple_payment_methods',
                'default' => 'multiple',
                'options' => array(
                    'single' => __('Single payment method', 'bobpay-woo'),
                    'multiple' => __('Multiple payment methods', 'bobpay-woo'),
                ),
            ),
            'card_enabled' => array(
                'title' => __('Card', 'bobpay-woo'),
                'label' => __('Enable Credit/Debit Card', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable credit/debit card payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_card_enabled',
                'default' => 'yes',
            ),
            'capitec_pay_enabled' => array(
                'title' => __('Capitec Pay', 'bobpay-woo'),
                'label' => __('Enable Capitec Pay', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Capitec Pay payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_capitec_pay_enabled',
                'default' => 'yes',
            ),
            'instant_eft_enabled' => array(
                'title' => __('Instant EFT', 'bobpay-woo'),
                'label' => __('Enable Instant EFT', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Instant EFT payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_instant_eft_enabled',
                'default' => 'yes',
            ),
            'manual_eft_enabled'=> array(
                'title' => __('Manual EFT', 'bobpay-woo'),
                'label' => __('Enable Manual EFT', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Manual EFT payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_manual_eft_enabled',
                'default' => 'yes',
            ),
            'scan_to_pay_enabled'=> array(
                'title' => __('Scan to Pay', 'bobpay-woo'),
                'label' => __('Enable Scan to Pay', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Scan to Pay payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_scan_to_pay_enabled',
                'default' => 'yes',
            ),
            'pay_shap_enabled'=> array(
                'title' => __('Pay Shap', 'bobpay-woo'),
                'label' => __('Enable Pay Shap', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Pay Shap payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_pay_shap_enabled',
                'default' => 'yes',
            ),
            'nedbank_direct_eft_enabled'=> array(
                'title' => __('Nedbank Direct EFT', 'bobpay-woo'),
                'label' => __('Enable Nedbank Direct EFT', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Nedbank Direct EFT payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_nedbank_direct_eft_enabled',
                'default' => 'yes',
            ),
            'absa_pay_enabled'=> array(
                'title' => __('Absa Pay', 'bobpay-woo'),
                'label' => __('Enable Absa Pay', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Absa Pay payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_absa_pay_enabled',
                'default' => 'yes',
            ),
            'apple_pay_enabled' => array(
                'title' => __('Apple Pay', 'bobpay-woo'),
                'label' => __('Enable Apple Pay', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Apple Pay payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_apple_pay_enabled',
                'default' => 'yes',
            ),
            'google_pay_enabled' => array(
                'title' => __('Google Pay', 'bobpay-woo'),
                'label' => __('Enable Google Pay', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable Google Pay payments.', 'bobpay-woo'),
                'class' => 'woocommerce_bobpay_google_pay_enabled',
                'default' => 'yes',
            ),
            'dev' => array(
                'title' => __('Enable development mode', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Place the payment gateway in development mode. Development REST API endpoints will be used. <i>Note: Funds will still be deducted, so use small amounts.</i>', 'bobpay-woo'),
                'default' => 'yes',
            ),
            'enable_logging' => array(
                'title' => __('Enable logging', 'bobpay-woo'),
                'type' => 'checkbox',
                'description' => __('Enable logging to help with debugging.', 'bobpay-woo'),
                'default' => 'no',
            ),
            'debug_email' => array(
                'title' => __('Who receives debug emails (development mode only)?', 'bobpay-woo'),
                'type' => 'text',
                'description' => __('The email address to which debug e-mails are sent when in development mode.', 'bobpay-woo'),
                'default' => get_option('admin_email'),
            ),
            'account_code' => array(
                'title' => __('Account code', 'bobpay-woo'),
                'type' => 'text',
                'description' => __('<strong>Deprecated.</strong> This field is no longer used. Please use the API Key above instead.', 'bobpay-woo'),
                'default' => '',
                'custom_attributes' => array(
                    'readonly' => 'readonly',
                ),
            ),
            'passphrase' => array(
                'title' => __('Passphrase', 'bobpay-woo'),
                'type' => 'text',
                'description' => __('<strong>Deprecated.</strong> This field is no longer used. Please use the API Key above instead.', 'bobpay-woo'),
                'default' => '',
                'custom_attributes' => array(
                    'readonly' => 'readonly',
                ),
            ),
        );
    }

    public function process_admin_options()
    {
        $posted_data = $this->get_post_data();

        $payment_methods = array(
            'card_enabled',
            'capitec_pay_enabled',
            'instant_eft_enabled',
            'manual_eft_enabled',
            'scan_to_pay_enabled',
            'pay_shap_enabled',
            'nedbank_direct_eft_enabled',
            'absa_pay_enabled',
            'apple_pay_enabled',
            'google_pay_enabled'
        );

        $gateway_enabled = !empty($posted_data[$this->plugin_id . $this->id . '_enabled']);

        // If Bob Pay is disabled, disable all individual payment methods
        if (!$gateway_enabled) {
            foreach ($payment_methods as $method) {
                $this->settings[$method] = 'no';
            }

            return parent::process_admin_options();
        }

        // Update the API endpoint based on the posted dev mode setting before validating the API key
        // Only update if the _dev field is expected in the form (main gateway only, not sub-gateways)
        $dev_key = $this->plugin_id . $this->id . '_dev';
        $dev_mode_changed = false;
        $form_fields = $this->get_form_fields();
        if ($form_fields && array_key_exists('dev', $form_fields)) {
            $posted_dev = !empty($posted_data[$dev_key]) ? 'yes' : 'no';
            $dev_mode_changed = $posted_dev !== $this->dev;
            $this->set_api_urls($posted_dev);
        }

        // Validate API key if provided
        if (!empty($posted_data[$this->plugin_id . $this->id . '_api_key'])) {
            $api_key = $posted_data[$this->plugin_id . $this->id . '_api_key'];
            $current_api_key = $this->get_option('api_key');

            // Re-validate if API key has changed or dev mode has changed
            if ($api_key !== $current_api_key || $dev_mode_changed) {
                $error_code = $this->validate_integration($api_key);

                if ($error_code !== 200) {
                    update_option('woocommerce_bobpay_invalid_api_key', 'yes');
                    return false;
                }

                // Clear invalid API key notice if validation succeeds
                delete_option('woocommerce_bobpay_invalid_api_key');
            }
        } else {
            // Clear invalid API key error if API key field is empty
            delete_option('woocommerce_bobpay_invalid_api_key');
        }

        $payment_methods_display = isset($posted_data[$this->plugin_id . $this->id . '_enable_multiple_payment_methods'])
            ? $posted_data[$this->plugin_id . $this->id . '_enable_multiple_payment_methods']
            : 'single';

        if ($payment_methods_display === 'multiple') {
            // If "Multiple payment methods" is selected, validate that at least one payment method is enabled
            $has_enabled_method = false;
            foreach ($payment_methods as $method) {
                if (!empty($posted_data[$this->plugin_id . $this->id . '_' . $method])) {
                    $has_enabled_method = true;
                    break;
                }
            }

            if (!$has_enabled_method) {
                WC_Admin_Settings::add_error(__('Please enable at least one payment method when "Multiple payment methods" is selected.', 'bobpay-woo'));
                return false;
            }
        } else {
            // If "Single payment method" is selected, disable all individual payment methods
            foreach ($payment_methods as $method) {
                $this->settings[$method] = 'no';
            }
        }

        return parent::process_admin_options();
    }

    public function admin_notices()
    {
        $errors_to_show = $this->check_requirements();

        if (!count($errors_to_show)) {
            return;
        }

        if ("no" === $this->enabled) {
            return;
        }

        if (!get_transient('wc-bobpay-plugin-admin-notice-transient')) {
            set_transient('wc-bobpay-plugin-admin-notice-transient', 1, 1);

            $error_list_html = array_reduce($errors_to_show, function ($errors_list, $error_item) {
                return $errors_list . PHP_EOL . '<li>' . esc_html($this->get_error_message($error_item)) . '</li>';
            }, '');

            echo '<div class="notice notice-error is-dismissible"><p>'
                . esc_html__('To use Bob Pay as a payment provider, you need to address the following problems:', 'bobpay-woo') . '</p>'
                . '<ul style="list-style-type: disc; list-style-position: inside; padding-left: 2em;">'
                . wp_kses( $error_list_html, array( 'li' => array() ) )
                . '</ul></p></div>';
        }
    }

    public function check_requirements()
    {
        // Get settings from the main Bob Pay gateway
        $bobpay_settings = get_option('woocommerce_bobpay_settings', array());

        $errors = [
            !in_array(get_woocommerce_currency(), $this->available_currencies) ? 'wc-bobpay-plugin-error-invalid-currency' : null,
            get_option('woocommerce_bobpay_invalid_api_key') ? 'wc-bobpay-plugin-error-invalid-api-key' : null,
        ];

        if (empty($bobpay_settings['api_key']) && (empty($bobpay_settings['account_code']) || empty($bobpay_settings['passphrase']))) {
            $errors[] = 'wc-bobpay-plugin-error-missing-account-credentials';
        }

        return array_filter($errors);
    }

    public function get_error_message($key)
    {
        switch ($key) {
            case 'wc-bobpay-plugin-error-invalid-currency':
                return __('Your store uses a currency that Bob Pay doesn\'t support yet.', 'bobpay-woo');
            case 'wc-bobpay-plugin-error-missing-api-key':
                return __('Bob Pay API Key is missing. Please add it in the Bob Pay settings.', 'bobpay-woo');
            case 'wc-bobpay-plugin-error-invalid-api-key':
                return __('Bob Pay API Key is invalid. Please verify and enter the correct API key.', 'bobpay-woo');
            case 'wc-bobpay-plugin-error-missing-account-credentials':
                return __('Bob Pay API key and Account Code/Passphrase are missing. Please add either in the Bob Pay settings.', 'bobpay-woo');
            default:
                return '';
        }
    }

    public function admin_scripts() {

        if ( 'woocommerce_page_wc-settings' !== get_current_screen()->id ) {
			return;
		}

        wp_enqueue_script('wc_bobpay_admin', WC_BOBPAY_PLUGIN_URL . '/assets/js/bobpay-admin.js', array('jquery'), WC_BOBPAY_PLUGIN_VERSION, true);
    }

    public static function get_order_prop($order, $prop)
    {
        switch ($prop) {
            case 'order_total':
                $getter = array($order, 'get_total');
                break;
            default:
                $getter = array($order, 'get_' . $prop);
                break;
        }

        return is_callable($getter) ? call_user_func($getter) : $order->{$prop};
    }

public function admin_options()
{
    if (in_array(get_woocommerce_currency(), $this->available_currencies)) {
        parent::admin_options();

        if ($this->id === 'bobpay') {
        // Download logs section
        echo '<h2>Download Logs</h2>';
        echo '<div class="bobpay-logs-section">';
        echo '<button type="button" class="button button-secondary" id="bobpay-download-logs-btn" style="float: right; margin-bottom: 10px;">Download Logs</button>';
        echo '<button type="button" class="button button-secondary" id="bobpay-test-api-key-btn" style="float: right; margin-bottom: 10px; margin-right: 10px;">Test API Key</button>';
        echo '</div>';

        ?>
        <script type="text/javascript">
        document.addEventListener('DOMContentLoaded', function() {
            var apiKeyField = document.getElementById('woocommerce_bobpay_api_key');
            var toggleBtn = document.createElement('button');
            toggleBtn.type = 'button';
            toggleBtn.className = 'bobpay-toggle-api-key';
            toggleBtn.setAttribute('style', 'margin-left: 4px; display: inline-flex; align-items: center; justify-content: center; background: #f0f0f1; border: 1px solid #8c8f94; border-radius: 4px; padding: 4px 8px; cursor: pointer; color: #50575e;');
            toggleBtn.setAttribute('aria-label', 'Show API Key');
            toggleBtn.innerHTML = '<span class="dashicons dashicons-visibility"></span>';
            apiKeyField.insertAdjacentElement('afterend', toggleBtn);
            toggleBtn.addEventListener('click', function() {
                var isPassword = apiKeyField.type === 'password';
                apiKeyField.type = isPassword ? 'text' : 'password';
                var icon = toggleBtn.querySelector('.dashicons');
                icon.classList.toggle('dashicons-visibility', !isPassword);
                icon.classList.toggle('dashicons-hidden', isPassword);
                toggleBtn.setAttribute('aria-label', isPassword ? 'Hide API Key' : 'Show API Key');
            });
        });

        jQuery(document).ready(function($) {
            $('#bobpay-download-logs-btn').on('click', function(e) {
                e.preventDefault();

                var form = $('<form>', {
                    'method': 'post',
                    'action': ajaxurl
                });

                form.append($('<input>', {
                    'type': 'hidden',
                    'name': 'action',
                    'value': 'bobpay_download_logs'
                }));

                form.append($('<input>', {
                    'type': 'hidden',
                    'name': 'nonce',
                    'value': '<?php echo esc_js( wp_create_nonce('bobpay_download_log_action') ); ?>'
                }));

                $('body').append(form);
                form.submit();
                form.remove();
            });

            $('#bobpay-test-api-key-btn').on('click', function(e) {
                e.preventDefault();

                var button = $(this);
                var apiKeyField = $('#woocommerce_bobpay_api_key');
                var apiKey = apiKeyField.val();

                if (!apiKey || apiKey.trim() === '') {
                    alert('Error: Please enter an API key before testing.');
                    return;
                }

                button.prop('disabled', true).text('Testing...');

                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'bobpay_test_api_key',
                        nonce: '<?php echo esc_js( wp_create_nonce('bobpay_test_api_key_action') ); ?>',
                        api_key: apiKey,
                        dev_mode: $('#woocommerce_bobpay_dev').is(':checked') ? 'yes' : 'no'
                    },
                    success: function(response) {
                        button.prop('disabled', false).text('Test API Key');

                        if (response.success) {
                            alert('Success! API key is valid.');
                        } else {
                            alert('Error: ' + (response.data.message || 'API key is invalid.'));
                        }
                    },
                    error: function() {
                        button.prop('disabled', false).text('Test API Key');
                        alert('Error: Failed to test API key. Please try again.');
                    }
                });
            });
        });
        </script>
        <?php
        }
    } else {
        ?>
        <h3>
            <?php echo esc_html__('Bob Pay', 'bobpay-woo'); ?>
        </h3>
        <div class="inline error">
            <p><strong>
                    <?php echo esc_html__('Gateway Disabled', 'bobpay-woo'); ?>
                </strong>
                <?php /* translators: 1: a href link 2: closing href */ echo wp_kses( sprintf( __('Choose South African Rands as your store currency in %1$sGeneral Settings%2$s to enable the Bob Pay Gateway.', 'bobpay-woo'), '<a href="' . esc_url(admin_url('admin.php?page=wc-settings&tab=general')) . '">', '</a>' ), array( 'a' => array( 'href' => array() ) ) ); ?>
            </p>
        </div>
        <?php
    }
}

    public function is_available()
    {
        if ('yes' === $this->enabled) {
            $errors = $this->check_requirements();

            return 0 === count($errors);
        }

        return parent::is_available();
    }

    public function needs_setup()
    {
        // Get settings from the main Bob Pay gateway
        $bobpay_settings = get_option('woocommerce_bobpay_settings', array());
        return empty($bobpay_settings['api_key']);
    }

    public function process_payment($order_id)
    {
        $order = wc_get_order($order_id);

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

    public function process_payment_response()
    {
        $json = file_get_contents('php://input');
        $data = json_decode($json, true);

        if ( ! is_array( $data ) ) {
            $this->log('Invalid webhook payload received');
            wp_die('Invalid payload', 'Bad Request', array('response' => 400));
            return;
        }

        // Keep raw decoded data for API validation
        $raw_data = $data;
        $data = $this->sanitize_webhook_data( $data );

        try {
            $this->log(PHP_EOL .
                '====================' .
                PHP_EOL .
                'Bob Pay webhook received' .
                PHP_EOL .
                '====================');
            $this->log('Webhook data: ' . wp_json_encode($data));

            $error = null;
            $done = false;


            $customPaymentID = $data['custom_payment_id'];

            // Remove the appended site_url from custom payment ID
            $site_url = wp_parse_url(site_url(), PHP_URL_HOST);
            $string_to_remove = $site_url . "_";

            $orderID = $customPaymentID;
            // Check if the string starts with the site_url
            if (strpos($customPaymentID, $string_to_remove) === 0) {
             // If yes, remove the prefix
                 $orderID = substr($customPaymentID, strlen($string_to_remove));
                }

            // Remove non numeric characters
            $order_id = preg_replace("/[^0-9]/", "", $data['custom_payment_id']);

            $order = wc_get_order($orderID);

            if (false === $data) {
                $error = 'Invalid data for webhook';
            }

            if (empty($error) && empty($order)) {
                $error = 'Order not found with order ID: ' . $order_id;
            }

            if (empty($error) && $this->dev != 'yes') {
                $this->log('Verify source IP');

                if ( ! isset( $_SERVER['REMOTE_ADDR'] ) || !$this->is_valid_ip(sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])))) {
                    $error = 'Webhook source forbidden';
                }
            }

            if (empty($error)) {
                $this->log('Verifying payment data against Bob Pay');
                $has_valid_response_data = $this->validate_payment_data($raw_data);

                if (!$has_valid_response_data) {
                    $error = 'Payment data validation failed';
                }
            }

            // Save the payment ID and payment method to the order meta
            if (empty($error)) {
                $bob_pay_payment_id = $data['payment']['id'];
                if (! empty($bob_pay_payment_id)) {
                    $order->update_meta_data('bob_pay_payment_id', $bob_pay_payment_id);
                    $payment_method_value = $data['payment_method'] ?? $data['payment']['payment_method'] ?? '';
                    if (!empty($payment_method_value)) {
                        $order->update_meta_data('bob_pay_payment_method', $payment_method_value);
                    }
                    $order->save();
                }
            }

            if (empty($error)) {
                $this->log('Check data against internal order');

                if (!$this->amounts_equal($data['amount'], self::get_order_prop($order, 'order_total'))) {
                    $error = 'Order gross amount mismatch';
                }
            }

            if (!$error && !$done) {
                $this->log_order_details($order);

                if ('completed' === self::get_order_prop($order, 'status')) {
                    $this->log('Order has already been processed');
                    $done = true;
                }
            }

            if (!empty($error)) {
                $this->log('Error occurred: ' . $error);

                if ($this->send_debug_email) {
                    $this->log('Sending email notification');

                    $forwardHeaderString = '';
                    if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && !empty(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])))) {
                        $forwardHeaderString .= "\n" .
                            'Forwarding header: ' .
                            sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR']));
                    }

                    $subject = 'An error occurred while handling a payment: ' . $error;
                    $body = "Hi,\n" .
                        "Something went south while processing an incoming payment response:\n" .
                        "============================================================\n" .
                        'Site: ' .
                        esc_html(get_bloginfo('name', 'display')) .
                        ' (' .
                        esc_url(home_url('/')) .
                        ")\n" .
                        'Remote IP Address: ' .
                        ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' ) .
                        "\n" .
                        'Remote host name: ' .
                        ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( gethostbyaddr( sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) ) ) : 'unknown' ) .
                        $forwardHeaderString .
                        "\n" .
                        'Bob Pay Transaction ID: ' .
                        sanitize_text_field($data['custom_payment_id']) .
                        "\n" .
                        'Error: ' .
                        $error .
                        "\n" .
                        "------------------------------------------------------------\n" .
                        'Payment payload:' .
                        "\n" .
                        wp_json_encode($data);
                    wp_mail($this->debug_email, $subject, $body);
                }
            } elseif (!$done) {
                $this->log('Check status and update order');

                $status = strtolower($data['status']);
                if ('paid' === $status) {
                    $this->handle_payment_complete($data, $order);
                } elseif ('unpaid' === $status) {
                    $this->handle_payment_pending($data, $order);
                }
            }

            $this->log(PHP_EOL .
                '====================' .
                PHP_EOL .
                'Webhook processing complete' .
                PHP_EOL .
                '====================');
        } catch (\Throwable $e) {
            $this->log('An error occurred: ' . $e->getMessage());
        }

        header('HTTP/1.0 200 OK');
        flush();
    }

    /**
     * Sanitize webhook data received from Bob Pay.
     *
     * @param array $data Raw webhook data.
     * @return array Sanitized webhook data.
     */
    private function sanitize_webhook_data( $data ) {
        $sanitized = array();

        foreach ( $data as $key => $value ) {
            $key = sanitize_text_field( $key );
            if ( is_array( $value ) ) {
                $sanitized[ $key ] = $this->sanitize_webhook_data( $value );
            } else {
                $sanitized[ $key ] = sanitize_text_field( $value );
            }
        }

        return $sanitized;
    }

    public function is_valid_ip($source_ip)
    {
        $valid_ips = array(
            '13.245.84.126', // Bob Pay dev
            '13.246.100.25', // Bob Pay prod
        );

        if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && !empty(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])))) {
            $source_ip = (string) rest_is_ip_address(trim(current(preg_split('/[,:]/', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])))))) ?: $source_ip;
        }

        $this->log("Valid IPs:\n" . wp_json_encode($valid_ips));
        $is_valid_ip = in_array($source_ip, $valid_ips);

        return apply_filters('woocommerce_bobpay_plugin_is_valid_ip', $is_valid_ip, $source_ip);
    }

    public function validate_payment_data($data)
    {
        $this->log('Host = ' . $this->validate_url);
        $this->log('Params = ' . wp_json_encode($data));

        if (empty($data) || !is_array($data)) {
            return false;
        }

        $response = wp_remote_post(
            $this->validate_url,
            array(
                'headers' => [
                    'Content-Type' => 'application/json',
                ],
                'timeout' => 60,
                'body' => wp_json_encode($data),
                'user-agent' => BOBPAY_USER_AGENT,
            )
        );

        if (is_wp_error($response)) {
            $this->log("Validation request error: " . PHP_EOL . wp_json_encode($response));
            return false;
        }

        if (!empty($response['response']['code']) && $response['response']['code'] == 200) {
            return true;
        } else {
            return false;
        }
    }

    public function amounts_equal($amount1, $amount2)
    {
        // Deviations greater than 1c in difference aren't seen as equal
        return !(abs(floatval($amount1) - floatval($amount2)) > 0.01);
    }

    public function log_order_details($order)
    {
        if (version_compare(WC_VERSION, '3.0.0', '<')) {
            $customer_id = get_post_meta($order->get_id(), '_customer_user', true);
        } else {
            $customer_id = $order->get_user_id();
        }

        $details = "Order Details:"
            . PHP_EOL . 'customer id:' . $customer_id
            . PHP_EOL . 'order id:   ' . $order->get_id()
            . PHP_EOL . 'parent id:  ' . $order->get_parent_id()
            . PHP_EOL . 'status:     ' . $order->get_status()
            . PHP_EOL . 'total:      ' . $order->get_total()
            . PHP_EOL . 'currency:   ' . $order->get_currency()
            . PHP_EOL . 'key:        ' . $order->get_order_key();

        $this->log($details);
    }

    public function handle_payment_complete($data, $order)
    {
        $this->log('- Complete');
        $order->add_order_note(__('Bob Pay: Payment completed', 'bobpay-woo'));
        $order->update_meta_data('bobpay_amount', $data['amount']);
        $order->payment_complete($data['custom_payment_id']);

        if ($this->send_debug_email) {
            $subject = 'Bob Pay payment on your site';
            $body =
                "Hi,\n" .
                "A Bob Pay transaction has been completed on your website:\n" .
                "============================================================\n" .
                'Site: ' . esc_html(get_bloginfo('name', 'display')) . ' (' . esc_url(home_url('/')) . ")\n" .
                'Remote IP Address: ' . ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' ) . "\n" .
                'Remote host name: ' . ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( gethostbyaddr( sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) ) ) : 'unknown' ) . "\n" .
                'Bob Pay Transaction ID: ' . esc_html($data['custom_payment_id']) . "\n" .
                'Order Status Code: ' . self::get_order_prop($order, 'status');
            wp_mail($this->debug_email, $subject, $body);
            die;
        }

        do_action('woocommerce_bobpay_handle_payment_complete', $data, $order);
    }

    public function handle_payment_pending($data, $order)
    {
        $this->log('- Pending');
        $order->update_status('on-hold', __('Payment processing on Bob Pay.', 'bobpay-woo'));
    }

    public function get_intent_request_data($order, $payment_method = '')
    {
        $order_id = $order->get_id();

        // Append the site url with the order number to maintain distinct custom_payment_id
        $site_url = wp_parse_url(site_url(), PHP_URL_HOST);

        $customPaymentID = $site_url . '_' . $order_id;

        // Ensure amount always has 2 decimal places
        $amount = number_format($order->get_total(), 2, '.', '');

        // Get return URL and ensure it's absolute (has domain)
        $return_url = $this->get_return_url($order);
        $return_url = $this->ensure_absolute_url($return_url);
        $return_url_with_param = esc_url_raw(add_query_arg('utm_nooverride', '1', $return_url));

        $body = array(
            'recipient_account_code' => $this->account_code,
            'custom_payment_id' => $customPaymentID,
            'email' => $order->get_billing_email(),
            'mobile_number' => $order->get_billing_phone(),
            'amount' => $amount,
            'item_name' =>  $order->get_order_number(),
            'item_description' => '',
            'notify_url' => $this->response_url,
            'success_url' => $return_url_with_param,
            'pending_url' => $return_url_with_param,
            'cancel_url' => $order->get_cancel_order_url()
        );

        //has to be added separately from other data to send since its value is a hash of the other data
        $body['signature'] = $this->generate_signature($body);

        if (!empty($payment_method)) {
            $body['payment_method'] = $payment_method;
        }

        // Add intent source
        $body['source'] = 'woocommerce';

        // Add order number
        $body['woocommerce_order_number'] = $order->get_order_number();

        // Get the first item in the order to use as item name
        $items = $order->get_items();
        $first_item = reset($items);
        $item_name = '';
        if ($first_item) {
            $item_name = $first_item->get_name();
        }

        // Add item name to metadata
        $body['woocommerce_item_name'] = $item_name;

        return $body;
    }

    /**
     * Ensure a URL is absolute (includes protocol and domain)
     *
     * @param string $url The URL to check and normalize
     * @return string Absolute URL
     */
    protected function ensure_absolute_url($url)
    {
        // If URL already starts with http:// or https://, it's absolute
        if (strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) {
            return $url;
        }

        // URL is relative, make it absolute using home_url()
        $absolute_url = home_url($url);

        return $absolute_url;
    }

    public function redirect_to_bobpay($order_id, string $payment_method = '')
    {
        $order = wc_get_order($order_id);

        // If API key is set, use the API to request a payment link
        if (!empty($this->api_key)) {
            $this->log('Using API to request payment link');

            $payment_link = $this->request_payment_link($order, $payment_method);

            if (!is_wp_error($payment_link)) {
                // Redirect to the payment link
                $logo_src = WC_BOBPAY_PLUGIN_URL . '/assets/images/bob-pay-logo-vertical.png';
                $cancel_url =  $order->get_cancel_order_url();

                echo '<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgb(255, 255, 255); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999;">'
                    . '<img src="' . esc_url( $logo_src ) . '" style="height: 15%;" alt="Bob Pay">'
                    . '<div style="text-align: center; color: #000; margin-top: 20px;">' . esc_html__( 'You are being redirected to Bob Pay to complete your payment...', 'bobpay-woo' ) . '</div>'
                    . '<a href="' . esc_url( $cancel_url ) . '" style="margin-top: 20px;">' . esc_html__( 'Cancel order & restore cart', 'bobpay-woo' ) . '</a>'
                    . '</div>'
                    . '<script type="text/javascript">'
                    . 'window.location.href = "' . esc_url( $payment_link ) . '";'
                    . '</script>';
                return;
            } elseif ($payment_link->get_error_code() === 'payment_link_error_401') {
                if (empty($this->account_code) || empty($this->passphrase)) {
                    $this->log('API key unauthorized (401) and account credentials missing, cannot fallback');
                    wc_add_notice(__('Unable to process payment. Please use a different payment method.', 'bobpay-woo'), 'error');
                    wp_safe_redirect(wc_get_checkout_url());
                    exit;
                }
                $this->log('API key unauthorized (401), falling back to standard method: ' . $payment_link->get_error_message());
            } else {
                $this->log('Failed to get payment link from API: ' . $payment_link->get_error_message());
                $error_data = json_decode($payment_link->get_error_message(), true);
                $error_reason = isset($error_data['message']) ? $error_data['message'] : __('Unable to process payment. Please use a different payment method.', 'bobpay-woo');
                wc_add_notice($error_reason, 'error');
                wp_safe_redirect(wc_get_checkout_url());
                exit;
            }
        }

        $this->log('Using standard method to request payment link');
        $this->data_to_send = $this->get_intent_request_data($order, $payment_method);

        $hidden_inputs = '';
        foreach ($this->data_to_send as $key => $value) {
            $hidden_inputs .= '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr($value) . '" />';
        }

        $cancel_url = $this->data_to_send['cancel_url'];

        $logo_src = WC_BOBPAY_PLUGIN_URL . '/assets/images/bob-pay-logo-vertical.png';

        echo '<form method="get" action="' . esc_url( $this->url . '/pay' ) . '">'
            . '<input id="bobpay_perform_redirect" type="submit" value="' . esc_attr__( 'Pay via Bob Pay', 'bobpay-woo' ) . '" />'
            . '<a href="' . esc_url( $cancel_url ) . '">' . esc_html__( 'Cancel order & restore cart', 'bobpay-woo' ) . '</a>'
            . wp_kses( $hidden_inputs, array( 'input' => array( 'type' => array(), 'name' => array(), 'value' => array() ) ) )
            . '<script type="text/javascript">'
            . 'document.addEventListener("DOMContentLoaded", function () {'
                . 'var overlay = document.createElement("div");'
                . 'overlay.style.position = "fixed";'
                . 'overlay.style.top = "0";'
                . 'overlay.style.left = "0";'
                . 'overlay.style.width = "100%";'
                . 'overlay.style.height = "100%";'
                . 'overlay.style.background = "rgb(255, 255, 255)";'
                . 'overlay.style.display = "flex";'
                . 'overlay.style.flexDirection = "column";'
                . 'overlay.style.alignItems = "center";'
                . 'overlay.style.justifyContent = "center";'
                . 'overlay.style.zIndex = "9999";'
                . 'overlay.style.cursor = "wait";'
                . 'var logo = document.createElement("img");'
                . 'logo.src = "' . esc_url( $logo_src ) . '";'
                . 'logo.style.height = "15%";'
                . 'var message = document.createElement("div");'
                . 'message.textContent = "You are being redirected to Bob Pay to complete your payment...";'
                . 'message.style.textAlign = "center";'
                . 'message.style.color = "#000";'
                . 'message.style.backgroundColor = "#fff";'
                . 'overlay.appendChild(logo);'
                . 'overlay.appendChild(message);'
                . 'document.body.appendChild(overlay);'
                . 'var bobPayButton = document.getElementById("bobpay_perform_redirect");'
                . 'if (bobPayButton) {'
                    . 'bobPayButton.click();'
                . '}'
            . '});'
            . '</script>'
            . '</form>';
    }

    /**
     * Request a payment link
     *
     * @param WC_Order $order The WooCommerce order object
     * @param string $payment_method Optional payment method
     * @return string|WP_Error Payment link URL or WP_Error on failure
     */
    protected function request_payment_link($order, $payment_method = '')
    {
        $body = $this->get_intent_request_data($order, $payment_method);

        // Remove values not required for API request
        unset($body['signature']);
        unset($body['recipient_account_code']);

        $body['amount'] = floatval($body['amount']);
        $body['origin'] = site_url();

        $this->log('Requesting intent link from API: ' . wp_json_encode($body));

        $response = wp_remote_post(
            $this->api_endpoint . '/payments/intents/link',
            array(
                'headers' => array(
                    'Content-Type' => 'application/json',
                    'Authorization' => 'Bearer ' . $this->api_key,
                ),
                'timeout' => 60,
                'body' => wp_json_encode($body),
                'user-agent' => BOBPAY_USER_AGENT,
            )
        );

        $response_code = wp_remote_retrieve_response_code($response);
        $response_body = json_decode(wp_remote_retrieve_body($response), true);

        $this->log('Payment link response code: ' . $response_code);
        $this->log('Payment link response body: ' . wp_json_encode($response_body));

        if (is_wp_error($response)) {
            $this->log('Payment link request error: ' . $response->get_error_message());
            return new WP_Error('payment_link_error', $response->get_error_message());
        }

        if ($response_code !== 200 && $response_code !== 201) {
            return new WP_Error('payment_link_error_' . $response_code, wp_json_encode($response_body));
        }

        // Extract the payment link from the response
        // Priority: short_url (if available) > url
        if (!empty($response_body['short_url'])) {
            return $response_body['short_url'];
        } elseif (!empty($response_body['url'])) {
            return $response_body['url'];
        }

        $this->log('Payment link not found in response');
        return new WP_Error('payment_link_error', 'Payment link not found in response');
    }

    protected function generate_signature($data)
    {
        $signString = '';
        foreach ($data as $key => $value) {
            $signString .= $key . '=' . urlencode(htmlspecialchars_decode($value)) . '&';
        }
        return md5($signString . 'passphrase=' . $this->passphrase);
    }

    public function log($message)
    {
        if ('yes' === $this->get_option('dev') || 'yes' === $this->get_option('enable_logging')) {
            if (empty($this->logger)) {
                $this->logger = new WC_Logger();
            }
            $this->logger->add('bobpay-logs', $message);
        }
    }

    public function setup_constants()
    {
        if (defined('BOBPAY_USER_AGENT')) {
            return;
        }

        define('BOBPAY_SOFTWARE_NAME', 'WooCommerce');
        define('BOBPAY_SOFTWARE_VER', WC_VERSION);
        define('BOBPAY_MODULE_NAME', 'BobPay-Woocommerce-Plugin');
        define('BOBPAY_MODULE_VER', $this->version);

        $features = 'PHP ' . phpversion() . ';';
        if (in_array('curl', get_loaded_extensions())) {
            define('WC_BOBPAY_CURL', '');
            $version = curl_version();
            $features .= ' curl ' . $version['version'] . ';';
        } else {
            $features .= ' nocurl;';
        }
        define('BOBPAY_USER_AGENT', BOBPAY_SOFTWARE_NAME . '/' . BOBPAY_SOFTWARE_VER . ' (' . trim($features) . ') ' . BOBPAY_MODULE_NAME . '/' . BOBPAY_MODULE_VER);

        do_action('woocommerce_bobpay_plugin_setup_constants');
    }

    // Process refund
    public function process_refund($order_id, $amount = null, $reason = '')
    {
        $order = wc_get_order($order_id);

        if ( ! $order || ! $order->get_id() ) {
            return new WP_Error( 'error', 'Invalid order ID' );
        }

        $order_total = $order->get_total();

        if ($amount === null || $order_total === null) {
            return new WP_Error('error', 'Invalid refund amount');
        }

        if (empty($this->api_key)) {
            return new WP_Error('error', 'Bob Pay API key is missing');
        }

        $bob_pay_payment_id = $order->get_meta('bob_pay_payment_id');
        if (empty($bob_pay_payment_id)) {
            return new WP_Error('error', 'Bob Pay payment ID is missing');
        }

        $body = array(
            'id' => intval($bob_pay_payment_id),
            'reversal_amount' => floatval($amount)
        );

        $bob_pay_method = $order->get_meta('bob_pay_payment_method');
        if ($bob_pay_method === 'scan-to-pay' || (empty($bob_pay_method) && $order->get_payment_method() === 'bobpay_scan_to_pay')) {
            $body['reverse_directly_to_bank'] = false;
        }

        $this->log('Processing refund request: ' . wp_json_encode($body));

        $response = wp_remote_post(
            $this->api_endpoint . '/payments/reversal',
            array(
                'headers' => [
                    'Content-Type' => 'application/json',
                    'Authorization' => 'Bearer ' . $this->api_key,
                ],
                'timeout' => 60,
                'body' => wp_json_encode($body),
                'user-agent' => BOBPAY_USER_AGENT,
            )
        );

        $response_data = json_decode(wp_remote_retrieve_body($response));
        $this->log('Refund response: ' . wp_json_encode($response_data));

        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
            return new WP_Error('error', wp_json_encode($response_data));
        }

        $status = strtolower($response_data->PaymentMethod->status);
        if ($status === 'refunded' || $status === 'partially_refunded' || $status === 'refund_pending') {
            $amount = $response_data->PaymentMethod->amount;
            $order->add_order_note(
                sprintf(
                    'Refunded %s via Bob Pay. Reason: %s',
                    wc_price($amount),
                    $reason
                )
            );
            return true;
        } else {
            return new WP_Error('error', 'Error processing refund request');
        }
    }

    public function validate_integration($api_key, $dev_mode = null) {
        if ($dev_mode !== null) {
            $this->set_api_urls($dev_mode);
        }

        $body = array(
            'url' => home_url(),
            'integration_type' => 'woocommerce',
            'metadata' => array(
                'plugin_version' => WC_BOBPAY_PLUGIN_VERSION,
                'woocommerce_version' => WC_VERSION,
                'wordpress_version' => get_bloginfo('version'),
                'php_version' => phpversion(),
            ),
        );

        $this->log('Validate API key request body: ' . wp_json_encode($body));
        $this->log('Validate API key request API key header: ' . $api_key);
        $this->log('Validate API key request URL: ' . $this->api_endpoint . '/accounts/integrations');

        $results = wp_remote_request($this->api_endpoint . '/accounts/integrations', array(
            'method' => 'PATCH',
            'headers' => array(
                'Authorization' => 'Bearer ' . $api_key,
                'Content-Type' => 'application/json',
            ),
            'timeout' => 60,
            'body' => wp_json_encode($body),
            'user-agent' => BOBPAY_USER_AGENT,
        ));

        $response_data = json_decode(wp_remote_retrieve_body($results));
        $this->log('Validate API key response: ' . wp_json_encode($response_data));

        $response_code = wp_remote_retrieve_response_code($results);
        $this->log('Validate API key response code: ' . $response_code);

        return (int) $response_code;
    }
}
