/**
 * ConvesioPay - Unified Blocks Checkout
 *
 * Purpose:
 * - Provide a single-mount, non-destructive Blocks checkout integration.
 * - Keep WooCommerce Blocks totals and session flow as the source of truth.
 * - Support Card, Apple Pay, Google Pay, and BTCPay through one unified runtime state.
 *
 * Design Principles:
 * - Strict submit preflight to prevent stale-amount and stale-method charges.
 * - Idempotent event orchestration across repeated store updates and UI re-renders.
 * - In-place amount/session refreshes without destructive remount behavior.
 * - Safe fallback behavior when native Blocks registration is unavailable.
 *
 * Notes:
 * - This file is the active Blocks v2 implementation for unified checkout.
 * - Legacy Blocks script is retained only as fallback/rollback reference.
 * - Checkout synchronization must remain resilient to repeated
 *   store resolution cycles and rapid method switching.
 */
(function () {
    'use strict';

    if (window.__convesiopayUnifiedBlocksBootstrapped) {
        return;
    }
    window.__convesiopayUnifiedBlocksBootstrapped = true;

    // This is the WooCommerce Blocks API.
    const { registerPaymentMethod } = window.wc?.wcBlocksRegistry || {};

    // This is the WordPress element API.
    const { createElement, useEffect } = window.wp?.element || {};

    // This is the WordPress i18n API.
    const { __ } = window.wp?.i18n || {};

    // This is the WooCommerce settings API.
    const getSetting = window.wc?.wcSettings?.getSetting || function (_key, defaultValue) { return defaultValue; };

    // This is the settings object that stores the settings for the checkout.
    const settings = getSetting('peachpay_convesiopay_unified_data', {});

    // This is the active methods object that stores the active methods for the checkout.
    const activeMethods = settings?.active_methods || [];

    // The WooCommerce Blocks API is required for any checkout behavior.
    if (!registerPaymentMethod) {
        return;
    }

    // This is the configuration object that stores the configuration for the checkout.
    const CONFIG = Object.freeze({
        gatewayId: 'peachpay_convesiopay_unified',
        checkoutRefreshDebounceMs: 220,
        customerContextDebounceMs: 450,
        sessionAction: 'peachpay_store_convesiopay_method',
        /** AJAX action to set WC chosen_payment_method in session (Blocks). */
        blocksSetChosenPaymentMethodAction: 'peachpay_convesiopay_blocks_set_chosen_payment_method',
        persistentMountNodeId: 'convesiopay-unified-persistent-mount'
    });

    // This is the state object that stores the current state of the checkout.
    const state = {
        initialized: false, // This is a flag that indicates if the checkout has been initialized.
        mounted: false, // This is a flag that indicates if the checkout has been mounted.
        component: null, // This is the component object that stores the component for the checkout.
        currentMethod: null, // This is the current method that is selected for the checkout.
        lastSelectedGatewayId: '', // This is the last selected Woo Blocks gateway id.
        lastSyncedMethod: '', // This is the last method that was synced to the checkout.
        lastKnownCartTotal: 0, // This is the last known cart total.
        btcSession: null, // This is the BTC session object that stores the BTC session for the checkout.
        paymentConfirmedData: null, // This is the payment confirmed data object that stores the payment confirmed data for the checkout.
        methodSwitchInProgress: false, // This is a flag that indicates if the method switch is in progress.
        methodSwitchTarget: '', // This is the target method that is being switched to for the checkout.
        refreshingSessions: false, // This is a flag that indicates if the sessions are being refreshed.
        pendingSessionRefreshReason: '', // This is the reason for the pending session refresh.
        fetchBridgeInstalled: false, // This is a flag that indicates if the fetch bridge is installed.
        nativeBlocksRegistered: false, // This is a flag that indicates if the native blocks are registered.
        suppressFetchBridge: false, // This is a flag that indicates if the fetch bridge is suppressed.
        btcMessageListenerBound: false, // This is a flag that indicates if BTCPay postMessage listener is already bound.
        sessionsPrimed: false, // This is a flag that indicates if the sessions are primed.
        initialPrimingPromise: null, // This is the initial priming promise.
        mountPromise: null, // Single-flight guard for native component mounts.
        methodSyncFailed: false, // This is a flag that indicates if the method sync failed.
        submitInProgress: false, // This is a flag that indicates if the submit is in progress.
        submitInteractionLocked: false, // This is a flag that indicates if the submit interaction is locked.
        storeTotalsResolving: false, // This is a flag that indicates if the store totals are resolving.
        currentCartSignature: '', // This is the signature for the current cart.
        applePaySessionSignature: '', // This is the signature for the Apple Pay session.
        applePaySessionPrimed: false, // This is a flag that indicates if the Apple Pay session is primed.
        googlePaySessionSignature: '', // This is the signature for the Google Pay session.
        googlePaySessionPrimed: false, // This is a flag that indicates if the Google Pay session is primed.
        btcSessionSignature: '', // This is the signature for the BTC session.
        lastKnownAmountCurrencySignature: '', // This is the signature for the last known amount and currency.
        latestBackendCartData: null, // This is the latest backend cart data.
        gatewaySelectionSyncVersion: 0, // Monotonic version to discard stale gateway-sync completions.
        lastFeeMirrorReason: '', // Last requested reason for coalesced fee mirror update.
        lastUserIntentMethod: '', // Last explicit method selected by user interaction.
        lastUserIntentAtMs: 0, // Timestamp of last explicit method intent.
        suppressNativeCardChangeUntilMs: 0, // Suppress default SDK card change during settle windows.
        lastBridgeMethod: '', // This is the last bridge method.
        lastBridgeAtMs: 0, // This is the last bridge at time.
        applePayActionPreflightInProgress: false, // This is a flag that indicates if the Apple Pay action preflight is in progress.
        applePayAutoSubmitInProgress: false, // This is a flag that indicates if the Apple Pay auto submit is in progress.
        googlePayAutoSubmitInProgress: false, // This is a flag that indicates if the Google Pay auto submit is in progress.
        googlePayUiLockUntilMs: 0, // Defer Google Pay session rebuilds while the browser payment sheet is active.
        allowNextApplePayActionClick: false, // This is a flag that indicates if the next Apple Pay action click is allowed.
        traceStartMs: 0, // This is the start time for the trace.
        traceSteps: [], // This is the array of trace steps.
        timers: {
            refresh: null, // This is the refresh timer.
            customerContext: null, // This is the customer context timer.
            methodSwitchUnlock: null, // This is the method switch unlock timer.
            submitLockFailsafe: null, // This is the submit lock failsafe timer.
            feeMirrorGrace: null, // This is the fee mirror grace timer.
            feeMirrorRetry: null, // Retry timer for transient no-fee snapshots.
            feeMirrorCoalesce: null // Coalesced mirror scheduler timer.
        }
    };

    /**
     * Triggers Woo place-order button after Apple Pay success.
     * This aligns wallet success UX with classic checkout handoff behavior.
     *
     * @param {string=} source
     * @returns {void}
     */
    function attemptAutoPlaceOrderFromApplePaySuccess(source) {
        // If the Apple Pay auto submit is in progress, return.
        if (state.applePayAutoSubmitInProgress) {
            return;
        }
        /** 
         * If the submit is in progress, 
         * the Apple Pay action preflight is in progress, 
         * the method switch is in progress, 
         * the sessions are being refreshed, 
         * the submit interaction is locked, 
         * the store totals are resolving, 
         * or the store totals are resolving, return.
         * 
         * @returns {void}
         */
        if (state.submitInProgress || state.applePayActionPreflightInProgress || state.methodSwitchInProgress || state.refreshingSessions || state.submitInteractionLocked || state.storeTotalsResolving || isStoreTotalsResolving()) {
            debugLog('Skipping Apple Pay auto-submit while checkout is in-flight', {
                source: source || 'unspecified'
            });
            return;
        }

        // If the current method is not Apple Pay, return.
        if (normalizeMethod(state.currentMethod || state.lastSyncedMethod) !== 'applepay') {
            return;
        }

        // Get the payload from the state.
        const payload = state.paymentConfirmedData;

        // Get the method from the payload.
        const payloadMethod = normalizeMethod(payload?.paymentMethod || '');

        // Get the has token from the payload, the window.convesiopayPaymentToken, or the window.convesiopayApplePayPaymentData.token.
        const hasToken = Boolean(payload?.token || window.convesiopayPaymentToken || window.convesiopayApplePayPaymentData?.token);

        // If the payload method is not Apple Pay or the has token is false, return.
        if (payloadMethod !== 'applepay' || !hasToken) {
            debugLog('Skipping Apple Pay auto-submit: payload not ready', {
                source: source || 'unspecified',
                payloadMethod: payloadMethod || null,
                hasToken: hasToken
            });
            return;
        }

        // Get the place order button from the document.
        const placeOrderButton = document.querySelector('.wc-block-components-checkout-place-order-button');

        // If the place order button is not found or the click function is not a function, return.
        if (!placeOrderButton || typeof placeOrderButton.click !== 'function') {
            debugLog('Skipping Apple Pay auto-submit: place-order button missing', {
                source: source || 'unspecified'
            });
            return;
        }

        // Set the Apple Pay auto submit in progress to true.
        state.applePayAutoSubmitInProgress = true;

        traceCheckoutEvent('submit:auto_trigger_applepay', { source: source || 'unspecified' });

        setTimeout(function () {
            /** 
             * If the submit is in progress, the method switch is in progress, the sessions are being refreshed, the store totals are resolving, or the store totals are resolving, return.
             * 
             * @returns {void}
             */
            if (
                state.submitInProgress ||
                state.methodSwitchInProgress ||
                state.refreshingSessions ||
                state.storeTotalsResolving ||
                isStoreTotalsResolving()
            ) {
                state.applePayAutoSubmitInProgress = false;
                debugLog('Skipping deferred Apple Pay auto-submit: checkout became busy', {
                    source: source || 'unspecified'
                });
                return;
            }

            // Click the place order button.
            placeOrderButton.click();

            // Allow subsequent retries if Woo blocks submit for any reason.
            setTimeout(function () {
                state.applePayAutoSubmitInProgress = false;
            }, 1000);
        }, 0);
    }

    /**
     * Triggers Woo place-order button after Google Pay success.
     *
     * @param {string=} source
     * @returns {void}
     */
    function attemptAutoPlaceOrderFromGooglePaySuccess(source) {
        if (state.googlePayAutoSubmitInProgress) {
            return;
        }
        if (state.submitInProgress || state.methodSwitchInProgress || state.refreshingSessions || state.submitInteractionLocked || state.storeTotalsResolving || isStoreTotalsResolving()) {
            debugLog('Skipping Google Pay auto-submit while checkout is in-flight', {
                source: source || 'unspecified'
            });
            return;
        }
        if (normalizeMethod(state.currentMethod || state.lastSyncedMethod) !== 'googlepay') {
            return;
        }
        const payload = state.paymentConfirmedData;
        const payloadMethod = normalizeMethod(payload?.paymentMethod || '');
        const hasToken = Boolean(payload?.token || window.convesiopayPaymentToken || window.convesiopayGooglePayPaymentData?.token);
        if (payloadMethod !== 'googlepay' || !hasToken) {
            debugLog('Skipping Google Pay auto-submit: payload not ready', {
                source: source || 'unspecified',
                payloadMethod: payloadMethod || null,
                hasToken: hasToken
            });
            return;
        }
        const placeOrderButton = document.querySelector('.wc-block-components-checkout-place-order-button');
        if (!placeOrderButton || typeof placeOrderButton.click !== 'function') {
            debugLog('Skipping Google Pay auto-submit: place-order button missing', {
                source: source || 'unspecified'
            });
            return;
        }
        state.googlePayAutoSubmitInProgress = true;
        traceCheckoutEvent('submit:auto_trigger_googlepay', { source: source || 'unspecified' });
        setTimeout(function () {
            if (
                state.submitInProgress ||
                state.methodSwitchInProgress ||
                state.refreshingSessions ||
                state.storeTotalsResolving ||
                isStoreTotalsResolving()
            ) {
                state.googlePayAutoSubmitInProgress = false;
                debugLog('Skipping deferred Google Pay auto-submit: checkout became busy', {
                    source: source || 'unspecified'
                });
                return;
            }
            placeOrderButton.click();
            setTimeout(function () {
                state.googlePayAutoSubmitInProgress = false;
            }, 1000);
        }, 0);
    }

    /**
     * Determines whether advanced debug logging is enabled.
     *
     * @returns {boolean}
     */
    function isAdvancedLoggingEnabled() {
        try {
            return true;
            if (window && window.convesiopayBlocksForceDebug === true) {
                return true;
            }
            if (window && window.localStorage) {
                const localDebug = String(window.localStorage.getItem('convesiopay_blocks_debug') || '').trim().toLowerCase();
                if (localDebug === '1' || localDebug === 'true' || localDebug === 'yes' || localDebug === 'on') {
                    return true;
                }
            }
            const raw = settings?.peachpay_is_advanced_logging_enabled;
            if (typeof raw === 'boolean') {
                return raw;
            }
            if (typeof raw === 'number') {
                return raw === 1;
            }
            if (typeof raw === 'string') {
                const normalized = raw.trim().toLowerCase();
                return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
            }
            return false;
        } catch (_e) {
            return false;
        }
    }

    /**
     * Logs a debug message when advanced logging is enabled.
     *
     * @param {string} message
     * @param {Object=} context
     * @returns {void}
     */
    function debugLog(message, context) {
        try {
            if (!isAdvancedLoggingEnabled()) {
                return;
            }
            if (typeof context === 'undefined') {
                console.log('[ConvesioPay][Blocks]', String(message || ''));
                return;
            }
            console.log('[ConvesioPay][Blocks]', String(message || ''), context);
        } catch (_e) {
            // Logging must never interrupt checkout.
        }
    }

    /**
     * Defer Google Pay session refreshes while the PaymentRequest sheet is opening/open.
     *
     * @param {string=} source
     * @returns {void}
     */
    function lockGooglePayUiRefresh(source) {
        state.googlePayUiLockUntilMs = Date.now() + 15000;
        debugLog('Google Pay UI refresh lock set', {
            source: source || 'unspecified',
            lockedUntil: state.googlePayUiLockUntilMs
        });
    }

    /**
     * Determine whether Google Pay session rebuilds should be deferred.
     *
     * @returns {boolean}
     */
    function isGooglePayUiRefreshLocked() {
        return state.googlePayUiLockUntilMs > Date.now();
    }

    /**
     * Emits compact timeline trace logs for checkout sequencing.
     *
     * @param {string} step
     * @param {Object=} context
     * @returns {void}
     */
    function traceCheckoutEvent(step, context) {
        try {
            if (!isAdvancedLoggingEnabled()) {
                return;
            }
            const now = Date.now();
            if (!state.traceStartMs) {
                state.traceStartMs = now;
            }
            const deltaMs = Math.max(0, now - state.traceStartMs);
            const compactStep = String(step || '').trim();
            if (!compactStep) {
                return;
            }
            state.traceSteps.push(compactStep);
            if (state.traceSteps.length > 8) {
                state.traceSteps.shift();
            }
            debugLog('[timeline +' + deltaMs + 'ms] ' + state.traceSteps.join(' -> '), context || {});
        } catch (_e) {
            // Trace logs are optional and must never break checkout.
        }
    }

    /**
     * Reads Woo Blocks cart totals from the store selector.
     *
     * @returns {Object|null}
     */
    function getStoreCartTotals() {
        try {
            if (!(window.wp && window.wp.data && window.wp.data.select)) {
                return null;
            }
            const store = window.wp.data.select('wc/store/cart');
            return store?.getCartTotals ? store.getCartTotals() : null;
        } catch (_e) {
            return null;
        }
    }

    /**
     * Reads Woo Blocks full cart data from the store selector.
     *
     * @returns {Object|null}
     */
    function getStoreCartData() {
        try {
            if (!(window.wp && window.wp.data && window.wp.data.select)) {
                return null;
            }
            const store = window.wp.data.select('wc/store/cart');
            return store?.getCartData ? store.getCartData() : null;
        } catch (_e) {
            return null;
        }
    }

    /**
     * Resolves currently selected Woo Blocks payment gateway id.
     *
     * @returns {string}
     */
    function getSelectedGatewayId() {
        function normalizeGatewayIdCandidate(candidate) {
            let value = '';
            if (typeof candidate === 'string') {
                value = candidate;
            } else if (candidate && typeof candidate === 'object') {
                value = String(
                    candidate.gatewayId ||
                    candidate.id ||
                    candidate.payment_method ||
                    candidate.paymentMethod ||
                    candidate.value ||
                    ''
                );
            } else {
                value = String(candidate || '');
            }

            value = String(value || '').trim();
            if (!value) {
                return '';
            }

            // Internal ConvesioPay method names are not Woo gateway ids.
            if (value === 'card' || value === 'applepay' || value === 'googlepay' || value === 'btcpay') {
                return '';
            }

            // Guard against shipping rate ids being misread as payment gateway ids.
            if (/^(flat_rate|free_shipping|local_pickup|pickup_location|table_rate):/i.test(value)) {
                return '';
            }

            return value;
        }

        try {
            if (window.wp && window.wp.data && window.wp.data.select) {
                const checkoutStore = window.wp.data.select('wc/store/checkout');
                const candidates = [
                    checkoutStore?.getSelectedPaymentMethod?.(),
                    checkoutStore?.getActivePaymentMethod?.(),
                    checkoutStore?.getPaymentMethod?.(),
                    checkoutStore?.getState?.()?.paymentMethod
                ];
                for (let i = 0; i < candidates.length; i += 1) {
                    const value = normalizeGatewayIdCandidate(candidates[i]);
                    if (value) {
                        return value;
                    }
                }
            }
        } catch (_e) {
            // Fall through to DOM fallback.
        }
        const checkedInput = document.querySelector(
            [
                'input[name="radio-control-wc-payment-method-options"]:checked',
                'input[name^="radio-control-wc-payment-method-options"]:checked',
                '.wc-block-checkout__payment-method input[type="radio"]:checked',
                '.wc-block-components-checkout-step--payment-methods input[type="radio"]:checked'
            ].join(', ')
        );
        return normalizeGatewayIdCandidate(checkedInput?.value || '');
    }

    /**
     * Returns whether ConvesioPay unified gateway is currently selected.
     *
     * @returns {boolean}
     */
    function isUnifiedGatewaySelected() {
        return getSelectedGatewayId() === CONFIG.gatewayId;
    }

    /**
     * Ensures a fresh mount node exists inside the active container.
     * Blocks flow is allowed to remount when re-entering unified gateway.
     *
     * @param {HTMLElement|null} container
     * @returns {HTMLElement|null}
     */
    function ensureMountNode(container) {
        if (!container) {
            return null;
        }
        container.innerHTML = '';
        const node = document.createElement('div');
        node.id = CONFIG.persistentMountNodeId;
        node.className = 'convesiopay-component-container';
        node.style.width = '100%';
        node.style.minWidth = '100%';
        node.style.display = 'flex';
        node.style.alignItems = 'center';
        node.style.justifyContent = 'center';
        container.appendChild(node);
        return node;
    }

    /**
     * Waits briefly for unified mount container to appear in DOM.
     *
     * @param {number=} timeoutMs
     * @returns {Promise<HTMLElement|null>}
     */
    function waitForUnifiedMountContainer(timeoutMs) {
        const maxWaitMs = Number.isFinite(timeoutMs) ? timeoutMs : 2500;
        const startedAt = Date.now();
        return new Promise(function (resolve) {
            function check() {
                if (!isUnifiedGatewaySelected()) {
                    resolve(null);
                    return;
                }
                const container = document.querySelector('#convesiopay-unified-component-container');
                if (container) {
                    resolve(container);
                    return;
                }
                if ((Date.now() - startedAt) >= maxWaitMs) {
                    resolve(null);
                    return;
                }
                setTimeout(check, 80);
            }
            check();
        });
    }

    /**
     * Parses localized money text into integer cents.
     *
     * @param {string|number} value
     * @returns {number}
     */
    function parsePriceToCents(value) {
        try {
            const text = String(value || '').trim();
            if (!text) {
                return 0;
            }
            const cleaned = text.replace(/[^\d.,-]/g, '');
            if (!cleaned) {
                return 0;
            }
            const lastDot = cleaned.lastIndexOf('.');
            const lastComma = cleaned.lastIndexOf(',');
            const splitAt = Math.max(lastDot, lastComma);
            let normalized;
            if (splitAt >= 0) {
                const integerPart = cleaned.slice(0, splitAt).replace(/[.,]/g, '');
                const decimalPart = cleaned.slice(splitAt + 1).replace(/[.,]/g, '');
                normalized = integerPart + (decimalPart ? '.' + decimalPart : '');
            } else {
                normalized = cleaned.replace(/[.,]/g, '');
            }
            const amount = parseFloat(normalized);
            if (Number.isNaN(amount)) {
                return 0;
            }
            return Math.round(amount * 100);
        } catch (_e) {
            return 0;
        }
    }

    /**
     * Resolves configured fee label for the active/target method.
     *
     * @param {string=} method
     * @returns {string}
     */
    function getConfiguredFeeLabelForMethod(method) {
        const feeConfig = settings?.fee_config || {};
        const normalizedMethod = normalizeMethod(method || state.currentMethod || getDefaultUnifiedMethod());
        const fromCurrent = String(feeConfig?.[normalizedMethod]?.label || '').trim();
        if (fromCurrent) {
            return fromCurrent;
        }
        const fallbackMethods = ['card', 'applepay', 'googlepay', 'btcpay'];
        for (let i = 0; i < fallbackMethods.length; i += 1) {
            const label = String(feeConfig?.[fallbackMethods[i]]?.label || '').trim();
            if (label) {
                return label;
            }
        }
        return '';
    }

    /**
     * Ensures a custom fee row exists for mirror fallback rendering.
     *
     * This function checks whether a ConvesioPay fee row already exists
     * in the WooCommerce Blocks totals section. If not, it creates one
     * and inserts it before the order total row.
     *
     * @returns {HTMLElement|null} The existing or newly created fee row element,
     * or null if insertion is not possible.
     */
    function ensureFeeRowElement() {
        // Look for an existing fee row with our specific data attribute
        let feeRow = document.querySelector('.wc-block-components-totals-fees[data-convesiopay-fee="1"]');
        if (feeRow) {
            return feeRow;
        }

        // Get the order total row from the document.
        const orderTotalRow = document.querySelector('.wc-block-components-totals-footer-item');

        // If the order total row is not found or the parent element is not found, return null.
        if (!orderTotalRow || !orderTotalRow.parentElement) {
            return null;
        }

        // Create a new div element for the custom fee row.
        feeRow = document.createElement('div');


        // Assign WooCommerce totals CSS classes for consistent styling.
        feeRow.className = 'wc-block-components-totals-item wc-block-components-totals-fees';

        // Add a custom attribute to identify this row later.
        feeRow.setAttribute('data-convesiopay-fee', '1');

        // Set inner HTML structure to match WooCommerce totals markup format.
        feeRow.innerHTML =
            '<span class="wc-block-components-totals-item__label"></span>' +
            '<span class="wc-block-components-totals-item__value">' +
            '<span class="wc-block-formatted-money-amount wc-block-components-formatted-money-amount wc-block-components-totals-item__value"></span>' +
            '</span>';

        // Insert the new fee row before the order total row.
        orderTotalRow.parentElement.insertBefore(feeRow, orderTotalRow);

        // Return the newly created fee row element.
        return feeRow;
    }

    /**
     * Normalizes row label text for stable comparisons.
     *
     * @param {string} value
     * @returns {string}
     */
    function normalizeLabelText(value) {
        return String(value || '').trim().replace(/\s+/g, ' ').toLowerCase();
    }

    /**
     * Removes custom mirrored fee rows from totals UI.
     *
     * @returns {void}
     */
    function removeCustomFeeRows() {

        // Select all ConvesioPay custom fee rows using the unique data attribute.
        const customRows = document.querySelectorAll('.wc-block-components-totals-fees[data-convesiopay-fee="1"]');

        // Iterate over each matched custom fee row.
        customRows.forEach(function (row) {

            // Ensure the row exists and is attached to a parent node before removing.
            if (row && row.parentNode) {

                // Remove the fee row from the DOM.
                row.parentNode.removeChild(row);
            }
        });
    }

    /**
     * Checks if Woo already rendered a native fee row for label.
     *
     * @param {string} label
     * @returns {boolean}
     */
    function hasNativeFeeRowForLabel(label) {

        // Normalize the incoming label for consistent comparison.
        const expected = normalizeLabelText(label);

        // If normalized label is empty or invalid, return false early.
        if (!expected) {
            return false;
        }

        // Select all native Woo fee rows (exclude ConvesioPay custom rows).
        const nativeRows = document.querySelectorAll(
            '.wc-block-components-totals-fees:not([data-convesiopay-fee="1"])'
        );

        // Iterate through each native fee row.
        for (let i = 0; i < nativeRows.length; i += 1) {

            // Get the current row element.
            const row = nativeRows[i];

            // Extract and normalize the label text from the row.
            const rowLabel = normalizeLabelText(
                row?.querySelector('.wc-block-components-totals-item__label')?.textContent || ''
            );

            // If a normalized label exists and matches the expected label, return true.
            if (rowLabel && rowLabel === expected) {
                return true;
            }
        }

        // If no matching native fee row is found, return false.
        return false;
    }

    /**
     * Extracts a fee total amount in integer cents.
     *
     * @param {Object} fee
     * @returns {number}
     */
    function parseFeeTotalCents(fee) {
        return parseInt(
            String(fee?.totals?.total || fee?.totals?.total_price || fee?.total || '0'),
            10
        );
    }

    /**
     * Builds a temporary fee estimate from config as last-resort UI fallback.
     *
     * @param {Object} totals
     * @param {string=} method
     * @returns {Object|null}
     */
    function buildEstimatedFeeFromConfig(totals, method) {
        const feeConfig = settings?.fee_config || {};
        const normalizedMethod = normalizeMethod(method || state.currentMethod || getDefaultUnifiedMethod());
        const methodConfig = feeConfig?.[normalizedMethod] || null;
        if (!methodConfig) {
            return null;
        }
        const enabledRaw = methodConfig.enabled;
        const enabled = enabledRaw === true ||
            enabledRaw === 1 ||
            String(enabledRaw || '').trim().toLowerCase() === 'yes' ||
            String(enabledRaw || '').trim().toLowerCase() === 'true' ||
            String(enabledRaw || '').trim().toLowerCase() === '1' ||
            String(enabledRaw || '').trim().toLowerCase() === 'on';
        if (!enabled) {
            return null;
        }
        const feeType = String(methodConfig.type || '').toLowerCase();
        const rawAmount = parseFloat(String(methodConfig.amount || '0'));
        if (!Number.isFinite(rawAmount) || rawAmount <= 0) {
            return null;
        }

        const totalCents = parseInt(String(totals?.total_price || '0'), 10);
        const baseAmount = Number.isFinite(totalCents) && totalCents > 0 ? (totalCents / 100) : 0;
        if (baseAmount <= 0) {
            return null;
        }

        let computed = 0;
        if (feeType === 'percentage' || feeType === 'percent') {
            computed = baseAmount * (rawAmount / 100);
        } else {
            computed = rawAmount;
        }
        const rounded = Math.round((computed + 0.00000001) * 100) / 100;
        if (!(rounded > 0)) {
            return null;
        }

        return {
            name: String(methodConfig.label || 'Payment gateway fee'),
            estimatedAmount: rounded
        };
    }

    /**
     * Selects the best matching ConvesioPay fee from store fee rows.
     *
     * @param {Array} fees
     * @returns {Object|null}
     */
    function selectFeeForMirror(fees) {
        const feeMethod = normalizeMethod(
            state.methodSwitchTarget ||
            (hasRecentUserIntent(state.lastUserIntentMethod, 2400) ? state.lastUserIntentMethod : '') ||
            state.lastSyncedMethod ||
            state.currentMethod ||
            getDefaultUnifiedMethod()
        ) || getDefaultUnifiedMethod();
        if (!Array.isArray(fees) || fees.length === 0) {
            debugLog('Fee mirror: no fees in cart data', {
                method: feeMethod
            });
            return null;
        }
        const expectedLabel = getConfiguredFeeLabelForMethod(feeMethod).toLowerCase();
        debugLog('Fee mirror: selecting fee candidate', {
            method: feeMethod,
            expectedLabel: expectedLabel || null,
            feeNames: fees.map(function (fee) { return String(fee?.name || ''); }),
            feeTotals: fees.map(function (fee) { return parseFeeTotalCents(fee); })
        });
        if (expectedLabel) {
            const exact = fees.find(function (fee) {
                return String(fee?.name || '').trim().toLowerCase() === expectedLabel;
            });
            if (exact) {
                debugLog('Fee mirror: matched expected label', {
                    expectedLabel: expectedLabel,
                    matchedName: String(exact?.name || '')
                });
                return exact;
            }
        }

        // Fallback only to positive fees to avoid hijacking wallet/store-credit
        // adjustments (typically negative) as the ConvesioPay fee row.
        const positive = fees.find(function (fee) {
            const cents = parseFeeTotalCents(fee);
            return Number.isFinite(cents) && cents > 0;
        });
        if (positive) {
            return positive;
        }

        debugLog('Fee mirror: no ConvesioPay fee candidate found in cart fees', {
            method: feeMethod,
            expectedLabel: expectedLabel || null
        });
        return null;
    }

    /**
     * Applies fee row UI from canonical cart data.
     *
     * @param {Object} cartData
     * @param {string=} reason
     * @returns {void}
     */
    function applyFeeRowFromCartData(cartData, reason) {
        // Fee mirror should only render when unified gateway is selected.
        if (!isUnifiedGatewaySelected()) {
            if (state.timers.feeMirrorGrace) {
                clearTimeout(state.timers.feeMirrorGrace);
                state.timers.feeMirrorGrace = null;
            }
            removeCustomFeeRows();
            return;
        }

        // Extract fees array safely from cart data.
        const fees = Array.isArray(cartData?.fees) ? cartData.fees : [];

        // Resolve totals from cartData or fallback to store totals.
        const totals = cartData?.totals || getStoreCartTotals() || {};

        // Determine currency symbol with safe fallback.
        const currencySymbol = String(totals?.currency_symbol || '$');

        // Select the fee that should be mirrored in UI.
        const selectedFee = selectFeeForMirror(fees);

        // Canonical-first policy: never estimate fee rows from static config.
        // Render only backend/store-confirmed fees to avoid UI/total mismatches.
        const hasFees = Boolean(selectedFee);

        // If no fee exists, clear timers, remove custom rows, and exit.
        if (!hasFees) {
            const checkoutBusy = state.methodSwitchInProgress || state.refreshingSessions || state.storeTotalsResolving || isStoreTotalsResolving();
            if (checkoutBusy && isUnifiedGatewaySelected()) {
                // During transient recalculation windows, keep existing fee row visible (if any)
                // and retry shortly. This avoids flicker/false negatives from temporary empty snapshots.
                if (!state.timers.feeMirrorRetry) {
                    state.timers.feeMirrorRetry = setTimeout(function () {
                        state.timers.feeMirrorRetry = null;
                        scheduleFeeMirrorUpdate('transient_no_fee_retry', 20);
                    }, 320);
                }
                debugLog('Fee mirror: transient no-fee snapshot, preserving current row', {
                    reason: reason || 'unspecified',
                    method: state.currentMethod || getDefaultUnifiedMethod(),
                    checkoutBusy: true
                });
                return;
            }
            if (state.timers.feeMirrorGrace) {
                clearTimeout(state.timers.feeMirrorGrace);
                state.timers.feeMirrorGrace = null;
            }
            if (state.timers.feeMirrorRetry) {
                clearTimeout(state.timers.feeMirrorRetry);
                state.timers.feeMirrorRetry = null;
            }
            if (state.timers.feeMirrorCoalesce) {
                clearTimeout(state.timers.feeMirrorCoalesce);
                state.timers.feeMirrorCoalesce = null;
            }
            removeCustomFeeRows();
            debugLog('Fee mirror: hiding fee row (no fee source)', {
                reason: reason || 'unspecified',
                method: state.currentMethod || getDefaultUnifiedMethod()
            });
            return;
        }

        // Default fallback label and amount.
        let feeLabel = 'Payment gateway fee';
        let feeAmount = 0;

        // If real store fee exists, extract its label and amount.
        if (selectedFee) {
            const fee = selectedFee;
            feeLabel = String(fee?.name || feeLabel);
            const feeTotalCents = parseFeeTotalCents(fee);
            feeAmount = Number.isFinite(feeTotalCents) ? (feeTotalCents / 100) : 0;
        }

        // If Woo Blocks already rendered the same fee row natively, remove our mirror row to avoid duplicate "Payment gateway fee" lines.
        if (hasNativeFeeRowForLabel(feeLabel)) {
            if (state.timers.feeMirrorGrace) {
                clearTimeout(state.timers.feeMirrorGrace);
                state.timers.feeMirrorGrace = null;
            }
            if (state.timers.feeMirrorRetry) {
                clearTimeout(state.timers.feeMirrorRetry);
                state.timers.feeMirrorRetry = null;
            }
            if (state.timers.feeMirrorCoalesce) {
                clearTimeout(state.timers.feeMirrorCoalesce);
                state.timers.feeMirrorCoalesce = null;
            }
            removeCustomFeeRows();
            debugLog('Fee mirror: using native fee row, custom mirror removed', {
                reason: reason || 'unspecified',
                feeLabel: feeLabel
            });
            return;
        }

        // Clear any existing grace timer before scheduling a new one.
        if (state.timers.feeMirrorGrace) {
            clearTimeout(state.timers.feeMirrorGrace);
        }


        // Track canonical source for diagnostics.
        const source = 'store_fees';

        // Capture cart signature for debugging trace.
        const cartSignature = state.currentCartSignature || getCurrentCartSignature();

        state.timers.feeMirrorGrace = setTimeout(function () {
            state.timers.feeMirrorGrace = null;
            if (!isUnifiedGatewaySelected()) {
                removeCustomFeeRows();
                return;
            }
            if (hasNativeFeeRowForLabel(feeLabel)) {
                removeCustomFeeRows();
                debugLog('Fee mirror: native row appeared during grace delay; skipped custom mirror', {
                    reason: reason || 'unspecified',
                    feeLabel: feeLabel
                });
                return;
            }
            // Ensure (or create) our custom fee row element.
            const feeRow = ensureFeeRowElement();

            // If fee row container cannot be created, log and exit.
            if (!feeRow) {
                debugLog('Fee mirror: totals fee row not found', {
                    reason: reason || 'unspecified'
                });
                return;
            }

            // Get label and value elements inside fee row.
            const labelEl = feeRow.querySelector('.wc-block-components-totals-item__label');
            const valueEl = feeRow.querySelector('.wc-block-components-totals-item__value');
            const innerValue = valueEl?.querySelector('.wc-block-formatted-money-amount');

            // Set fee label text.
            if (labelEl) {
                labelEl.textContent = feeLabel;
            }

            // Format using Woo Blocks price formatter (safe & consistent)
            const rawCents = Math.round(feeAmount * 100);

            const { formatPrice } =
                window.wc?.wcBlocksData?.priceFormat || {};

            const formattedAmount = formatPrice
                ? formatPrice(rawCents)
                : '$' + feeAmount.toFixed(2); // hard fallback

            if (innerValue) {
                innerValue.textContent = formattedAmount;
            } else if (valueEl) {
                valueEl.textContent = formattedAmount;
            }

            // Ensure the fee row is visible.
            feeRow.style.display = '';

            // Log successful mirror application for debugging.
            debugLog('Applied Blocks fee row mirror', {
                reason: reason || 'unspecified',
                source: source,
                feeLabel: feeLabel,
                feeAmount: feeAmount,
                method: state.currentMethod || getDefaultUnifiedMethod(),
                cartSignature: cartSignature
            });
        }, 220);
    }

    /**
     * Mirrors fee row from Blocks store data (or cart endpoint fallback).
     *
     * @param {string=} reason
     * @returns {Promise<void>}
     */
    async function mirrorFeeRowFromStore(reason) {
        // Never mirror ConvesioPay fee when another Woo gateway is selected.
        if (!isUnifiedGatewaySelected()) {
            if (state.timers.feeMirrorGrace) {
                clearTimeout(state.timers.feeMirrorGrace);
                state.timers.feeMirrorGrace = null;
            }
            if (state.timers.feeMirrorRetry) {
                clearTimeout(state.timers.feeMirrorRetry);
                state.timers.feeMirrorRetry = null;
            }
            if (state.timers.feeMirrorCoalesce) {
                clearTimeout(state.timers.feeMirrorCoalesce);
                state.timers.feeMirrorCoalesce = null;
            }
            removeCustomFeeRows();
            return;
        }

        // Attempt to retrieve cart data from the Woo Blocks data store.
        let cartData = getStoreCartData();

        debugLog('Fee mirror: start', {
            reason: reason || 'unspecified',
            hasStoreCartData: Boolean(cartData),
            method: state.currentMethod || getDefaultUnifiedMethod()
        });

        const hasStoreFees = Array.isArray(cartData?.fees) && cartData.fees.length > 0;
        if (!hasStoreFees && Array.isArray(state.latestBackendCartData?.fees) && state.latestBackendCartData.fees.length > 0) {
            cartData = state.latestBackendCartData;
        }

        // If store cart data is missing/empty, fallback to cart REST endpoint.
        if (!cartData || !Array.isArray(cartData.fees) || cartData.fees.length === 0) {
            try {

                // Fetch latest cart data from Woo Store API endpoint.
                const response = await fetch('/wp-json/wc/store/v1/cart', {
                    method: 'GET',
                    credentials: 'same-origin',
                    headers: {
                        'Content-Type': 'application/json',
                        'Nonce': window.wcBlocksMiddlewareConfig?.storeApiNonce || ''
                    }
                });

                // Parse the JSON response into cartData.
                cartData = await response.json();

                // If fallback is still empty, prefer latest backend snapshot when available.
                if ((!Array.isArray(cartData?.fees) || cartData.fees.length === 0) && Array.isArray(state.latestBackendCartData?.fees) && state.latestBackendCartData.fees.length > 0) {
                    cartData = state.latestBackendCartData;
                }

                // Log successful fallback fetch with fee diagnostics.
                debugLog('Fee mirror: fetched cart endpoint fallback', {
                    reason: reason || 'unspecified',
                    hasFeesArray: Array.isArray(cartData?.fees),
                    feeCount: Array.isArray(cartData?.fees) ? cartData.fees.length : null
                });

            } catch (_e) {

                // Log failure of fallback fetch and abort gracefully.
                debugLog('Fee mirror: cart endpoint fallback failed', {
                    reason: reason || 'unspecified'
                });

                return;
            }
        }

        // Applies the fee row from the cart data.
        applyFeeRowFromCartData(cartData, reason);
    }

    /**
     * Resolves current cart total in cents via canonical fallback chain.
     *
     * @returns {number}
     */
    function getCurrentCartTotalCents() {
        // 1) Canonical Blocks store total.
        const totals = getStoreCartTotals();
        const storeTotal = parseInt(totals?.total_price || '0', 10);
        if (Number.isFinite(storeTotal) && storeTotal > 0) {
            return storeTotal;
        }

        // 2) DOM fallback (use first value only to avoid concatenation).
        const node = document.querySelector('.wc-block-components-totals-item__value');
        const domCents = parsePriceToCents(node?.textContent || '');
        if (domCents > 0) {
            return domCents;
        }

        // 3) Localized backend fallback.
        return Math.round(Number(settings?.cart_total || 0) * 100);
    }

    /**
     * Invalidates Blocks store selectors to force totals recalculation.
     *
     * @returns {void}
     */
    function triggerBlocksCartRefresh() {

        try {

            // Ensure WordPress data API is available before proceeding.
            if (!(window.wp && window.wp.data && window.wp.data.dispatch)) {
                return;
            }

            // Get dispatch handlers for cart and checkout stores.
            const dispatchCart = window.wp.data.dispatch('wc/store/cart');
            const dispatchCheckout = window.wp.data.dispatch('wc/store/checkout');

            // Invalidate cart-related selectors to force cart data + totals refresh.
            if (dispatchCart && typeof dispatchCart.invalidateResolutionForStoreSelector === 'function') {
                dispatchCart.invalidateResolutionForStoreSelector('getCartData');
                dispatchCart.invalidateResolutionForStoreSelector('getCartTotals');
            }

            // Invalidate checkout selector to refresh checkout status state.
            if (dispatchCheckout && typeof dispatchCheckout.invalidateResolutionForStoreSelector === 'function') {
                dispatchCheckout.invalidateResolutionForStoreSelector('getCheckoutStatus');
            }

        } catch (_e) {

            // Silently ignore errors to avoid breaking checkout if API changes.
            debugLog('Blocks cart refresh: error', {
                error: _e
            });
        }
    }

    /**
     * Re-publishes checkout notice as dismissible after Woo Blocks payment notice write.
     *
     * @param {string} message
     * @returns {void}
     */
    function scheduleDismissibleCheckoutNotice(message) {
        if (!message || typeof message !== 'string') {
            return;
        }
        if (!(window.wp && window.wp.data && window.wp.data.dispatch)) {
            return;
        }
        const dispatchNotices = window.wp.data.dispatch('core/notices');
        if (!dispatchNotices || typeof dispatchNotices.createErrorNotice !== 'function') {
            return;
        }
        const publishDismissible = function () {
            try {
                if (typeof dispatchNotices.removeNotice === 'function') {
                    dispatchNotices.removeNotice('wc-payment-error', 'wc/checkout');
                }
                dispatchNotices.createErrorNotice(message, {
                    id: 'wc-payment-error',
                    context: 'wc/checkout',
                    isDismissible: true
                });
            } catch (_noticeError) {
                debugLog('Failed to publish dismissible checkout notice', {
                    message: String(_noticeError?.message || _noticeError)
                });
            }
        };
        setTimeout(publishDismissible, 0);
        setTimeout(publishDismissible, 60);
    }

    /**
     * Syncs the currently selected payment gateway to the server so fee calculation
     * uses the correct gateway on the next cart/totals request (fixes fee showing for
     * ConvesioPay when COD or another gateway is selected in Blocks).
     *
     * @param {string} gatewayId - WooCommerce payment gateway id (e.g. 'cod', 'peachpay_convesiopay_unified').
     * @returns {Promise<{success:boolean,cart?:Object}>}
     */
    function syncChosenPaymentMethodToServer(gatewayId) {
        if (!gatewayId || typeof gatewayId !== 'string') {
            return Promise.resolve({ success: false });
        }
        const url = settings?.ajax_url || '/wp-admin/admin-ajax.php';
        const formData = new FormData();
        formData.append('action', CONFIG.blocksSetChosenPaymentMethodAction);
        formData.append('payment_method', gatewayId);
        return fetch(url, {
            method: 'POST',
            credentials: 'same-origin',
            body: formData
        })
            .then(function (res) { return res.json(); })
            .then(function (data) {
                if (data && data.success) {
                    debugLog('Blocks: synced chosen payment method to server', { payment_method: gatewayId });
                    const backendCart = data?.data?.cart;
                    if (backendCart && typeof backendCart === 'object') {
                        state.latestBackendCartData = backendCart;
                    }
                    triggerBlocksCartRefresh();
                    return { success: true, cart: backendCart || null };
                }
                return { success: false };
            })
            .catch(function (err) {
                debugLog('Blocks: sync chosen payment method failed', { error: String(err && err.message || err) });
                return { success: false };
            });
    }

    /**
     * Enables/disables the Blocks place-order button lock.
     *
     * @param {boolean} locked
     * @returns {void}
     */
    function setPlaceOrderInteractionLock(locked) {
        // Select the Woo Blocks place order button
        const button = document.querySelector('.wc-block-components-checkout-place-order-button');
        if (!button) {
            return;
        }

        // If locking is requested, disable button and mark it as controlled by ConvesioPay
        if (locked) {
            button.dataset.cpayBlocksDisabled = '1';
            button.disabled = true;
            button.setAttribute('aria-disabled', 'true');
            return;
        }

        // Only unlock if this script previously locked it.
        if (button.dataset.cpayBlocksDisabled === '1') {
            button.disabled = false;
            button.removeAttribute('aria-disabled');
            delete button.dataset.cpayBlocksDisabled;
        }
    }

    /**
     * Manages submit lock with failsafe unlock timer.
     *
     * @param {boolean} locked
     * @param {string=} reason
     * @returns {void}
     */
    function setSubmitInteractionLock(locked, reason) {

        // If locking is requested, enable submit lock and disable button interaction.
        if (locked) {
            state.submitInteractionLocked = true;
            setPlaceOrderInteractionLock(true);

            // Clear any existing failsafe timer.
            if (state.timers.submitLockFailsafe) {
                clearTimeout(state.timers.submitLockFailsafe);
            }
            state.timers.submitLockFailsafe = setTimeout(function () {
                // If lock was already cleared manually, do nothing
                if (!state.submitInteractionLocked) {
                    return;
                }
                debugLog('Submit lock failsafe unlock fired', {
                    reason: reason || 'unspecified'
                });
                // Reset lock state.
                state.submitInteractionLocked = false;

                // Only unlock button if no other blocking processes are active.
                if (!state.methodSwitchInProgress && !state.refreshingSessions && !state.storeTotalsResolving) {
                    setPlaceOrderInteractionLock(false);
                }
                state.timers.submitLockFailsafe = null;
            }, 15000);
            return;
        }

        // If unlocking is requested, clear submit lock state.
        state.submitInteractionLocked = false;

        // Clear any active failsafe timer.
        if (state.timers.submitLockFailsafe) {
            clearTimeout(state.timers.submitLockFailsafe);
            state.timers.submitLockFailsafe = null;
        }

        // Unlock button only if no other blocking operations are running.
        if (!state.methodSwitchInProgress && !state.refreshingSessions && !state.storeTotalsResolving) {
            setPlaceOrderInteractionLock(false);
        }
    }

    /**
     * Resolves an order identifier for session/intents.
     *
     * @returns {string}
     */
    function getOrderNumber() {
        try {
            // Attempt to retrieve order ID from Woo Blocks checkout store.
            if (window.wp && window.wp.data && window.wp.data.select) {
                const checkoutStore = window.wp.data.select('wc/store/checkout');
                const orderId = checkoutStore?.getOrderId ? checkoutStore.getOrderId() : null;
                if (orderId) {
                    return String(orderId);
                }
            }
        } catch (_e) {
            // Fall through to URL/session fallback.
            debugLog('Order number: error', {
                error: _e
            });
        }
        const urlParams = new URLSearchParams(window.location.search);
        const orderId = urlParams.get('order');
        if (orderId) {
            return orderId;
        }
        return 'SESSION-' + Date.now() + '-' + Math.floor(Math.random() * 10000);
    }

    /**
     * Builds normalized order/session payload context.
     *
     * @returns {Object}
     */
    function getOrderData() {

        // Retrieve current cart totals from store.
        const totals = getStoreCartTotals();

        // Resolve cart amount in cents.
        const amount = getCurrentCartTotalCents();

        // Determine currency with safe fallback chain.
        const currency = totals?.currency_code || settings?.currency || 'USD';

        // Capture email input field from checkout form.
        const emailNode = document.querySelector('input[type="email"]');

        // Retrieve billing first and last name values.
        const firstName = document.querySelector('#billing-first_name, #billing_first_name')?.value || '';
        const lastName = document.querySelector('#billing-last_name, #billing_last_name')?.value || '';

        // Return normalized order/session payload object.
        return {
            isValid: amount > 0, // Valid only if cart amount is positive.
            orderNumber: getOrderNumber(), // Resolve order/session identifier.
            amount: amount, // Total amount in cents.
            currency: currency, // Currency code.
            email: String(emailNode?.value || '').trim() || 'customer@example.com', // Sanitized email fallback.
            name: String((firstName + ' ' + lastName).trim() || 'Customer') // Combined full name fallback.
        };
    }

    /**
     * Builds a cart signature used for freshness checks.
     *
     * @returns {string}
     */
    function getCurrentCartSignature() {

        // Retrieve cart totals with safe fallback.
        const totals = getStoreCartTotals() || {};

        // Resolve current cart total in cents.
        const amount = getCurrentCartTotalCents();

        // Determine currency with fallback chain.
        const currency = totals?.currency_code || settings?.currency || 'USD';

        // Extract shipping total for signature consistency.
        const shipping = String(totals?.total_shipping || '');

        // Extract tax total for signature consistency.
        const tax = String(totals?.total_tax || '');

        // Extract fee totals (support multiple field shapes).
        const fees = String(totals?.total_fees || totals?.fee_total || '');

        // Extract discount total for signature consistency.
        const discount = String(totals?.total_discount || '');

        // Build deterministic signature string for freshness validation.
        return [amount, currency, shipping, tax, fees, discount].join('|');
    }

    /**
     * Builds a minimal amount/currency signature.
     *
     * @param {Object} orderData
     * @returns {string}
     */
    function getAmountCurrencySignature(orderData) {
        const amount = parseInt(String(orderData?.amount || 0), 10);
        const currency = String(orderData?.currency || settings?.currency || 'USD').trim().toUpperCase();
        return [Number.isFinite(amount) ? amount : 0, currency || 'USD'].join('|');
    }

    /**
     * Applies runtime amount/currency updates through SDK when available.
     *
     * @param {string=} reason
     * @param {boolean=} force
     * @returns {Promise<boolean>}
     */
    async function applyRuntimeAmountUpdate(reason, force) {
        if (!state.component || typeof state.component.updateAmount !== 'function') {
            return false;
        }
        const orderData = getOrderData();
        if (!orderData.isValid) {
            return false;
        }
        const signature = getAmountCurrencySignature(orderData);

        // Skip update if signature unchanged and not forced.
        if (!force && signature && signature === state.lastKnownAmountCurrencySignature) {
            return true;
        }
        try {
            await state.component.updateAmount({
                amount: orderData.amount,
                currency: orderData.currency
            });
            state.lastKnownAmountCurrencySignature = signature;
            debugLog('Runtime amount update applied', {
                reason: reason || 'unspecified',
                amount: orderData.amount,
                currency: orderData.currency
            });
            return true;
        } catch (_objectSignatureError) {
            try {
                await state.component.updateAmount(orderData.amount, orderData.currency);
                state.lastKnownAmountCurrencySignature = signature;
                debugLog('Runtime amount update applied (fallback signature)', {
                    reason: reason || 'unspecified',
                    amount: orderData.amount,
                    currency: orderData.currency
                });
                return true;
            } catch (_fallbackSignatureError) {
                return false;
            }
        }
    }

    /**
     * Retries backend method sync when prior sync failed.
     *
     * @returns {Promise<boolean>}
     */
    function recoverMethodSyncIfNeeded() {
        // If previous method sync did not fail, no recovery needed
        if (!state.methodSyncFailed) {
            return Promise.resolve(true);
        }
        const method = normalizeMethod(state.currentMethod || state.lastSyncedMethod || getDefaultUnifiedMethod());
        if (!method) {
            return Promise.resolve(false);
        }
        traceCheckoutEvent('method-sync:retry', { method: method });

        // Retry syncing method to backend session.
        return syncMethodToSession(method).then(function (ok) {
            if (ok) {
                state.lastSyncedMethod = method;
                state.methodSyncFailed = false;
            }
            return ok;
        });
    }

    /**
     * Invalidates amount-bound session and, optionally, authorization state.
     *
     * @param {string=} reason
     * @param {{preserveAuthorization?: boolean}=} options
     * @returns {void}
     */
    function markAmountBoundSessionsStale(reason, options) {
        // Determine whether authorization data should be preserved.
        const preserveAuthorization = Boolean(options && options.preserveAuthorization);

        // Clear Apple Pay, Google Pay, and BTCPay session signatures.
        state.applePaySessionSignature = '';
        state.googlePaySessionSignature = '';
        state.btcSessionSignature = '';

        // Clear cached BTCPay session object.
        state.btcSession = null;

        if (!preserveAuthorization) {
            state.paymentConfirmedData = null;
            if (typeof window !== 'undefined') {
                if (Object.prototype.hasOwnProperty.call(window, 'convesiopayApplePayPaymentData')) {
                    window.convesiopayApplePayPaymentData = null;
                }
                if (Object.prototype.hasOwnProperty.call(window, 'convesiopayGooglePayPaymentData')) {
                    window.convesiopayGooglePayPaymentData = null;
                }
                if (Object.prototype.hasOwnProperty.call(window, 'convesiopayPaymentToken')) {
                    window.convesiopayPaymentToken = '';
                }
            }
        }
        traceCheckoutEvent('sessions:stale', {
            reason: reason || 'unspecified',
            preserveAuthorization: preserveAuthorization
        });
    }

    /**
     * Indicates whether Woo store totals are currently resolving.
     *
     * @returns {boolean}
     */
    function isStoreTotalsResolving() {

        try {

            // Ensure WordPress data selector API is available.
            if (!(window.wp && window.wp.data && window.wp.data.select)) {
                return false;
            }

            // Access core data store for resolution tracking.
            const coreData = window.wp.data.select('core/data');

            // Abort if core data store is unavailable.
            if (!coreData) {
                return false;
            }

            // Prefer isResolving() if available.
            if (typeof coreData.isResolving === 'function') {
                return Boolean(coreData.isResolving('wc/store/cart', 'getCartTotals', []));
            }

            // Fallback to hasFinishedResolution() if available.
            if (typeof coreData.hasFinishedResolution === 'function') {
                return !Boolean(coreData.hasFinishedResolution('wc/store/cart', 'getCartTotals', []));
            }

            return false;

        } catch (_e) {

            // Silently fail and assume not resolving.
            return false;
        }
    }

    /**
     * Async sleep helper for polling loops.
     *
     * @param {number} ms
     * @returns {Promise<void>}
     */
    function sleep(ms) {
        return new Promise(function (resolve) {
            setTimeout(resolve, ms);
        });
    }

    /**
     * Waits until store totals are stable or timeout budget is reached.
     *
     * @param {number=} maxWaitMs
     * @returns {Promise<{ready:boolean,signature:string}>}
     */
    async function waitForStableStoreTotals(maxWaitMs) {

        // Determine maximum wait budget (default 2500ms).
        const budget = Number.isFinite(maxWaitMs) ? maxWaitMs : 2500;

        // Capture start time for timeout calculation.
        const startedAt = Date.now();

        // Track previous cart signature for stability comparison.
        let previousSignature = '';

        // Count consecutive stable signature reads.
        let stableCount = 0;

        // Loop until totals stabilize or timeout budget is exceeded.
        while ((Date.now() - startedAt) < budget) {

            // Get current cart signature snapshot.
            const signature = getCurrentCartSignature();

            // Get current total amount in cents.
            const total = getCurrentCartTotalCents();

            // Check whether Woo store totals are still resolving.
            const resolving = isStoreTotalsResolving();

            // Only consider stability if not resolving and total is valid.
            if (!resolving && total > 0) {

                // If signature matches previous, increment stability counter.
                if (signature === previousSignature) {
                    stableCount += 1;
                } else {

                    // Reset stability tracking if signature changed.
                    previousSignature = signature;
                    stableCount = 1;
                }

                // Consider totals stable after two consecutive identical signatures.
                if (stableCount >= 2) {
                    state.currentCartSignature = signature;
                    return { ready: true, signature: signature };
                }

            } else {

                // Reset stability tracking if resolving or invalid total.
                previousSignature = signature;
                stableCount = 0;
            }

            // Wait briefly before next stability check.
            await sleep(80);
        }

        // Use latest signature as fallback after timeout.
        const fallbackSignature = getCurrentCartSignature();

        // Store fallback signature in state.
        state.currentCartSignature = fallbackSignature;

        // Return not-ready state with last known signature.
        return { ready: false, signature: fallbackSignature };
    }

    /**
     * Records explicit method intent from user-facing interactions.
     *
     * @param {string} method
     * @param {string=} source
     * @returns {void}
     */
    function recordUserMethodIntent(method, source) {
        const normalized = normalizeMethod(method);
        if (!normalized) {
            return;
        }
        state.lastUserIntentMethod = normalized;
        state.lastUserIntentAtMs = Date.now();
        debugLog('Recorded user method intent', {
            method: normalized,
            source: source || 'unspecified'
        });
    }

    /**
     * Returns true when same method was explicitly selected recently.
     *
     * @param {string} method
     * @param {number=} windowMs
     * @returns {boolean}
     */
    function hasRecentUserIntent(method, windowMs) {
        const normalized = normalizeMethod(method);
        if (!normalized || state.lastUserIntentMethod !== normalized) {
            return false;
        }
        const budget = Number.isFinite(windowMs) ? windowMs : 1800;
        return (Date.now() - state.lastUserIntentAtMs) <= budget;
    }

    /**
     * Returns true when SDK native card-change should be ignored in settle windows.
     *
     * @param {string} method
     * @returns {boolean}
     */
    function shouldIgnoreNativeCardChange(method) {
        const normalized = normalizeMethod(method);
        if (normalized !== 'card') {
            return false;
        }
        if (Date.now() > Number(state.suppressNativeCardChangeUntilMs || 0)) {
            return false;
        }
        if (hasRecentUserIntent('card', 2200)) {
            return false;
        }
        const previous = normalizeMethod(state.lastSyncedMethod || state.currentMethod || '');
        if (!previous || previous === 'card') {
            return false;
        }
        return true;
    }

    /**
     * Coalesces fee mirror updates to avoid race/flicker from parallel triggers.
     *
     * @param {string=} reason
     * @param {number=} delayMs
     * @returns {void}
     */
    function scheduleFeeMirrorUpdate(reason, delayMs) {
        const wait = Number.isFinite(delayMs) ? delayMs : 80;
        state.lastFeeMirrorReason = String(reason || 'unspecified');
        if (state.timers.feeMirrorCoalesce) {
            clearTimeout(state.timers.feeMirrorCoalesce);
        }
        state.timers.feeMirrorCoalesce = setTimeout(function () {
            state.timers.feeMirrorCoalesce = null;
            mirrorFeeRowFromStore(state.lastFeeMirrorReason || 'coalesced_update');
        }, wait);
    }

    /**
     * Awaits canonical cart settle before applying fee/method-sensitive updates.
     *
     * @param {string=} reason
     * @param {number=} maxWaitMs
     * @returns {Promise<{ready:boolean,signature:string}>}
     */
    async function canonicalSettle(reason, maxWaitMs) {
        const settled = await waitForStableStoreTotals(Number.isFinite(maxWaitMs) ? maxWaitMs : 3200);
        debugLog('Canonical settle result', {
            reason: reason || 'unspecified',
            ready: settled.ready,
            signature: settled.signature || null
        });
        return settled;
    }

    /**
     * Checks whether BTCPay is enabled in runtime config.
     *
     * @returns {boolean}
     */
    function isBtcpayEnabled() {
        const active = settings?.active_methods || [];
        return Array.isArray(active) && active.length > 0 && (active.includes('crypto') || active.includes('btcpay'));
    }

    /**
     * Checks whether Apple Pay is enabled in runtime config.
     *
     * @returns {boolean}
     */
    function isApplePayEnabled() {
        const active = settings?.active_methods || [];
        return Array.isArray(active) && active.length > 0 && (active.includes('applepay') || active.includes('applePay'));
    }

    /**
     * Checks whether Google Pay is enabled in runtime config.
     *
     * @returns {boolean}
     */
    function isGooglePayEnabled() {
        const active = settings?.active_methods || [];
        return Array.isArray(active) && active.length > 0 && (active.includes('googlepay') || active.includes('googlePay') || active.includes('google-pay'));
    }

    /**
     * Get native checkout disabled method flags for the ConvesioPay SDK.
     *
     * @returns {{cards: boolean, applePay: boolean, googlePay: boolean}}
     */
    function getDisabledPaymentMethodsConfig() {
        const disabled = settings?.disabled_payment_methods || {};
        return {
            cards: Boolean(disabled.cards),
            applePay: Boolean(typeof disabled.applePay !== 'undefined' ? disabled.applePay : disabled.applepay),
            googlePay: Boolean(typeof disabled.googlePay !== 'undefined' ? disabled.googlePay : disabled.googlepay)
        };
    }

    /**
     * Persists selected internal method to backend session.
     *
     * @param {string} method
     * @returns {Promise<boolean>}
     */
    function syncMethodToSession(method) {
        const normalized = String(method || '').toLowerCase();
        if (!normalized) {
            return Promise.resolve(false);
        }
        const selectedGatewayId = getSelectedGatewayId();
        const formData = new FormData();
        formData.append('action', CONFIG.sessionAction);
        formData.append('convesiopay_selected_method', normalized);
        if (selectedGatewayId) {
            formData.append('selected_gateway', selectedGatewayId);
        }
        state.suppressFetchBridge = true;
        return fetch(settings?.ajax_url || '/wp-admin/admin-ajax.php', {
            method: 'POST',
            credentials: 'same-origin',
            body: formData
        })
            .then((response) => response.json())
            .then((payload) => {
                const ok = !(payload && payload.success === false);
                state.methodSyncFailed = !ok;
                const backendCart = payload?.data?.cart;
                if (backendCart && typeof backendCart === 'object') {
                    state.latestBackendCartData = backendCart;
                }
                return ok;
            })
            .catch(() => {
                state.methodSyncFailed = true;
                return false;
            })
            .finally(function () {
                state.suppressFetchBridge = false;
            });
    }

    /**
     * Creates or reuses BTCPay session for current order context.
     *
     * @returns {Promise<Object>}
     */
    async function createBTCPaySession() {
        if (!isBtcpayEnabled()) {
            return { success: false, disabled: true, message: 'BTCPay disabled' };
        }
        if (state.btcSession) {
            return { success: true, session: state.btcSession };
        }
        const orderData = getOrderData();
        if (!orderData.isValid) {
            return { success: false, message: 'Invalid order data' };
        }
        try {
            const requestData = {
                action: 'peachpay_convesiopay_create_btcpay_session',
                nonce: settings?.nonce || '',
                integration: settings?.integration_name || '',
                returnUrl: window.location.href,
                orderNumber: orderData.orderNumber,
                amount: orderData.amount,
                currency: orderData.currency,
                email: orderData.email,
                name: orderData.name
            };
            const response = await fetch(settings?.ajax_url || '/wp-admin/admin-ajax.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: new URLSearchParams(requestData).toString()
            });
            const payload = await response.json();
            if (payload?.success && payload?.data?.session) {
                state.btcSession = payload.data.session;
                return { success: true, session: state.btcSession };
            }
            return {
                success: false,
                message: payload?.data?.message || payload?.message || 'Failed to create BTCPay session'
            };
        } catch (error) {
            return { success: false, message: String(error?.message || 'BTCPay session request failed') };
        }
    }

    /**
     * Validates BTCPay iframe message origins.
     *
     * @param {string} origin
     * @returns {boolean}
     */
    function isAllowedBtcMessageOrigin(origin) {
        const normalized = String(origin || '').toLowerCase();
        return normalized.includes('btcpay') || normalized.includes('convesiopay');
    }

    /**
     * Triggers Woo place-order button after BTCPay processing confirmation.
     *
     * @param {string=} source
     * @returns {void}
     */
    function attemptAutoPlaceOrderFromBTCPayProcessing(source) {
        if (
            state.submitInProgress ||
            state.methodSwitchInProgress ||
            state.refreshingSessions ||
            state.submitInteractionLocked ||
            state.storeTotalsResolving ||
            isStoreTotalsResolving()
        ) {
            return;
        }
        if (normalizeMethod(state.currentMethod || state.lastSyncedMethod) !== 'btcpay') {
            return;
        }
        const payload = state.paymentConfirmedData;
        if (!payload || normalizeMethod(payload.paymentMethod || '') !== 'btcpay') {
            return;
        }
        const placeOrderButton = document.querySelector('.wc-block-components-checkout-place-order-button');
        if (!placeOrderButton || typeof placeOrderButton.click !== 'function') {
            return;
        }
        traceCheckoutEvent('submit:auto_trigger_btcpay', { source: source || 'unspecified' });
        setTimeout(function () {
            if (
                state.submitInProgress ||
                state.methodSwitchInProgress ||
                state.refreshingSessions ||
                state.submitInteractionLocked ||
                state.storeTotalsResolving ||
                isStoreTotalsResolving()
            ) {
                return;
            }
            placeOrderButton.click();
        }, 120);
    }

    /**
     * Binds BTCPay postMessage listener once for iframe status updates.
     *
     * @returns {void}
     */
    function setupBTCPayMessageListener() {
        if (state.btcMessageListenerBound || !isBtcpayEnabled()) {
            return;
        }
        state.btcMessageListenerBound = true;
        window.addEventListener('message', function (event) {
            if (!isAllowedBtcMessageOrigin(event?.origin)) {
                return;
            }
            try {
                let messageData = event?.data;
                if (typeof messageData === 'string') {
                    try {
                        messageData = JSON.parse(messageData);
                    } catch (_parseError) {
                        return;
                    }
                }
                if (!messageData || typeof messageData !== 'object') {
                    return;
                }
                if (String(messageData.status || '').toLowerCase() !== 'processing') {
                    return;
                }

                const invoiceId = toSafeString(messageData.invoiceId || messageData.paymentId || '');
                const cartSignature = getCurrentCartSignature();
                state.currentMethod = 'btcpay';
                state.paymentConfirmedData = {
                    paymentId: invoiceId,
                    paymentMethod: 'btcpay',
                    status: 'Processing',
                    invoiceId: invoiceId,
                    token: toSafeString('btc-session-' + (state.btcSession || invoiceId)),
                    cartSignature: toSafeString(cartSignature || '')
                };
                attemptAutoPlaceOrderFromBTCPayProcessing('btcpay_message_processing');
            } catch (_error) {
                // Guard only; never break checkout due to listener parse/runtime issues.
            }
        });
    }

    /**
     * Converts any value to a safe string with fallback.
     *
     * @param {*} value
     * @param {string=} fallback
     * @returns {string}
     */
    function toSafeString(value, fallback) {
        if (typeof value === 'undefined' || value === null) {
            return String(fallback || '');
        }
        return String(value);
    }

    /**
     * Resolves internal payment method from SDK event payload.
     *
     * @param {Object} event
     * @returns {string}
     */
    function pickPaymentMethodFromEvent(event) {
        const candidates = [
            event?.paymentMethod,
            event?.payment_method,
            event?.method,
            event?.selectedMethod,
            event?.activeMethod,
            event?.brand,
            event?.type
        ];
        for (let i = 0; i < candidates.length; i += 1) {
            const resolved = normalizeMethod(candidates[i]);
            if (resolved) {
                return resolved;
            }
        }
        return normalizeMethod(state.currentMethod) || getDefaultUnifiedMethod();
    }

    /**
     * Normalizes method aliases to card/applepay/googlepay/btcpay.
     *
     * @param {string} method
     * @returns {string}
     */
    function normalizeMethod(method) {
        const normalized = String(method || '').toLowerCase();
        if (!normalized) {
            return '';
        }
        if (normalized === 'card') {
            return 'card';
        }
        if (normalized === 'applepay' || normalized === 'apple_pay' || normalized === 'apple-pay') {
            return 'applepay';
        }
        if (normalized === 'googlepay' || normalized === 'google_pay' || normalized === 'google-pay') {
            return 'googlepay';
        }
        if (normalized === 'btcpay' || normalized === 'crypto' || normalized === 'btc-pay' || normalized === 'btc_pay') {
            return 'btcpay';
        }
        if (normalized.includes('apple')) {
            return 'applepay';
        }
        if (normalized.includes('google')) {
            return 'googlepay';
        }
        if (normalized.includes('btc') || normalized.includes('crypto') || normalized.includes('bitcoin')) {
            return 'btcpay';
        }
        if (normalized.includes('card') || normalized.includes('credit') || normalized.includes('debit')) {
            return 'card';
        }
        return '';
    }

    /**
     * Safely reads className from DOM nodes.
     *
     * @param {Element|null} element
     * @returns {string}
     */
    function getClassNameSafe(element) {
        if (!element) {
            return '';
        }
        const cn = element.className;
        if (typeof cn === 'string') {
            return cn;
        }
        if (cn && typeof cn.baseVal === 'string') {
            return cn.baseVal;
        }
        return '';
    }

    /**
     * Detects internal method from click target context.
     *
     * @param {Element|null} target
     * @returns {string|null}
     */
    function detectInternalMethodFromClickTarget(target) {
        if (!target || typeof target.closest !== 'function') {
            return null;
        }
        const headerSelectors = [
            '.accordion-header',
            '.adyen-checkout__payment-method__header'
        ];
        let header = null;
        for (let i = 0; i < headerSelectors.length; i += 1) {
            header = target.closest(headerSelectors[i]);
            if (header) {
                break;
            }
        }
        if (!header) {
            return null;
        }

        const headerText = String(header.textContent || '').toLowerCase();
        const headerClass = getClassNameSafe(header).toLowerCase();
        const accordionItem = header.closest('.accordion-item, .adyen-checkout__payment-method');
        const itemClass = getClassNameSafe(accordionItem).toLowerCase();
        const itemHtml = String(accordionItem?.outerHTML || '').toLowerCase().slice(0, 700);

        if (
            headerText.includes('apple') ||
            headerClass.includes('apple') ||
            itemClass.includes('apple') ||
            itemHtml.includes('applepay') ||
            itemHtml.includes('apple-pay')
        ) {
            return 'applepay';
        }
        if (
            headerText.includes('google') ||
            headerClass.includes('google') ||
            itemClass.includes('google') ||
            itemHtml.includes('googlepay') ||
            itemHtml.includes('google-pay')
        ) {
            return 'googlepay';
        }
        if (
            headerText.includes('crypto') ||
            headerText.includes('bitcoin') ||
            headerText.includes('btc') ||
            headerClass.includes('btc') ||
            headerClass.includes('crypto') ||
            itemClass.includes('btc') ||
            itemHtml.includes('btcpay') ||
            itemHtml.includes('crypto')
        ) {
            return 'btcpay';
        }
        if (
            (headerText.includes('card') || headerText.includes('credit') || headerText.includes('debit')) &&
            !headerText.includes('apple') &&
            !headerText.includes('google') &&
            !headerText.includes('crypto') &&
            !headerText.includes('bitcoin')
        ) {
            return 'card';
        }
        return null;
    }

    /**
     * Checks whether click target is a method selector header.
     *
     * @param {Element|null} target
     * @returns {boolean}
     */
    function isMethodSelectionHeaderClick(target) {
        if (!target || typeof target.closest !== 'function') {
            return false;
        }
        return Boolean(target.closest('.accordion-header, .adyen-checkout__payment-method__header'));
    }

    /**
     * Checks whether click target is a payment action button.
     *
     * @param {Element|null} target
     * @returns {boolean}
     */
    function isActionButtonClick(target) {
        if (!target || typeof target.closest !== 'function') {
            return false;
        }
        if (isMethodSelectionHeaderClick(target)) {
            return false;
        }
        return Boolean(
            target.closest(
                'button, [role="button"], iframe, .adyen-checkout__button, .adyen-checkout__applepay, .adyen-checkout__applepay__button, .apple-pay-button, .adyen-checkout__googlepay, .adyen-checkout__googlepay__button, .google-pay-button'
            )
        );
    }

    /**
     * Detects active internal method from expanded/selected node.
     *
     * @returns {string|null}
     */
    function detectInternalMethodFromActiveNode() {
        const activeNode = document.querySelector(
            '#convesiopay-unified-component-container .accordion-item.active, ' +
            '#convesiopay-unified-component-container .accordion-item[aria-expanded="true"], ' +
            '#convesiopay-unified-component-container .adyen-checkout__payment-method--selected, ' +
            '#convesiopay-unified-component-container .adyen-checkout__payment-method[aria-expanded="true"]'
        );
        if (!activeNode) {
            return null;
        }
        const className = getClassNameSafe(activeNode).toLowerCase();
        const html = String(activeNode.outerHTML || '').toLowerCase().slice(0, 700);
        const text = String(activeNode.textContent || '').toLowerCase();
        if (
            className.includes('apple') ||
            html.includes('applepay') ||
            html.includes('apple-pay') ||
            text.includes('apple')
        ) {
            return 'applepay';
        }
        if (
            className.includes('google') ||
            html.includes('googlepay') ||
            html.includes('google-pay') ||
            text.includes('google')
        ) {
            return 'googlepay';
        }
        if (
            className.includes('btc') ||
            className.includes('crypto') ||
            html.includes('btcpay') ||
            html.includes('crypto') ||
            text.includes('bitcoin') ||
            text.includes('btc') ||
            text.includes('crypto')
        ) {
            return 'btcpay';
        }
        if (
            (className.includes('card') || text.includes('card') || text.includes('credit') || text.includes('debit')) &&
            !text.includes('apple') &&
            !text.includes('google') &&
            !text.includes('crypto')
        ) {
            return 'card';
        }
        return null;
    }

    /**
     * Stops event propagation/default handling.
     *
     * @param {Event} event
     * @returns {void}
     */
    function stopEvent(event) {
        if (!event) {
            return;
        }
        event.preventDefault();
        event.stopPropagation();
        if (typeof event.stopImmediatePropagation === 'function') {
            event.stopImmediatePropagation();
        }
    }

    /**
     * Runs Apple Pay click preflight before replaying action click.
     *
     * @param {Element} actionTarget
     * @returns {Promise<boolean>}
     */
    async function runApplePayActionPreflight(actionTarget) {
        if (state.applePayActionPreflightInProgress) {
            return false;
        }
        if (state.storeTotalsResolving || isStoreTotalsResolving()) {
            traceCheckoutEvent('submit:blocked', { reason: 'applepay_action_preflight_store_resolving' });
            return false;
        }
        state.applePayActionPreflightInProgress = true;
        setMethodInteractionLock(true);
        try {
            const activeMethod = normalizeMethod(
                detectInternalMethodFromActiveNode() || state.currentMethod || state.lastSyncedMethod || ''
            );
            if (activeMethod !== 'applepay') {
                return false;
            }
            recordUserMethodIntent('applepay', 'applepay_action_preflight');
            state.currentMethod = 'applepay';
            syncMethodHiddenFields('applepay');

            // Ensure backend session is synced to Apple Pay
            const switched = await handleMethodSwitch('applepay', 'applepay_action_preflight_switch');
            if (!switched) {
                traceCheckoutEvent('submit:blocked', { reason: 'applepay_action_preflight_switch_failed' });
                return false;
            }

            // Wait for cart totals to stabilize before proceeding
            const settled = await canonicalSettle('applepay_action_preflight', 3200);
            if (!settled.ready) {
                traceCheckoutEvent('submit:blocked', { reason: 'applepay_action_preflight_totals_not_settled' });
                return false;
            }

            const submitSignature = settled.signature || getCurrentCartSignature();


            markAmountBoundSessionsStale('applepay_action_click_preflight');

            // Refresh Apple Pay session in-place
            await refreshSessionsInPlace('applepay_action_click_preflight');
            if (!state.applePaySessionSignature || state.applePaySessionSignature !== submitSignature) {
                traceCheckoutEvent('submit:blocked', { reason: 'applepay_action_preflight_session_not_fresh' });
                return false;
            }
            return true;
        } catch (_error) {
            traceCheckoutEvent('submit:blocked', { reason: 'applepay_action_preflight_exception' });
            return false;
        } finally {
            state.applePayActionPreflightInProgress = false;
            setMethodInteractionLock(false);
        }
    }

    /**
     * Binds container click monitoring for method/action detection.
     *
     * @returns {void}
     */
    function setupInternalMethodClickMonitoring() {
        const container = document.querySelector('#convesiopay-unified-component-container');
        if (!container) {
            return;
        }
        if (container.dataset.cpayBlocksMethodClickBound === '1') {
            return;
        }
        container.dataset.cpayBlocksMethodClickBound = '1';
        container.addEventListener('click', function (event) {
            if (isActionButtonClick(event.target)) {
                if (state.allowNextApplePayActionClick) {
                    state.allowNextApplePayActionClick = false;
                    return;
                }
                const activeMethod = normalizeMethod(
                    detectInternalMethodFromActiveNode() || state.currentMethod || state.lastSyncedMethod || ''
                );
                if (activeMethod === 'applepay') {
                    stopEvent(event);
                    if (state.applePayActionPreflightInProgress) {
                        return;
                    }
                    const clickedTarget = event.target;
                    runApplePayActionPreflight(clickedTarget).then(function (ok) {
                        if (!ok) {
                            return;
                        }
                        state.allowNextApplePayActionClick = true;
                        setTimeout(function () {
                            const fallbackTarget = document.querySelector(
                                '#convesiopay-unified-component-container .adyen-checkout__applepay__button, ' +
                                '#convesiopay-unified-component-container .apple-pay-button, ' +
                                '#convesiopay-unified-component-container .adyen-checkout__button, ' +
                                '#convesiopay-unified-component-container button'
                            );
                            const replayTarget = (clickedTarget && typeof clickedTarget.click === 'function' && document.contains(clickedTarget))
                                ? clickedTarget
                                : fallbackTarget;
                            if (replayTarget && typeof replayTarget.click === 'function') {
                                replayTarget.click();
                            } else {
                                state.allowNextApplePayActionClick = false;
                            }
                        }, 0);
                    });
                    return;
                }
                if (activeMethod === 'googlepay') {
                    lockGooglePayUiRefresh('googlepay_action_click');
                    return;
                }
                setTimeout(function () {
                    const delayedDetected = normalizeMethod(detectInternalMethodFromActiveNode());
                    if (!delayedDetected) {
                        return;
                    }
                    if (state.currentMethod === delayedDetected && state.lastSyncedMethod === delayedDetected) {
                        return;
                    }
                    debugLog('Detected method switch from delayed active node', {
                        detectedMethod: delayedDetected,
                        currentMethod: state.currentMethod,
                        lastSyncedMethod: state.lastSyncedMethod
                    });
                    recordUserMethodIntent(delayedDetected, 'ui_click_delayed_detect');
                    handleMethodSwitch(delayedDetected, 'ui_click_delayed_detect');
                }, 120);
                return;
            }
            const detected = detectInternalMethodFromClickTarget(event.target);
            if (!detected) {
                setTimeout(function () {
                    const delayedDetected = normalizeMethod(detectInternalMethodFromActiveNode());
                    if (!delayedDetected) {
                        return;
                    }
                    if (state.currentMethod === delayedDetected && state.lastSyncedMethod === delayedDetected) {
                        return;
                    }
                    debugLog('Detected method switch from delayed active node', {
                        detectedMethod: delayedDetected,
                        currentMethod: state.currentMethod,
                        lastSyncedMethod: state.lastSyncedMethod
                    });
                    recordUserMethodIntent(delayedDetected, 'ui_click_delayed_detect');
                    handleMethodSwitch(delayedDetected, 'ui_click_delayed_detect');
                }, 140);
                return;
            }
            const normalized = normalizeMethod(detected);
            if (!normalized) {
                return;
            }
            if (state.currentMethod === normalized && state.lastSyncedMethod === normalized) {
                return;
            }
            debugLog('Detected method switch from click', {
                detectedMethod: normalized,
                currentMethod: state.currentMethod,
                lastSyncedMethod: state.lastSyncedMethod
            });
            recordUserMethodIntent(normalized, 'ui_click_detect');
            handleMethodSwitch(normalized, 'ui_click_detect');
        }, true);
    }

    /**
     * Resolves default enabled internal method.
     *
     * @returns {string}
     */
    function getDefaultUnifiedMethod() {
        const configured = normalizeMethod(settings?.default_method || settings?.default_payment_method || '');
        if (configured === 'applepay' && isApplePayEnabled()) {
            return configured;
        }
        if (configured === 'googlepay' && isGooglePayEnabled()) {
            return configured;
        }
        if (configured === 'btcpay' && isBtcpayEnabled()) {
            return configured;
        }
        return 'card';
    }

    /**
     * Determines whether SDK event represents successful auth/state.
     *
     * @param {Object} event
     * @returns {boolean}
     */
    function hasSuccessfulEvent(event) {
        if (!event || typeof event !== 'object') {
            return false;
        }
        return (
            event.isSuccessful === true ||
            event.status === 'authorized' ||
            event?.paymentData?.status === 'authorized' ||
            Boolean(event.token) ||
            Boolean(event?.paymentData?.token)
        );
    }

    /**
     * Maps SDK event fields into submit payload structure.
     *
     * @param {Object} event
     * @returns {Object}
     */
    function mapPaymentDataFromEvent(event) {
        const paymentMethod = pickPaymentMethodFromEvent(event);
        const cartSignature = getCurrentCartSignature();
        const mapped = {
            paymentId: toSafeString(event?.paymentId || event?.paymentData?.paymentId || event?.id || ''),
            paymentMethod: paymentMethod,
            status: toSafeString(event?.status || event?.paymentData?.status || (hasSuccessfulEvent(event) ? 'authorized' : ''), ''),
            amount: toSafeString(event?.amount || event?.paymentData?.amount || ''),
            currency: toSafeString(event?.currency || event?.paymentData?.currency || ''),
            orderNumber: toSafeString(event?.orderNumber || event?.paymentData?.orderNumber || ''),
            token: toSafeString(event?.token || event?.paymentToken || event?.paymentData?.token || ''),
            invoiceId: toSafeString(event?.invoiceId || event?.paymentData?.invoiceId || ''),
            cartSignature: toSafeString(cartSignature || '')
        };
        return mapped;
    }

    /**
     * Refreshes amount-bound sessions/intents without remount.
     *
     * @param {string=} reason
     * @returns {Promise<void>}
     */
    async function refreshSessionsInPlace(reason) {
        if (state.refreshingSessions) {
            if (reason) {
                state.pendingSessionRefreshReason = reason;
            }
            return;
        }
        if (!state.component || !state.mounted) {
            debugLog('Skipping session refresh: component not mounted, scheduling retry', {
                reason: reason || 'unspecified',
                hasComponent: Boolean(state.component),
                mounted: Boolean(state.mounted)
            });
            ensureNativeComponentMounted('refresh_sessions_in_place_missing_component').then(function (result) {
                if (!result?.success) {
                    debugLog('Mount attempt failed before deferred session refresh', {
                        reason: reason || 'unspecified',
                        message: result?.message || null
                    });
                    return;
                }
                setTimeout(function () {
                    refreshSessionsInPlace(reason || 'deferred_after_mount');
                }, 0);
            });
            return;
        }
        const lockForRefresh = !state.methodSwitchInProgress;
        if (lockForRefresh) {
            setMethodInteractionLock(true);
        }
        state.refreshingSessions = true;
        try {
            // Always settle store totals first so session/intent refresh binds to canonical backend totals.
            const settled = await waitForStableStoreTotals(3200);
            const orderData = getOrderData();
            const cartSignature = settled.signature || getCurrentCartSignature();
            const runtimeAmountUpdated = await applyRuntimeAmountUpdate('refresh_sessions_in_place', false);
            debugLog('Refreshing Blocks sessions in place', {
                reason: reason || 'unspecified',
                amount: orderData.amount,
                currency: orderData.currency,
                method: state.currentMethod,
                totalsReady: settled.ready,
                cartSignature: cartSignature,
                runtimeAmountUpdated: runtimeAmountUpdated
            });
            if (!orderData.isValid) {
                debugLog('Skipping session refresh: invalid order data', {
                    reason: reason || 'unspecified',
                    amount: orderData.amount,
                    currency: orderData.currency
                });
                return;
            }

            if (isApplePayEnabled()) {
                // Apple Pay sessions are amount-bound; rebuild when runtime update is unavailable
                // or when the session is stale/unprimed for current cart signature.
                const shouldRebuildApplePaySession = (
                    !runtimeAmountUpdated ||
                    !state.applePaySessionPrimed ||
                    !state.applePaySessionSignature ||
                    state.applePaySessionSignature !== cartSignature
                );
                try {
                    if (shouldRebuildApplePaySession) {
                        debugLog('Creating Apple Pay session', {
                            amount: orderData.amount,
                            currency: orderData.currency,
                            email: orderData.email,
                            name: orderData.name
                        });
                        await state.component.createApplePaySession({
                            integration: settings?.integration_name || 'PeachPay',
                            returnUrl: window.location.href,
                            amount: orderData.amount,
                            currency: orderData.currency,
                            email: orderData.email,
                            name: orderData.name
                        });
                    }
                    debugLog('Apple Pay session refreshed', {
                        rebuilt: shouldRebuildApplePaySession,
                        signature: cartSignature
                    });
                    state.applePaySessionPrimed = true;
                    state.applePaySessionSignature = cartSignature;
                } catch (_appleError) {
                    // Keep checkout functional even if ApplePay session refresh fails.
                    debugLog('Apple Pay session refresh failed', {
                        error: _appleError
                    });
                    state.applePaySessionSignature = '';
                    state.applePaySessionPrimed = false;
                }
            } else {
                state.applePaySessionSignature = '';
                state.applePaySessionPrimed = false;
            }

            if (isGooglePayEnabled()) {
                const shouldRebuildGooglePaySession = (
                    !runtimeAmountUpdated ||
                    !state.googlePaySessionPrimed ||
                    !state.googlePaySessionSignature ||
                    state.googlePaySessionSignature !== cartSignature
                );
                try {
                    const googlePayUiLocked = isGooglePayUiRefreshLocked();
                    if (googlePayUiLocked) {
                        debugLog('Skipping Google Pay session refresh while UI lock is active', {
                            reason: reason || 'unspecified',
                            signature: cartSignature,
                            previousSignature: state.googlePaySessionSignature,
                            primed: state.googlePaySessionPrimed
                        });
                    } else if (shouldRebuildGooglePaySession) {
                        debugLog('Creating Google Pay session', {
                            amount: orderData.amount,
                            currency: orderData.currency,
                            orderNumber: orderData.orderNumber,
                            email: orderData.email,
                            name: orderData.name
                        });
                        await state.component.createGooglePaySession({
                            integration: settings?.integration_name || 'PeachPay',
                            returnUrl: window.location.href,
                            orderNumber: orderData.orderNumber,
                            amount: orderData.amount,
                            currency: orderData.currency,
                            email: orderData.email,
                            name: orderData.name
                        });
                    }
                    debugLog('Google Pay session refreshed', {
                        rebuilt: shouldRebuildGooglePaySession && !googlePayUiLocked,
                        signature: googlePayUiLocked ? state.googlePaySessionSignature : cartSignature
                    });
                    if (!googlePayUiLocked) {
                        state.googlePaySessionPrimed = true;
                        state.googlePaySessionSignature = cartSignature;
                    }
                } catch (_googleError) {
                    debugLog('Google Pay session refresh failed', {
                        error: _googleError
                    });
                    state.googlePaySessionSignature = '';
                    state.googlePaySessionPrimed = false;
                }
            } else {
                state.googlePaySessionSignature = '';
                state.googlePaySessionPrimed = false;
            }

            if (isBtcpayEnabled()) {
                // BTCPay intent/session is also amount-bound; refresh only when signature changed.
                const shouldRebuildBtcSession = !state.btcSession || !state.btcSessionSignature || state.btcSessionSignature !== cartSignature;
                try {
                    if (shouldRebuildBtcSession) {
                        state.btcSession = null;
                        state.btcSessionSignature = '';
                        const btc = await createBTCPaySession();
                        if (btc.success && btc.session) {
                            await state.component.createBTCPayIntent({ session: btc.session });
                            setupBTCPayMessageListener();
                            state.btcSessionSignature = cartSignature;
                        }
                    }
                } catch (_btcError) {
                    // Keep checkout functional even if BTCPay refresh fails.
                    state.btcSession = null;
                    state.btcSessionSignature = '';
                }
            } else {
                state.btcSession = null;
                state.btcSessionSignature = '';
            }

            // If cart changed while sessions were being refreshed, queue one more pass.
            const latestSignature = getCurrentCartSignature();
            if (latestSignature && latestSignature !== cartSignature) {
                state.pendingSessionRefreshReason = 'cart_changed_during_refresh';
                markAmountBoundSessionsStale('cart_changed_during_refresh');
            }
            scheduleFeeMirrorUpdate('refresh_sessions_in_place', 40);
        } finally {
            state.refreshingSessions = false;
            if (lockForRefresh && !state.methodSwitchInProgress && !state.submitInteractionLocked) {
                setMethodInteractionLock(false);
            }
            if (state.pendingSessionRefreshReason) {
                const next = state.pendingSessionRefreshReason;
                state.pendingSessionRefreshReason = '';
                setTimeout(function () {
                    refreshSessionsInPlace(next);
                }, 0);
            }
        }
    }

    /**
     * Inject the cpay spinner keyframe stylesheet once into the document head.
     */
    function ensureCpaySpinnerStyles() {
        if (document.getElementById('cpay-spin-style')) {
            return;
        }
        const s = document.createElement('style');
        s.id = 'cpay-spin-style';
        s.textContent = '@keyframes cpaySpin{to{transform:rotate(360deg)}}';
        document.head.appendChild(s);
    }

    /**
     * Applies interaction lock to component container and submit button.
     *
     * @param {boolean} locked
     * @returns {void}
     */
    function setMethodInteractionLock(locked) {
        if (locked) {
            setPlaceOrderInteractionLock(true);
        } else if (!state.submitInteractionLocked && !state.storeTotalsResolving) {
            setPlaceOrderInteractionLock(false);
        }
        const container = document.querySelector('#convesiopay-unified-component-container');
        if (!container) {
            return;
        }
        const lockId = 'convesiopay-unified-blocks-lock';
        let lockEl = document.getElementById(lockId);
        if (locked) {
            ensureCpaySpinnerStyles();
            if (!lockEl) {
                lockEl = document.createElement('div');
                lockEl.id = lockId;
                lockEl.style.position = 'absolute';
                lockEl.style.inset = '0';
                lockEl.style.zIndex = '20';
                lockEl.style.background = 'rgba(255,255,255,0.6)';
                lockEl.style.pointerEvents = 'all';
                lockEl.style.display = 'flex';
                lockEl.style.alignItems = 'center';
                lockEl.style.justifyContent = 'center';
                lockEl.style.borderRadius = '4px';
                lockEl.setAttribute('aria-hidden', 'true');
                lockEl.innerHTML = '<div style="width:24px;height:24px;border:3px solid #ddd;border-top-color:#555;border-radius:50%;animation:cpaySpin .65s linear infinite;"></div>';
            }
            const style = window.getComputedStyle(container);
            if (style.position === 'static') {
                container.dataset.cpayBlocksLockPositionAdjusted = '1';
                container.style.position = 'relative';
            }
            if (!container.contains(lockEl)) {
                container.appendChild(lockEl);
            }
            return;
        }
        if (lockEl && lockEl.parentNode) {
            lockEl.parentNode.removeChild(lockEl);
        }
        if (container.dataset.cpayBlocksLockPositionAdjusted === '1') {
            container.style.position = '';
            delete container.dataset.cpayBlocksLockPositionAdjusted;
        }
    }

    /**
     * Keeps hidden method fields synchronized with active method.
     *
     * @param {string} method
     * @returns {void}
     */
    function syncMethodHiddenFields(method) {
        const normalized = normalizeMethod(method);
        if (!normalized) {
            return;
        }
        const selectors = [
            'input[name="convesiopay_selected_method"]',
            '#convesiopay_selected_method',
            'input[name="extensions[peachpay_convesiopay_unified][selected_method]"]'
        ];
        for (let i = 0; i < selectors.length; i += 1) {
            const nodes = document.querySelectorAll(selectors[i]);
            if (!nodes || !nodes.length) {
                continue;
            }
            Array.prototype.forEach.call(nodes, function (node) {
                if (node && typeof node.value !== 'undefined') {
                    node.value = normalized;
                    node.setAttribute('value', normalized);
                }
            });
        }

        const checkoutForm = document.querySelector('.wc-block-checkout__form');
        if (checkoutForm && !checkoutForm.querySelector('input[name="convesiopay_selected_method"]')) {
            const hidden = document.createElement('input');
            hidden.type = 'hidden';
            hidden.name = 'convesiopay_selected_method';
            hidden.value = normalized;
            checkoutForm.appendChild(hidden);
        } else if (checkoutForm) {
            const hidden = checkoutForm.querySelector('input[name="convesiopay_selected_method"]');
            if (hidden) {
                hidden.value = normalized;
                hidden.setAttribute('value', normalized);
            }
        }
    }

    /**
     * Starts guarded method-switch transaction and fallback unlock timer.
     *
     * @param {string} method
     * @param {string=} source
     * @returns {void}
     */
    function beginMethodSwitch(method, source) {
        const normalized = String(method || '').toLowerCase();
        if (!normalized) {
            return;
        }
        state.currentMethod = normalized;
        syncMethodHiddenFields(normalized);
        state.methodSwitchInProgress = true;
        state.methodSwitchTarget = normalized;
        debugLog('Method switch started', { method: normalized, source: source || 'unspecified' });
        traceCheckoutEvent('switch:' + normalized, { source: source || 'unspecified' });
        setMethodInteractionLock(true);

        if (state.timers.methodSwitchUnlock) {
            clearTimeout(state.timers.methodSwitchUnlock);
        }
        state.timers.methodSwitchUnlock = setTimeout(function () {
            debugLog('Method switch fallback unlock fired', { target: state.methodSwitchTarget });
            state.methodSwitchInProgress = false;
            state.methodSwitchTarget = '';
            setMethodInteractionLock(false);
            state.timers.methodSwitchUnlock = null;
        }, 9000);
    }

    /**
     * Orchestrates method switch sync, refresh, and completion.
     *
     * @param {string} method
     * @param {string=} source
     * @returns {Promise<boolean>}
     */
    function handleMethodSwitch(method, source) {
        const normalized = normalizeMethod(method);
        if (!normalized) {
            return Promise.resolve(false);
        }
        if (state.paymentConfirmedData && normalizeMethod(state.paymentConfirmedData.paymentMethod) !== normalized) {
            state.paymentConfirmedData = null;
        }
        debugLog('Method switch handler invoked', {
            source: source || 'unknown',
            method: normalized,
            lastSyncedMethod: state.lastSyncedMethod
        });
        if (state.methodSwitchInProgress && state.methodSwitchTarget === normalized) {
            return Promise.resolve(true);
        }

        beginMethodSwitch(normalized, source || 'unknown');
        return syncMethodToSession(normalized)
            .then((success) => {
                if (!success) {
                    state.methodSwitchInProgress = false;
                    state.methodSwitchTarget = '';
                    state.methodSyncFailed = true;
                    setMethodInteractionLock(false);
                    return false;
                }
                state.lastSyncedMethod = normalized;
                state.methodSyncFailed = false;
                const backendFees = Array.isArray(state.latestBackendCartData?.fees) ? state.latestBackendCartData.fees : [];
                if (isUnifiedGatewaySelected() && backendFees.length > 0) {
                    // Mirror backend snapshot quickly so user sees fee change while store settles.
                    applyFeeRowFromCartData(state.latestBackendCartData, 'method_sync_backend_snapshot');
                }
                // Force store re-resolution before refreshing amount-bound sessions.
                triggerBlocksCartRefresh();
                return canonicalSettle('method_switch_' + normalized + '_pre_refresh', 3200)
                    .then(function () {
                        if (!state.component || !state.mounted) {
                            return ensureNativeComponentMounted('method_switch_mount_before_refresh').then(function (mountResult) {
                                if (!mountResult?.success) {
                                    throw new Error('mount_before_refresh_failed');
                                }
                                return refreshSessionsInPlace('method_switch_' + normalized);
                            });
                        }
                        return refreshSessionsInPlace('method_switch_' + normalized);
                    })
                    .then(function () {
                        scheduleFeeMirrorUpdate('method_switch_success', 40);
                        completeMethodSwitch('method_switch_refresh_complete');
                        return true;
                    })
                    .catch(function () {
                        completeMethodSwitch('method_switch_refresh_failed');
                        return false;
                    });
            })
            .catch(() => {
                state.methodSwitchInProgress = false;
                state.methodSwitchTarget = '';
                state.methodSyncFailed = true;
                setMethodInteractionLock(false);
                return false;
            });
    }

    /**
     * Safely reads body values from supported fetch payload types.
     *
     * @param {*} body
     * @param {string} key
     * @returns {string}
     */
    function tryReadBodyValue(body, key) {
        if (!body) {
            return '';
        }
        try {
            if (typeof FormData !== 'undefined' && body instanceof FormData) {
                const val = body.get(key);
                return val ? String(val) : '';
            }
            if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) {
                return String(body.get(key) || '');
            }
            if (typeof body === 'string') {
                const params = new URLSearchParams(body);
                return String(params.get(key) || '');
            }
        } catch (_e) {
            return '';
        }
        return '';
    }

    /**
     * Extracts selected method from fetch call arguments.
     *
     * @param {IArguments|Array} args
     * @returns {string}
     */
    function extractMethodFromFetchArgs(args) {
        try {
            const input = args[0];
            const init = args[1] || {};
            const requestBody = init.body || (input && typeof input === 'object' ? input.body : null);
            const action = tryReadBodyValue(requestBody, 'action');
            if (action !== CONFIG.sessionAction) {
                return '';
            }
            return tryReadBodyValue(requestBody, 'convesiopay_selected_method');
        } catch (_e) {
            return '';
        }
    }

    /**
     * Installs fetch bridge for legacy method-sync requests.
     *
     * @returns {void}
     */
    function setupLegacyMethodSwitchBridge() {
        if (state.fetchBridgeInstalled) {
            return;
        }
        if (typeof window.fetch !== 'function') {
            return;
        }
        state.fetchBridgeInstalled = true;
        const originalFetch = window.fetch.bind(window);
        window.fetch = function () {
            const args = arguments;
            if (state.suppressFetchBridge) {
                return originalFetch.apply(window, args);
            }
            const selectedMethod = String(extractMethodFromFetchArgs(args) || '').toLowerCase();
            if (!selectedMethod) {
                return originalFetch.apply(window, args);
            }

            if (state.methodSwitchInProgress && state.methodSwitchTarget === selectedMethod) {
                return originalFetch.apply(window, args);
            }
            const now = Date.now();
            if (state.lastBridgeMethod === selectedMethod && (now - state.lastBridgeAtMs) < 350) {
                return originalFetch.apply(window, args);
            }
            state.lastBridgeMethod = selectedMethod;
            state.lastBridgeAtMs = now;

            beginMethodSwitch(selectedMethod, 'legacy_ajax_bridge');
            return originalFetch.apply(window, args)
                .then(function (response) {
                    state.lastSyncedMethod = selectedMethod;
                    triggerBlocksCartRefresh();
                    return refreshSessionsInPlace('legacy_ajax_bridge_' + selectedMethod)
                        .then(function () {
                            completeMethodSwitch('legacy_ajax_bridge_complete');
                            return response;
                        })
                        .catch(function () {
                            completeMethodSwitch('legacy_ajax_bridge_refresh_failed');
                            return response;
                        });
                })
                .catch(function (error) {
                    completeMethodSwitch('legacy_ajax_bridge_fetch_failed');
                    throw error;
                });
        };
    }

    /**
     * Primes initial method/session state once after mount.
     *
     * @returns {Promise<boolean>}
     */
    function ensureInitialSessionsPrimed() {
        if (state.sessionsPrimed) {
            return Promise.resolve(true);
        }
        if (state.initialPrimingPromise) {
            return state.initialPrimingPromise;
        }
        const method = normalizeMethod(state.currentMethod || state.lastSyncedMethod || getDefaultUnifiedMethod());
        traceCheckoutEvent('prime:start', { method: method });
        // Reuse one in-flight priming promise to avoid duplicate initial switch/refresh races.
        state.initialPrimingPromise = handleMethodSwitch(method, 'initial_mount_prime')
            .then(function (success) {
                state.sessionsPrimed = Boolean(success);
                if (success) {
                    debugLog('Initial sessions primed', { method: method });
                    traceCheckoutEvent('prime:ready', { method: method });
                }
                return Boolean(success);
            })
            .catch(function () {
                state.sessionsPrimed = false;
                traceCheckoutEvent('prime:failed', { method: method });
                return false;
            })
            .finally(function () {
                state.initialPrimingPromise = null;
            });
        return state.initialPrimingPromise;
    }

    /**
     * Blocks submit interactions while checkout is not submission-ready.
     *
     * @param {Event} event
     * @returns {void}
     */
    function blockSubmitIfSwitching(event) {
        if (isSubmissionAllowed()) {
            return;
        }
        traceCheckoutEvent('submit:blocked', {
            reason: 'submit_guard_intercept',
            methodSwitchInProgress: state.methodSwitchInProgress,
            refreshingSessions: state.refreshingSessions,
            methodSyncFailed: state.methodSyncFailed,
            submitInProgress: state.submitInProgress,
            currentMethod: state.currentMethod,
            lastSyncedMethod: state.lastSyncedMethod
        });
        event.preventDefault();
        event.stopPropagation();
        if (typeof event.stopImmediatePropagation === 'function') {
            event.stopImmediatePropagation();
        }
    }

    /**
     * Registers capture-phase submit/click guard handlers.
     *
     * @returns {void}
     */
    function setupSubmissionGuards() {
        document.addEventListener('submit', function (event) {
            const form = event.target;
            if (!form || !(form.matches && form.matches('.wc-block-checkout__form'))) {
                return;
            }
            blockSubmitIfSwitching(event);
        }, true);

        document.addEventListener('click', function (event) {
            const target = event.target && event.target.closest
                ? event.target.closest('.wc-block-components-checkout-place-order-button')
                : null;
            if (!target) {
                return;
            }
            blockSubmitIfSwitching(event);
        }, true);
    }

    /**
     * Finalizes method switch when refresh queue is settled.
     *
     * @param {string=} reason
     * @returns {void}
     */
    function completeMethodSwitch(reason) {
        if (!state.methodSwitchInProgress) {
            return;
        }
        if (state.refreshingSessions || state.pendingSessionRefreshReason) {
            setTimeout(function () {
                completeMethodSwitch(reason);
            }, 100);
            return;
        }
        if (state.timers.methodSwitchUnlock) {
            clearTimeout(state.timers.methodSwitchUnlock);
            state.timers.methodSwitchUnlock = null;
        }
        debugLog('Method switch completed', {
            method: state.methodSwitchTarget || state.currentMethod,
            reason: reason || 'unspecified'
        });
        traceCheckoutEvent('switch:done', {
            method: state.methodSwitchTarget || state.currentMethod,
            reason: reason || 'unspecified'
        });
        state.methodSwitchInProgress = false;
        state.methodSwitchTarget = '';
        setMethodInteractionLock(false);
    }

    /**
     * Returns whether submit can proceed right now.
     *
     * @returns {boolean}
     */
    function isSubmissionAllowed() {
        return !state.methodSwitchInProgress &&
            !state.refreshingSessions &&
            !state.methodSyncFailed &&
            !state.submitInProgress &&
            !state.storeTotalsResolving;
    }

    /**
     * Unmounts and clears current mounted component state.
     *
     * @param {string=} reason
     * @returns {void}
     */
    function resetMountedComponentState(reason) {
        if (state.component && state.mounted) {
            try {
                if (typeof state.component.unmount === 'function') {
                    state.component.unmount();
                }
            } catch (_cleanupUnmountError) {
                // Keep cleanup resilient.
            }
        }
        state.component = null;
        state.mounted = false;
        state.sessionsPrimed = false;
        state.initialPrimingPromise = null;
        if (reason) {
            debugLog('Mounted component state reset', { reason: reason });
        }
    }

    /**
     * Ensures native component is mounted once when unified gateway is selected.
     *
     * @param {string=} reason
     * @returns {Promise<{success:boolean,component?:Object,message?:string,reused?:boolean}>}
     */
    function ensureNativeComponentMounted(reason) {
        if (!isUnifiedGatewaySelected()) {
            return Promise.resolve({ success: false, message: 'Unified gateway not selected' });
        }
        const containerNode = document.querySelector('#convesiopay-unified-component-container');
        const existingMountNode = document.getElementById(CONFIG.persistentMountNodeId);
        if (state.component && state.mounted && containerNode && existingMountNode && containerNode.contains(existingMountNode)) {
            return Promise.resolve({ success: true, component: state.component, reused: true });
        }
        return createAndMountNativeComponent(reason || 'ensure_mount');
    }

    /**
     * Subscribes to cart store changes and triggers session refreshes.
     *
     * @returns {void}
     */
    function setupCartStoreMonitoring() {
        if (!(window.wp && window.wp.data && window.wp.data.subscribe)) {
            return;
        }
        state.lastSelectedGatewayId = getSelectedGatewayId();
        state.lastKnownCartTotal = getCurrentCartTotalCents();
        state.currentCartSignature = getCurrentCartSignature();
        state.storeTotalsResolving = isStoreTotalsResolving();
        debugLog('Cart store monitoring initialized', {
            initialTotal: state.lastKnownCartTotal,
            initialSignature: state.currentCartSignature,
            storeTotalsResolving: state.storeTotalsResolving
        });
        if (state.storeTotalsResolving) {
            setMethodInteractionLock(true);
        }

        /**
         * Reactive Woo Blocks store subscription.
         *
         * This subscriber acts as the synchronization bridge between the
         * Woo Blocks data store and the ConvesioPay unified runtime.
         *
         * Responsibilities:
         *
         * 1) Gateway selection changes
         *    - Detects payment method switches.
         *    - Syncs chosen gateway to backend session (authoritative fee/totals calculation).
         *    - Guards against async race conditions using versioning.
         *    - Handles unified re-entry remount and cleanup on deselection.
         *
         * 2) Totals resolving state
         *    - Locks user interaction while cart totals are recalculating.
         *    - Unlocks only when no method switch, session refresh, or submit flow is active.
         *
         * 3) Cart signature changes (amount-bound integrity)
         *    - Detects cart total/signature updates.
         *    - Invalidates amount-bound sessions (Apple Pay / BTCPay).
         *    - Schedules fee mirror updates.
         *    - Triggers in-place session refresh to maintain amount correctness.
         *
         * Design principles:
         * - Backend/store totals are authoritative.
         * - Only one gateway sync transaction is honored at a time (version-guarded).
         * - Amount-bound sessions must never survive signature changes.
         * - Interaction is locked during unstable states to prevent race submissions.
         *
         * NOTE:
         * This subscriber fires on every store mutation. All logic must remain
         * diff-based and idempotent to avoid unnecessary session refreshes.
         */
        window.wp.data.subscribe(function () {
            const selectedGatewayIdRaw = getSelectedGatewayId();
            const selectedGatewayId = selectedGatewayIdRaw || state.lastSelectedGatewayId || '';
            const unifiedSelected = selectedGatewayId === CONFIG.gatewayId;
            if (selectedGatewayId !== state.lastSelectedGatewayId) {
                state.lastSelectedGatewayId = selectedGatewayId;
                debugLog('Blocks payment gateway selection changed', {
                    selectedGatewayId: selectedGatewayId || null,
                    unifiedSelected: unifiedSelected
                });
                // Sync chosen payment method to server so cart/totals (and fee calculation) use the correct gateway.
                const syncVersion = ++state.gatewaySelectionSyncVersion;
                if (selectedGatewayId) {

                    // Sync chosen payment method to server so cart/totals (and fee calculation) use the correct gateway.
                    syncChosenPaymentMethodToServer(selectedGatewayId).then(async function (syncResult) {
                        if (syncVersion !== state.gatewaySelectionSyncVersion) {
                            return;
                        }
                        if (!unifiedSelected) {
                            if (state.timers.feeMirrorRetry) {
                                clearTimeout(state.timers.feeMirrorRetry);
                                state.timers.feeMirrorRetry = null;
                            }
                            return;
                        }
                        if (syncResult?.cart && Array.isArray(syncResult.cart.fees)) {
                            applyFeeRowFromCartData(syncResult.cart, 'gateway_sync_backend_snapshot');
                        }
                        const settled = await waitForStableStoreTotals(3200);
                        if (syncVersion !== state.gatewaySelectionSyncVersion) {
                            return;
                        }
                        debugLog('Gateway sync settle status', {
                            gateway: selectedGatewayId,
                            settled: settled.ready,
                            signature: settled.signature || null
                        });
                        scheduleFeeMirrorUpdate('gateway_reselected_unified_after_sync', 60);
                    });
                }
                if (!unifiedSelected) {
                    if (state.timers.feeMirrorGrace) {
                        clearTimeout(state.timers.feeMirrorGrace);
                        state.timers.feeMirrorGrace = null;
                    }
                    if (state.timers.feeMirrorRetry) {
                        clearTimeout(state.timers.feeMirrorRetry);
                        state.timers.feeMirrorRetry = null;
                    }
                    if (state.timers.feeMirrorCoalesce) {
                        clearTimeout(state.timers.feeMirrorCoalesce);
                        state.timers.feeMirrorCoalesce = null;
                    }
                    resetMountedComponentState('gateway_deselected');
                    removeCustomFeeRows();
                } else {
                    state.suppressNativeCardChangeUntilMs = Date.now() + 1800;
                    setTimeout(function () {
                        ensureNativeComponentMounted('gateway_reselected_unified');
                    }, 50);
                }
            }

            const resolving = isStoreTotalsResolving();
            if (resolving !== state.storeTotalsResolving) {
                state.storeTotalsResolving = resolving;
                if (resolving) {
                    traceCheckoutEvent('totals:resolving', {});
                    setMethodInteractionLock(true);
                } else {
                    traceCheckoutEvent('totals:settled', {});
                    if (!state.methodSwitchInProgress && !state.refreshingSessions && !state.submitInteractionLocked) {
                        setMethodInteractionLock(false);
                    }
                }
            }

            if (!unifiedSelected) {
                return;
            }

            const current = getCurrentCartTotalCents();
            const signature = getCurrentCartSignature();
            if (!signature || signature === state.currentCartSignature) {
                return;
            }
            debugLog('Cart store change detected', {
                previousTotal: state.lastKnownCartTotal,
                nextTotal: current,
                previousSignature: state.currentCartSignature,
                nextSignature: signature
            });
            state.lastKnownCartTotal = current;
            state.currentCartSignature = signature;
            markAmountBoundSessionsStale('blocks_store_total_change');
            scheduleFeeMirrorUpdate('blocks_store_total_change', 30);
            refreshSessionsInPlace('blocks_store_total_change');
        });
    }

    /**
     * Creates and mounts ConvesioPay component into Blocks container.
     *
     * @param {string=} reason
     * @returns {Promise<{success:boolean,component?:Object,message?:string}>}
     */
    async function createAndMountNativeComponent(reason) {
        if (!isUnifiedGatewaySelected()) {
            return { success: false, message: 'Unified gateway not selected' };
        }
        if (state.mountPromise) {
            return state.mountPromise;
        }
        state.mountPromise = (async function () {
            try {
                traceCheckoutEvent('mount:start', { reason: reason || 'unspecified' });
                if (typeof window.ConvesioPay === 'undefined') {
                    return { success: false, message: 'ConvesioPay SDK not available' };
                }
                const containerNode = await waitForUnifiedMountContainer(2500);
                if (!containerNode) {
                    return { success: false, message: 'Mount container not found' };
                }
                const existingMountNode = document.getElementById(CONFIG.persistentMountNodeId);
                if (state.component && state.mounted && existingMountNode && containerNode.contains(existingMountNode)) {
                    return { success: true, component: state.component };
                }
                if (state.component || state.mounted) {
                    resetMountedComponentState('mount_recreate');
                }
                const mountNode = ensureMountNode(containerNode);
                if (!mountNode) {
                    return { success: false, message: 'Mount node creation failed' };
                }
                const config = settings?.convesiopay_config || {};
                const apiKey = config?.apiKey || '';
                if (!apiKey) {
                    return { success: false, message: 'Missing ConvesioPay api key' };
                }

                const cpay = window.ConvesioPay(apiKey);
                const orderData = getOrderData();
                const component = cpay.component({
                    environment: config?.api_url?.includes('qa') ? 'test' : 'live',
                    clientKey: config?.clientKey || '',
                    customerEmail: orderData.email || 'customer@example.com',
                    integration: settings?.integration_name || 'PeachPay',
                    theme: settings?.checkout_theme || 'light',
                    disabledPaymentMethods: getDisabledPaymentMethodsConfig()
                });

                component.on('change', function (event) {
                    const method = normalizeMethod(pickPaymentMethodFromEvent(event));
                    if (method) {
                        // SDK emits source-of-truth method transitions (card/applepay/btcpay).
                        state.currentMethod = method;
                    }

                    if (
                        state.paymentConfirmedData &&
                        method &&
                        normalizeMethod(state.paymentConfirmedData.paymentMethod) !== method &&
                        !hasSuccessfulEvent(event)
                    ) {
                        state.paymentConfirmedData = null;
                    }

                    if (method && method !== state.lastSyncedMethod) {
                        if (shouldIgnoreNativeCardChange(method)) {
                            debugLog('Ignoring native card change during settle window', {
                                method: method,
                                currentMethod: state.currentMethod,
                                lastSyncedMethod: state.lastSyncedMethod,
                                suppressUntil: state.suppressNativeCardChangeUntilMs
                            });
                        } else {
                            // Run full guarded switch pipeline on method transition.
                            handleMethodSwitch(method, 'native_component_change');
                        }
                    }

                    // Legacy parity fallback: some BTCPay flows emit "Processing" via component change event
                    // before/without a postMessage signal. Capture it and continue with guarded auto handoff.
                    const btcpayStatus = String(event?.paymentData?.status || event?.status || '').toLowerCase();
                    const isBtcpayProcessing = method === 'btcpay' && btcpayStatus === 'processing';
                    if (isBtcpayProcessing) {
                        const eventPaymentId = toSafeString(
                            event?.paymentData?.paymentId || event?.paymentId || event?.id || ''
                        );
                        const eventInvoiceId = toSafeString(
                            event?.paymentData?.invoiceId || event?.invoiceId || eventPaymentId
                        );
                        state.currentMethod = 'btcpay';
                        state.paymentConfirmedData = {
                            paymentId: eventPaymentId,
                            paymentMethod: 'btcpay',
                            status: 'Processing',
                            invoiceId: eventInvoiceId,
                            amount: toSafeString(event?.paymentData?.amount || event?.amount || ''),
                            currency: toSafeString(event?.paymentData?.currency || event?.currency || ''),
                            orderNumber: toSafeString(event?.paymentData?.orderNumber || event?.orderNumber || ''),
                            token: toSafeString('btc-session-' + (state.btcSession || eventInvoiceId || eventPaymentId)),
                            cartSignature: toSafeString(getCurrentCartSignature() || '')
                        };
                        attemptAutoPlaceOrderFromBTCPayProcessing('component_change_processing');
                        return;
                    }

                    if (hasSuccessfulEvent(event)) {
                        // Cache latest authorization payload; stale guards apply at submit time.
                        state.paymentConfirmedData = mapPaymentDataFromEvent(event);
                        const successfulMethod = normalizeMethod(state.paymentConfirmedData?.paymentMethod || method || '');
                        if (successfulMethod === 'applepay') {
                            attemptAutoPlaceOrderFromApplePaySuccess('component_change_success');
                        } else if (successfulMethod === 'googlepay') {
                            attemptAutoPlaceOrderFromGooglePaySuccess('component_change_success');
                        }
                    }
                });

                if (!isUnifiedGatewaySelected()) {
                    return { success: false, message: 'Unified gateway not selected' };
                }
                await component.mount('#' + CONFIG.persistentMountNodeId);
                state.component = component;
                state.mounted = true;
                if (!isUnifiedGatewaySelected()) {
                    resetMountedComponentState('mount_post_selection_changed');
                    return { success: false, message: 'Unified gateway not selected' };
                }
                state.suppressNativeCardChangeUntilMs = Date.now() + 1800;
                setupBTCPayMessageListener();
                setupInternalMethodClickMonitoring();
                traceCheckoutEvent('mount:ready', { method: state.currentMethod || getDefaultUnifiedMethod() });
                if (!state.currentMethod) {
                    state.currentMethod = getDefaultUnifiedMethod();
                }
                // Prime first session immediately so first submit/click does not race initialization.
                ensureInitialSessionsPrimed();
                return { success: true, component: component };
            } catch (error) {
                traceCheckoutEvent('mount:failed', { message: String(error?.message || 'Component mount failed') });
                return { success: false, message: String(error?.message || 'Component mount failed') };
            } finally {
                state.mountPromise = null;
            }
        })();
        return state.mountPromise;
    }

    /**
     * Builds payment method payload for Blocks submit response.
     *
     * @returns {Object}
     */
    function buildPaymentMethodData() {
        const paymentConfirmedData = state.paymentConfirmedData;
        const currentCartSignature = getCurrentCartSignature();
        const currentMethod = normalizeMethod(state.currentMethod || state.lastSyncedMethod || getDefaultUnifiedMethod()) || 'card';
        const confirmedMethod = normalizeMethod(paymentConfirmedData?.paymentMethod || '');
        const confirmedSignature = String(paymentConfirmedData?.cartSignature || '');
        const confirmedApplePaySessionFresh = Boolean(
            confirmedMethod === 'applepay' &&
            state.applePaySessionSignature &&
            currentCartSignature &&
            state.applePaySessionSignature === currentCartSignature
        );
        const confirmedGooglePaySessionFresh = Boolean(
            confirmedMethod === 'googlepay' &&
            state.googlePaySessionSignature &&
            currentCartSignature &&
            state.googlePaySessionSignature === currentCartSignature
        );
        const confirmedSignatureMatches =
            !confirmedSignature ||
            !currentCartSignature ||
            confirmedSignature === currentCartSignature ||
            confirmedApplePaySessionFresh ||
            confirmedGooglePaySessionFresh;
        const canUseConfirmedData = Boolean(
            paymentConfirmedData &&
            confirmedMethod &&
            confirmedMethod === currentMethod &&
            confirmedSignatureMatches
        );
        if (paymentConfirmedData && !canUseConfirmedData) {
            debugLog('Ignoring stale paymentConfirmedData for submit payload', {
                currentMethod: currentMethod,
                confirmedMethod: confirmedMethod || null,
                confirmedSignature: confirmedSignature || null,
                currentCartSignature: currentCartSignature || null
            });
        }
        const paymentMethodData = {
            peachpay_transaction_id: toSafeString('convesio-tx-' + Date.now()),
            payment_method_type: toSafeString(currentMethod),
            convesiopay_selected_method: toSafeString(currentMethod),
            selected_method: toSafeString(currentMethod)
        };

        if (canUseConfirmedData) {
            paymentMethodData.paymentid = toSafeString(paymentConfirmedData.paymentId || '');
            paymentMethodData.paymentmethod = toSafeString(paymentConfirmedData.paymentMethod || '');
            paymentMethodData.status = toSafeString(paymentConfirmedData.status || '');
            paymentMethodData.amount = toSafeString(paymentConfirmedData.amount || '0');
            paymentMethodData.currency = toSafeString(paymentConfirmedData.currency || '');
            paymentMethodData.ordernumber = toSafeString(paymentConfirmedData.orderNumber || '');
            paymentMethodData.paymentId = toSafeString(paymentConfirmedData.paymentId || '');
            paymentMethodData.paymentMethod = toSafeString(paymentConfirmedData.paymentMethod || '');

            if (paymentConfirmedData.paymentMethod === 'card') {
                paymentMethodData.convesiopay_payment_token = toSafeString(paymentConfirmedData.token || window.convesiopayPaymentToken || '');
            } else if (paymentConfirmedData.paymentMethod === 'applepay') {
                const appleToken = paymentConfirmedData.token || window.convesiopayPaymentToken || '';
                paymentMethodData.convesiopay_payment_token = toSafeString(appleToken);
                paymentMethodData.payment_method_type = 'applepay';
                paymentMethodData.convesiopay_payment_id = toSafeString(paymentConfirmedData.paymentId || '');
                paymentMethodData.applepay_payment_id = toSafeString(paymentConfirmedData.paymentId || '');
            } else if (paymentConfirmedData.paymentMethod === 'googlepay') {
                const googleToken = paymentConfirmedData.token || window.convesiopayPaymentToken || '';
                paymentMethodData.convesiopay_payment_token = toSafeString(googleToken);
                paymentMethodData.payment_method_type = 'googlepay';
                paymentMethodData.convesiopay_payment_id = toSafeString(paymentConfirmedData.paymentId || '');
                paymentMethodData.googlepay_payment_id = toSafeString(paymentConfirmedData.paymentId || '');
                paymentMethodData.googlepay_amount = toSafeString(paymentConfirmedData.amount || '0');
                paymentMethodData.googlepay_currency = toSafeString(paymentConfirmedData.currency || '');
            } else if (paymentConfirmedData.paymentMethod === 'btcpay') {
                paymentMethodData.convesiopay_payment_token = toSafeString('btc-session-' + (state.btcSession || ''));
                paymentMethodData.btcpay_session_id = toSafeString(state.btcSession || '');
                paymentMethodData.btcpay_payment_id = toSafeString(paymentConfirmedData.paymentId || '');
                paymentMethodData.btcpay_invoice_id = toSafeString(paymentConfirmedData.invoiceId || '');
                paymentMethodData.btcpay_status = toSafeString(paymentConfirmedData.status || '');
            }
            return paymentMethodData;
        }

        const hasFreshApplePaySessionForFallback = Boolean(
            state.applePaySessionSignature &&
            currentCartSignature &&
            state.applePaySessionSignature === currentCartSignature
        );
        const hasFreshGooglePaySessionForFallback = Boolean(
            state.googlePaySessionSignature &&
            currentCartSignature &&
            state.googlePaySessionSignature === currentCartSignature
        );

        if (
            currentMethod === 'applepay' &&
            hasFreshApplePaySessionForFallback &&
            window.convesiopayApplePayPaymentData &&
            window.convesiopayApplePayPaymentData.token
        ) {
            paymentMethodData.convesiopay_payment_token = toSafeString(window.convesiopayApplePayPaymentData.token);
            paymentMethodData.payment_method_type = 'applepay';
            paymentMethodData.paymentmethod = 'applepay';
            paymentMethodData.status = 'authorized';
            return paymentMethodData;
        }

        if (
            currentMethod === 'applepay' &&
            hasFreshApplePaySessionForFallback &&
            window.convesiopayPaymentToken
        ) {
            paymentMethodData.convesiopay_payment_token = toSafeString(window.convesiopayPaymentToken);
            paymentMethodData.payment_method_type = 'applepay';
            paymentMethodData.paymentmethod = 'applepay';
            paymentMethodData.status = 'authorized';
            return paymentMethodData;
        }

        if (
            currentMethod === 'googlepay' &&
            hasFreshGooglePaySessionForFallback &&
            window.convesiopayGooglePayPaymentData &&
            window.convesiopayGooglePayPaymentData.token
        ) {
            paymentMethodData.convesiopay_payment_token = toSafeString(window.convesiopayGooglePayPaymentData.token);
            paymentMethodData.payment_method_type = 'googlepay';
            paymentMethodData.paymentmethod = 'googlepay';
            paymentMethodData.status = 'authorized';
            paymentMethodData.googlepay_amount = toSafeString(window.convesiopayGooglePayPaymentData.amount || '0');
            paymentMethodData.googlepay_currency = toSafeString(window.convesiopayGooglePayPaymentData.currency || '');
            return paymentMethodData;
        }

        if (
            currentMethod === 'googlepay' &&
            hasFreshGooglePaySessionForFallback &&
            window.convesiopayPaymentToken
        ) {
            paymentMethodData.convesiopay_payment_token = toSafeString(window.convesiopayPaymentToken);
            paymentMethodData.payment_method_type = 'googlepay';
            paymentMethodData.paymentmethod = 'googlepay';
            paymentMethodData.status = 'authorized';
            return paymentMethodData;
        }

        if (currentMethod === 'btcpay') {
            paymentMethodData.paymentid = toSafeString('btc-session-' + (state.btcSession || ''));
            paymentMethodData.paymentmethod = 'btcpay';
            paymentMethodData.status = 'pending';
            paymentMethodData.convesiopay_payment_token = toSafeString('btc-session-' + (state.btcSession || ''));
            paymentMethodData.btcpay_session_id = toSafeString(state.btcSession || '');
            return paymentMethodData;
        }

        paymentMethodData.paymentmethod = 'card';
        return paymentMethodData;
    }

    /**
     * Executes strict submit preflight before returning Blocks SUCCESS.
     *
     * @param {Object} emitResponse
     * @returns {Promise<Object>}
     */
    async function runBlocksSubmitPreflightFlow(emitResponse) {
        traceCheckoutEvent('submit:attempt', { method: state.currentMethod || getDefaultUnifiedMethod() });

        if (state.methodSwitchInProgress || state.refreshingSessions || state.storeTotalsResolving || isStoreTotalsResolving()) {
            traceCheckoutEvent('submit:blocked', { reason: 'switch_or_refresh_or_sync_in_progress' });
            return {
                type: emitResponse.responseTypes.ERROR,
                message: 'Updating checkout totals. Please wait a moment and try again.'
            };
        }

        const syncRecovered = await recoverMethodSyncIfNeeded();
        if (!syncRecovered) {
            traceCheckoutEvent('submit:blocked', { reason: 'method_sync_not_recovered' });
            return {
                type: emitResponse.responseTypes.ERROR,
                message: 'Payment method sync is still updating. Please try again.'
            };
        }

        state.submitInProgress = true;
        setSubmitInteractionLock(true, 'submit_preflight');
        try {
            // Submit lifecycle:
            // 1) Resolve active method and backend-sync it if needed.
            // 2) Wait for stable totals.
            // 3) Apply runtime amount update.
            // 4) Verify method-specific amount-bound session freshness.
            // 5) Build guarded payload and return SUCCESS.
            if (!state.currentMethod) {
                state.currentMethod = normalizeMethod(state.lastSyncedMethod || getDefaultUnifiedMethod()) || 'card';
            }
            syncMethodHiddenFields(state.currentMethod);

            const activeMethodAtSubmit = normalizeMethod(detectInternalMethodFromActiveNode());
            if (activeMethodAtSubmit && activeMethodAtSubmit !== state.currentMethod) {
                debugLog('Submit preflight using active method from UI', {
                    from: state.currentMethod,
                    to: activeMethodAtSubmit
                });
                state.currentMethod = activeMethodAtSubmit;
                syncMethodHiddenFields(state.currentMethod);
            }

            if (state.currentMethod !== state.lastSyncedMethod) {
                const submitSwitchOk = await handleMethodSwitch(state.currentMethod, 'submit_preflight');
                if (!submitSwitchOk) {
                    traceCheckoutEvent('submit:blocked', {
                        reason: 'method_switch_preflight_failed',
                        currentMethod: state.currentMethod,
                        lastSyncedMethod: state.lastSyncedMethod
                    });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Payment method is still updating. Please try again.'
                    };
                }
            }

            if (state.currentMethod === 'applepay') {
                const hasApplePayAuthorization = Boolean(
                    state.paymentConfirmedData &&
                    normalizeMethod(state.paymentConfirmedData.paymentMethod || '') === 'applepay' &&
                    state.paymentConfirmedData.token
                );
                if (!hasApplePayAuthorization) {
                    scheduleDismissibleCheckoutNotice('Please complete Apple Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'applepay_authorization_missing' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Please complete Apple Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
            }

            if (state.currentMethod === 'googlepay') {
                const hasGooglePayAuthorization = Boolean(
                    state.paymentConfirmedData &&
                    normalizeMethod(state.paymentConfirmedData.paymentMethod || '') === 'googlepay' &&
                    state.paymentConfirmedData.token
                );
                if (!hasGooglePayAuthorization) {
                    scheduleDismissibleCheckoutNotice('Please complete Google Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'googlepay_authorization_missing' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Please complete Google Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
            }

            const settledAtSubmit = await waitForStableStoreTotals(3200);
            if (!settledAtSubmit.ready) {
                traceCheckoutEvent('submit:blocked', { reason: 'store_totals_not_settled' });
                return {
                    type: emitResponse.responseTypes.ERROR,
                    message: 'Checkout totals are still updating. Please try again.'
                };
            }

            if (state.currentMethod !== 'applepay' && state.currentMethod !== 'googlepay') {
                const hasRuntimeAmountUpdater = Boolean(state.component && typeof state.component.updateAmount === 'function');
                const runtimeAmountUpdated = await applyRuntimeAmountUpdate('submit_preflight', true);
                if (hasRuntimeAmountUpdater && !runtimeAmountUpdated) {
                    traceCheckoutEvent('submit:blocked', { reason: 'runtime_amount_update_failed' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Payment amount is still syncing. Please try again.'
                    };
                }
            }

            if (!state.sessionsPrimed) {
                if (state.currentMethod === 'applepay') {
                    scheduleDismissibleCheckoutNotice('Please complete Apple Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'applepay_session_not_primed' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Please complete Apple Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
                if (state.currentMethod === 'googlepay') {
                    scheduleDismissibleCheckoutNotice('Please complete Google Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'googlepay_session_not_primed' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Please complete Google Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
                const primed = await ensureInitialSessionsPrimed();
                if (!primed) {
                    traceCheckoutEvent('submit:blocked', { reason: 'priming_not_ready' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Payment session is still preparing. Please try again.'
                    };
                }
            }

            const submitOrderData = getOrderData();
            const signatureAmount = parseInt(String(settledAtSubmit.signature || '0').split('|')[0] || '0', 10);
            if (signatureAmount > 0 && submitOrderData.amount > 0 && signatureAmount !== submitOrderData.amount) {
                traceCheckoutEvent('submit:blocked', {
                    reason: 'signature_amount_mismatch',
                    signatureAmount: signatureAmount,
                    orderAmount: submitOrderData.amount
                });
                return {
                    type: emitResponse.responseTypes.ERROR,
                    message: 'Checkout amount changed. Please try again.'
                };
            }

            if (state.currentMethod === 'applepay') {
                const submitSignature = settledAtSubmit.signature || getCurrentCartSignature();
                const confirmedSignature = String(state.paymentConfirmedData?.cartSignature || '');
                if (confirmedSignature && submitSignature && confirmedSignature !== submitSignature) {
                    scheduleDismissibleCheckoutNotice('Checkout amount changed. Please complete Apple Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'applepay_authorization_signature_mismatch' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Checkout amount changed. Please complete Apple Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
                if (state.applePaySessionSignature && submitSignature && state.applePaySessionSignature !== submitSignature) {
                    scheduleDismissibleCheckoutNotice('Checkout amount changed. Please complete Apple Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'applepay_session_not_fresh' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Checkout amount changed. Please complete Apple Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
            }

            if (state.currentMethod === 'googlepay') {
                const submitSignature = settledAtSubmit.signature || getCurrentCartSignature();
                const confirmedSignature = String(state.paymentConfirmedData?.cartSignature || '');
                if (confirmedSignature && submitSignature && confirmedSignature !== submitSignature) {
                    scheduleDismissibleCheckoutNotice('Checkout amount changed. Please complete Google Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'googlepay_authorization_signature_mismatch' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Checkout amount changed. Please complete Google Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
                if (state.googlePaySessionSignature && submitSignature && state.googlePaySessionSignature !== submitSignature) {
                    scheduleDismissibleCheckoutNotice('Checkout amount changed. Please complete Google Pay authorization first.');
                    traceCheckoutEvent('submit:blocked', { reason: 'googlepay_session_not_fresh' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Checkout amount changed. Please complete Google Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
            }

            if (state.currentMethod === 'btcpay' && !state.btcSession) {
                const btc = await createBTCPaySession();
                if (btc.success && btc.session) {
                    state.btcSession = btc.session;
                    setupBTCPayMessageListener();
                } else {
                    traceCheckoutEvent('submit:blocked', { reason: 'btcpay_session_not_ready' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'BTCPay session not ready. Please try again.'
                    };
                }
            }

            if (state.currentMethod === 'btcpay') {
                const submitSignature = settledAtSubmit.signature || getCurrentCartSignature();
                if (!state.btcSessionSignature || state.btcSessionSignature !== submitSignature) {
                    markAmountBoundSessionsStale('submit_signature_mismatch_btcpay');
                    await refreshSessionsInPlace('submit_ensure_btcpay_fresh');
                }
                if (!state.btcSession || !state.btcSessionSignature || state.btcSessionSignature !== submitSignature) {
                    traceCheckoutEvent('submit:blocked', { reason: 'btcpay_session_not_fresh' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'BTCPay session is updating. Please try again.'
                    };
                }
            }

            if (state.currentMethod === 'card') {
                const existingCardToken = Boolean(
                    state.paymentConfirmedData &&
                    normalizeMethod(state.paymentConfirmedData.paymentMethod || '') === 'card' &&
                    state.paymentConfirmedData.token
                );
                if (!existingCardToken) {
                    traceCheckoutEvent('submit:card_token_fallback_started', {});
                    if (!state.component || typeof state.component.createToken !== 'function') {
                        traceCheckoutEvent('submit:blocked', { reason: 'card_tokenization_unavailable' });
                        return {
                            type: emitResponse.responseTypes.ERROR,
                            message: 'Card details are not ready. Please re-enter card details and try again.'
                        };
                    }

                    let token = '';
                    try {
                        const tokenResult = state.component.createToken();
                        token = (tokenResult && typeof tokenResult.then === 'function')
                            ? await tokenResult
                            : tokenResult;
                    } catch (_tokenError) {
                        token = '';
                    }

                    if (!token) {
                        traceCheckoutEvent('submit:blocked', { reason: 'card_token_missing' });
                        return {
                            type: emitResponse.responseTypes.ERROR,
                            message: 'Card token is missing. Please verify card details and try again.'
                        };
                    }

                    const orderDataForCardToken = getOrderData();
                    if (!orderDataForCardToken.isValid) {
                        traceCheckoutEvent('submit:blocked', { reason: 'card_token_invalid_order_data' });
                        return {
                            type: emitResponse.responseTypes.ERROR,
                            message: 'Checkout amount is invalid. Please review your order and try again.'
                        };
                    }

                    state.paymentConfirmedData = {
                        paymentId: toSafeString('card_' + Date.now()),
                        status: 'authorized',
                        paymentMethod: 'card',
                        amount: orderDataForCardToken.amount,
                        currency: orderDataForCardToken.currency,
                        orderNumber: orderDataForCardToken.orderNumber,
                        token: toSafeString(token),
                        cartSignature: toSafeString(getCurrentCartSignature() || '')
                    };
                    if (typeof window !== 'undefined') {
                        window.convesiopayPaymentToken = toSafeString(token);
                    }
                    traceCheckoutEvent('submit:card_token_fallback_success', {});
                }
            }

            const paymentMethodData = buildPaymentMethodData();
            // Final defensive check: if Apple Pay is active, payload must carry Apple Pay-ready data.
            const payloadMethod = normalizeMethod(
                paymentMethodData?.payment_method_type ||
                paymentMethodData?.paymentmethod ||
                paymentMethodData?.paymentMethod ||
                ''
            );
            if (state.currentMethod === 'applepay') {
                let hasToken = Boolean(paymentMethodData?.convesiopay_payment_token);
                let effectivePayloadMethod = payloadMethod;
                if (effectivePayloadMethod !== 'applepay' || !hasToken) {
                    scheduleDismissibleCheckoutNotice('Please complete Apple Pay authorization first.');
                    debugLog('Apple Pay payload not ready after submit preflight', {
                        payloadMethod: effectivePayloadMethod || null,
                        hasToken: hasToken,
                        hasPaymentConfirmedData: Boolean(state.paymentConfirmedData)
                    });
                    traceCheckoutEvent('submit:blocked', { reason: 'applepay_payload_not_ready' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Please complete Apple Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
            }
            if (state.currentMethod === 'googlepay') {
                let hasToken = Boolean(paymentMethodData?.convesiopay_payment_token);
                let effectivePayloadMethod = payloadMethod;
                if (effectivePayloadMethod !== 'googlepay' || !hasToken) {
                    scheduleDismissibleCheckoutNotice('Please complete Google Pay authorization first.');
                    debugLog('Google Pay payload not ready after submit preflight', {
                        payloadMethod: effectivePayloadMethod || null,
                        hasToken: hasToken,
                        hasPaymentConfirmedData: Boolean(state.paymentConfirmedData)
                    });
                    traceCheckoutEvent('submit:blocked', { reason: 'googlepay_payload_not_ready' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Please complete Google Pay authorization first.',
                        messageContext: emitResponse.noticeContexts?.CHECKOUT || 'wc/checkout'
                    };
                }
            }
            if (state.currentMethod === 'card') {
                const cardPayloadMethod = normalizeMethod(
                    paymentMethodData?.payment_method_type ||
                    paymentMethodData?.paymentmethod ||
                    paymentMethodData?.paymentMethod ||
                    ''
                );
                const hasCardToken = Boolean(paymentMethodData?.convesiopay_payment_token);
                if (cardPayloadMethod !== 'card' || !hasCardToken) {
                    debugLog('Card payload not ready after submit preflight', {
                        payloadMethod: cardPayloadMethod || null,
                        hasToken: hasCardToken,
                        hasPaymentConfirmedData: Boolean(state.paymentConfirmedData)
                    });
                    traceCheckoutEvent('submit:blocked', { reason: 'card_payload_not_ready' });
                    return {
                        type: emitResponse.responseTypes.ERROR,
                        message: 'Card authorization is not ready. Please verify card details and try again.'
                    };
                }
            }

            traceCheckoutEvent('submit:ok', {
                method: state.currentMethod || 'card',
                hasPaymentData: Boolean(state.paymentConfirmedData)
            });
            return {
                type: emitResponse.responseTypes.SUCCESS,
                meta: {
                    paymentMethodData: paymentMethodData
                }
            };
        } catch (error) {
            debugLog('Submit preflight failed', { error: error });
            traceCheckoutEvent('submit:failed', { message: String(error?.message || 'preflight_failed') });
            return {
                type: emitResponse.responseTypes.ERROR,
                message: 'Unable to complete payment preflight. Please try again.'
            };
        } finally {
            state.submitInProgress = false;
            state.applePayAutoSubmitInProgress = false;
            setSubmitInteractionLock(false, 'submit_preflight_complete');
        }
    }

    /**
     * Creates the Blocks content component and payment setup hooks.
     *
     * @returns {Function|null}
     */
    function createBlocksContentComponent() {
        if (!createElement || !useEffect) {
            return null;
        }
        return function ConvesioPayUnifiedContent(props) {
            const eventRegistration = props?.eventRegistration;
            const emitResponse = props?.emitResponse;

            useEffect(function () {
                let cancelled = false;
                setTimeout(function () {
                    if (cancelled) {
                        return;
                    }
                    if (!isUnifiedGatewaySelected()) {
                        return;
                    }
                    ensureNativeComponentMounted('content_mount_effect').then(function (result) {
                        if (!result.success) {
                            debugLog('Native mount failed; legacy fallback will continue', { message: result.message });
                        }
                    });
                }, 100);
                return function () {
                    cancelled = true;
                    resetMountedComponentState('content_unmount_cleanup');
                };
            }, []);

            useEffect(function () {
                if (!(eventRegistration && eventRegistration.onPaymentSetup && emitResponse && emitResponse.responseTypes)) {
                    return undefined;
                }
                const unsubscribe = eventRegistration.onPaymentSetup(async function () {
                    if (state.submitInProgress) {
                        traceCheckoutEvent('submit:blocked', { reason: 'submit_already_in_progress' });
                        return {
                            type: emitResponse.responseTypes.ERROR,
                            message: 'Payment is already being processed. Please wait.'
                        };
                    }
                    return runBlocksSubmitPreflightFlow(emitResponse);
                });
                return unsubscribe;
            }, [eventRegistration, emitResponse]);

            return createElement(
                'div',
                {
                    className: 'wc-convesiopay-unified-blocks-content',
                    style: { width: '100%', padding: '0', margin: '0' }
                },
                createElement('div', {
                    id: 'convesiopay-unified-component-container',
                    className: 'convesiopay-component-container',
                    style: {
                        border: '1px solid #e0e0e0',
                        borderRadius: '5px',
                        background: '#f8f8f8',
                        margin: '0',
                        minWidth: '100%',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        width: '100%'
                    }
                })
            );
        };
    }

    /**
 * Registers the unified gateway with Woo Blocks API.
 *
 * @returns {boolean}
 */
    function registerNativeBlocksPaymentMethod() {
        if (state.nativeBlocksRegistered || typeof registerPaymentMethod !== 'function' || !createElement) {
            return false;
        }

        const ContentComponent = createBlocksContentComponent();
        if (!ContentComponent) {
            return false;
        }

        try {
            const dynamicLabel = toSafeString(settings?.label || settings?.title || 'ConvesioPay');

            const shouldShowCardIcons = Boolean(
                settings?.show_card_icons &&
                Array.isArray(activeMethods) &&
                activeMethods.includes('card') &&
                settings?.card_icons &&
                typeof settings.card_icons === 'object'
            );

            const iconOrder = ['visa', 'mastercard', 'amex', 'discover'];
            const iconObjects = shouldShowCardIcons
                ? iconOrder
                    .map(function (key) {
                        const src = toSafeString(settings?.card_icons?.[key] || '');
                        if (!src) return null;
                        return {
                            id: CONFIG.gatewayId + '_' + key,
                            src: src,
                            alt: dynamicLabel
                        };
                    })
                    .filter(Boolean)
                : [];

            function ConvesioPayBlocksLabel(props) {
                const paymentComponents = props?.components || {};
                const PaymentMethodLabel = paymentComponents?.PaymentMethodLabel;
                const PaymentMethodIcons = paymentComponents?.PaymentMethodIcons;
                const Fragment = window.wp?.element?.Fragment;
                const hasIcons = iconObjects.length > 0;

                // Preferred Woo Blocks pattern (same as Stripe/Square blocks integrations).
                if (PaymentMethodLabel && PaymentMethodIcons && hasIcons) {
                    if (Fragment) {
                        return createElement(
                            Fragment,
                            null,
                            createElement(PaymentMethodLabel, { text: dynamicLabel }),
                            createElement(PaymentMethodIcons, {
                                className: 'pp-label-icon peachpay-convesiopay-unified-label-icon',
                                icons: iconObjects
                            })
                        );
                    }
                    // Fallback only when Fragment is unavailable.
                    return createElement(PaymentMethodLabel, { text: dynamicLabel });
                }

                if (PaymentMethodLabel) {
                    return createElement(PaymentMethodLabel, { text: dynamicLabel });
                }

                // Safe fallback if Blocks components contract is unavailable.
                return createElement('span', { className: 'wc-block-components-payment-method-label' }, dynamicLabel);
            }

            registerPaymentMethod({
                name: CONFIG.gatewayId,
                label: createElement(ConvesioPayBlocksLabel),
                content: createElement(ContentComponent),
                edit: createElement(ContentComponent),

                canMakePayment: function () {
                    return Array.isArray(activeMethods) && activeMethods.length > 0;
                },

                ariaLabel: dynamicLabel,

                supports: {
                    features: settings?.supports || ['products']
                },

                placeOrderButtonLabel: __('Complete Order', 'peachpay-for-woocommerce')
            });

            state.nativeBlocksRegistered = true;
            return true;
        } catch (error) {
            debugLog('Native Blocks registration failed', {
                message: String(error?.message || error)
            });
            return false;
        }
    }

    /**
     * Loads legacy Blocks implementation script as fallback.
     *
     * @returns {void}
     */
    function loadLegacyBlocksImplementation() {
        const currentScript = document.currentScript;
        if (!currentScript || !currentScript.src) {
            return;
        }
        const legacySrc = currentScript.src.replace(/-v\d+\.js(\?.*)?$/, '.js$1');
        if (!legacySrc || legacySrc === currentScript.src) {
            return;
        }
        if (document.querySelector('script[data-convesiopay-blocks-legacy-loader="1"]')) {
            return;
        }
        const script = document.createElement('script');
        script.src = legacySrc;
        script.defer = true;
        script.setAttribute('data-convesiopay-blocks-legacy-loader', '1');
        script.onload = function () {
            debugLog('Legacy Blocks implementation loaded');
        };
        script.onerror = function () {
            debugLog('Failed to load legacy Blocks implementation');
        };
        document.head.appendChild(script);
    }

    /**
     * Injects CSS adjustments for unified Blocks rendering.
     *
     * @returns {void}
     */
    function addBlockCheckoutStyles() {
        // Add CSS to ensure ConvesioPay component takes full width
        const styleId = 'convesiopay-full-width-styles';
        if (!document.getElementById(styleId)) {
            const style = document.createElement('style');
            style.id = styleId;
            style.textContent = `
                #convesiopay-unified-component-container #convesiopay-checkout {
                    width: 100% !important;
                }
                #convesiopay-unified-component-container.loading-in-progress {
                    min-width: 100% !important;
                    display: flex !important;
                    align-items: center !important;
                    justify-content: center !important;
                }
                #convesiopay-unified-component-container {
                    width: 100% !important;
                    min-width: 100% !important;
                }
                #convesiopay-unified-component-container .convesiopay-component,
                #convesiopay-unified-component-container iframe,
                #convesiopay-unified-component-container > div {
                    width: 100% !important;
                    max-width: 100% !important;
                    min-width: 100% !important;
                }
                /*#convesiopay-unified-component-container .adyen-checkout,
                #convesiopay-unified-component-container [class*="adyen-checkout"] {
                    min-width: 100% !important;
                }*/
                .convesiopay-component-container {
                    width: 100% !important;
                }
                /* Hide radio button when ConvesioPay is the only payment method */
                .wc-block-components-radio-control-accordion-option:only-child .wc-block-components-radio-control__option {
                    display: none !important;
                }
                /* Remove padding from accordion content */
                .wc-block-components-radio-control-accordion-content {
                    padding: 0 !important;
                }

                .wc-block-components-payment-method-icons.peachpay-convesiopay-unified-label-icon {
                    flex-wrap: nowrap !important;
                }
            `;
            document.head.appendChild(style);
        }
    }

    /**
     * Initializes Blocks runtime orchestration once.
     *
     * @returns {void}
     */
    function init() {
        if (state.initialized) {
            return;
        }
        state.initialized = true;

        // It takes a "snapshot" of the current cart (amount and currency). This acts as a baseline to detect if the user changes their order later.
        state.lastKnownAmountCurrencySignature = getAmountCurrencySignature(getOrderData());

        // This starts "watching" the WooCommerce cart. If an item is added or a coupon applied, this watcher will trigger updates.
        setupCartStoreMonitoring();

        // This sets up security checks that happen the moment a user clicks "Place Order". It prevents the order from going through if the payment information isn't ready or if the price just changed.
        setupSubmissionGuards();

        // The script tries to register itself with the modern WooCommerce Blocks system.
        const nativeRegistered = registerNativeBlocksPaymentMethod();
        if (!nativeRegistered) {
            // Connects older AJAX-based payment switching to this new logic.
            setupLegacyMethodSwitchBridge();

            // Loads an older version of the script to ensure the customer can still pay.
            loadLegacyBlocksImplementation();
        }
        debugLog('Blocks initialized', {
            initialTotal: state.lastKnownCartTotal
        });

        // Sync current payment gateway to server so first cart/totals use correct fee (e.g. no CPay fee when COD selected).
        setTimeout(function () {
            const gatewayId = getSelectedGatewayId();
            if (gatewayId) {
                syncChosenPaymentMethodToServer(gatewayId);
            }
        }, 300);

        setTimeout(function () {
            // Mirrors the fee row from the store to the checkout.
            mirrorFeeRowFromStore('blocks_init');
        }, 400);
    }

    // Expose read-only snapshot for phased migration debugging.
    window.__convesiopayUnifiedBlocksState = function () {
        return {
            initialized: state.initialized,
            mounted: state.mounted,
            component: Boolean(state.component),
            currentMethod: state.currentMethod,
            lastSyncedMethod: state.lastSyncedMethod,
            lastKnownCartTotal: state.lastKnownCartTotal,
            btcSession: state.btcSession,
            methodSwitchInProgress: state.methodSwitchInProgress,
            methodSwitchTarget: state.methodSwitchTarget,
            refreshingSessions: state.refreshingSessions,
            pendingSessionRefreshReason: state.pendingSessionRefreshReason,
            submitInProgress: state.submitInProgress,
            submitInteractionLocked: state.submitInteractionLocked,
            applePaySessionPrimed: state.applePaySessionPrimed,
            lastKnownAmountCurrencySignature: state.lastKnownAmountCurrencySignature
        };
    };

    // Expose helper hooks for next migration phases.
    window.__convesiopayUnifiedBlocksHooks = {
        getOrderData: getOrderData,
        getCurrentCartTotalCents: getCurrentCartTotalCents,
        triggerBlocksCartRefresh: triggerBlocksCartRefresh,
        syncMethodToSession: syncMethodToSession,
        refreshSessionsInPlace: refreshSessionsInPlace,
        beginMethodSwitch: beginMethodSwitch,
        handleMethodSwitch: handleMethodSwitch,
        completeMethodSwitch: completeMethodSwitch,
        isSubmissionAllowed: isSubmissionAllowed,
        setMountedComponent: function (component, method) {
            state.component = component || null;
            state.mounted = Boolean(component);
            if (method) {
                state.currentMethod = String(method).toLowerCase();
            }
        }
    };
    // Add CSS to ensure ConvesioPay component takes full width
    addBlockCheckoutStyles();

    // Entry point for the script.
    init();
})();
