(function (){
    /**
     * Define the version of the Google Pay API referenced when creating your
     * configuration
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
     */
    const baseRequest = {
        apiVersion: 2,
        apiVersionMinor: 0
    };

    /**
     * Card networks supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];

    /**
     * Card authentication methods supported by your site and your gateway
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     * supported card networks
     */
    const allowedCardAuthMethods = ["PAN_ONLY"];

    /**
     * Identify your gateway and your site's gateway merchant identifier
     *
     * The Google Pay API response will return an encrypted payment method capable
     * of being charged by a supported gateway after payer authorization
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
     */
    const tokenizationSpecification = {
        type: 'PAYMENT_GATEWAY',
        parameters: {
            'gateway': 'acceptblue',
            'gatewayMerchantId': pgabfwProGetMerchantID()
        }
    };

    /**
     * Describe your site's support for the CARD payment method and its required
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const baseCardPaymentMethod = {
        type: 'CARD',
        parameters: {
            allowedAuthMethods: allowedCardAuthMethods,
            allowedCardNetworks: allowedCardNetworks
        }
    };

    /**
     * Describe your site's support for the CARD payment method including optional
     * fields
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
     */
    const cardPaymentMethod = Object.assign(
        {},
        baseCardPaymentMethod,
        {
            tokenizationSpecification: tokenizationSpecification
        }
    );

    /**
     * An initialized google.payments.api.PaymentsClient object or null if not yet set
     *
     * @see {@link getGooglePaymentsClient}
     */
    let paymentsClient = null;

    /**
     * Configure your site's support for payment methods supported by the Google Pay
     * API.
     *
     * Each member of allowedPaymentMethods should contain only the required fields,
     * allowing reuse of this base request when determining a viewer's ability
     * to pay and later requesting a supported payment method
     *
     * @returns {object} Google Pay API version, payment methods supported by the site
     */
    function getGoogleIsReadyToPayRequest() {
        return Object.assign(
            {},
            baseRequest,
            {
                allowedPaymentMethods: [baseCardPaymentMethod]
            }
        );
    }

    /**
     * Configure support for the Google Pay API
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
     * @returns {object} PaymentDataRequest fields
     */
    function getGooglePaymentDataRequest() {
        const paymentDataRequest = Object.assign({}, baseRequest);
        paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
        let transactionInfo = pgabfwProGetGoogleTransactionInfo();
        let surchargeAmount = pgabfwProGetSurcharge();
        paymentDataRequest.transactionInfo = {
            ...transactionInfo,
            totalPrice: (parseFloat(transactionInfo.totalPrice) + parseFloat(surchargeAmount)).toFixed(2).toString()
        };
        paymentDataRequest.merchantInfo = {
            merchantId: pgabfwProGetMerchantID(),
            merchantName: pgabfwProGetMerchantName()
        };

        paymentDataRequest.callbackIntents = ["PAYMENT_AUTHORIZATION"];

        return paymentDataRequest;
    }

    /**
     * Return an active PaymentsClient or initialize
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
     * @returns {google.payments.api.PaymentsClient} Google Pay API client
     */
    function getGooglePaymentsClient() {
        if ( paymentsClient === null ) {
            paymentsClient = new google.payments.api.PaymentsClient({
                environment: pgabfwProGetMode(),
                paymentDataCallbacks: {
                    onPaymentAuthorized: onPaymentAuthorized
                }
            });
        }
        return paymentsClient;
    }

    /**
     * Handles authorize payments callback intents.
     *
     * @param {object} paymentData response from Google Pay API after a payer approves payment through user gesture.
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData object reference}
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentAuthorizationResult}
     * @returns Promise<{object}> Promise of PaymentAuthorizationResult object to acknowledge the payment authorization status.
     */
    function onPaymentAuthorized(paymentData) {
        return new Promise(function(resolve, reject){
            // handle the response
            processPayment(paymentData)
                .then(function() {
                    resolve({transactionState: 'SUCCESS'});
                })
                .catch(function() {
                    resolve({
                        transactionState: 'ERROR',
                        error: {
                            intent: 'PAYMENT_AUTHORIZATION',
                            message: 'Insufficient funds',
                            reason: 'PAYMENT_DATA_INVALID'
                        }
                    });
                });
        });
    }

    /**
     * Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
     *
     * Display a Google Pay payment button after confirmation of the viewer's
     * ability to pay.
     */
    window.pgabfwOnGooglePayLoaded =  function () {
        const paymentsClient = getGooglePaymentsClient();
        paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
            .then(function(response) {
                if (response.result) {
                    jQuery( 'body' ).on( 'updated_checkout', function () {
                        addGooglePayButton();
                    });
                }
            })
            .catch(function(err) {
                // show error in developer console for debugging
                console.log(err);
            });
    }

    /**
     * Add a Google Pay purchase button alongside an existing checkout button
     *
     * @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
     * @see {@link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
     */
    function addGooglePayButton() {
        const paymentsClient = getGooglePaymentsClient();
        const button = paymentsClient.createButton({
                onClick: onGooglePaymentButtonClicked,
                allowedPaymentMethods: [baseCardPaymentMethod]
            });
        const container = document.getElementById('pgabfw_google_pay_button_container');
        if(container) {
            container.innerHTML = '';
            container.appendChild(button);
        }
    }

    /**
     * Show Google Pay payment sheet when Google Pay payment button is clicked
     */
    function onGooglePaymentButtonClicked() {
        const paymentDataRequest = getGooglePaymentDataRequest();
        let transactionInfo = pgabfwProGetGoogleTransactionInfo();
        let surchargeAmount = pgabfwProGetSurcharge();
        paymentDataRequest.transactionInfo = {
            ...transactionInfo,
            totalPrice: (parseFloat(transactionInfo.totalPrice) + parseFloat(surchargeAmount)).toFixed(2).toString()
        };
        const paymentsClient = getGooglePaymentsClient();
        paymentsClient.loadPaymentData(paymentDataRequest);
    }

    /**
     * Process payment data returned by the Google Pay API
     *
     * @param {object} paymentData response from Google Pay API after user approves payment
     * @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
     */
    function processPayment(paymentData) {
        return new Promise(async function(resolve, reject) {
            const paymentToken = paymentData.paymentMethodData.tokenizationData.token;
            const tokenField = document.getElementById('pgabfw-google-pay-token');
            const checkoutButton = document.querySelector('form.checkout.woocommerce-checkout button[type="submit"]');
            if(tokenField && checkoutButton) {
                tokenField.value = JSON.stringify(paymentToken);
                checkoutButton.click();
                resolve();
            }else{
                reject();
                alert('Error! Something went wrong!');
            }

        });
    }
})();