function addBrainTreeLibrary(onBrainTreeLibraryLoaded) {
    var before = Date.now();
    var loadedScripts = 0;
    for (var i = 0; i < CONST.BT_LIBS.length; i++) {
        $.getScript(CONST.BT_LIBS[i])
            .done(function (script, textStatus) {
                loadedScripts++;

                if (loadedScripts === CONST.BT_LIBS.length) {
                    onBrainTreeLibraryLoaded();
                }
            })
            .fail(function (jqxhr, settings, exception) {
                console.error("Fail, exception: " + exception + ", took: " + (Date.now() - before) + ", now: " + Date.now());
            });
    }

    var after = Date.now();
}

function initializeCreditMessageAndDropIn(isMiniCartCheckout) {
    braintree.paypalCheckout.create({
        autoSetDataUserIdToken: shouldRenderPayPalChangePaymentButton,
        client: client
    }, function (paypalCheckoutErr, paypalCheckoutInstance) {
        if (typeof paypal === "undefined") {
            const commit = paypalIntent === CONST.INTENT_SALE && userAction === 'true';
            loadSDK(paypalCheckoutInstance, paypalIntent, commit, getPaypalFlow(), function(){
                hidePayPalPayLaterMessages();

                initializeDropIn(createPayPalOtionsExpressCheckout(), isMiniCartCheckout);
            });
        } else {
            initializeDropIn(createPayPalOtionsExpressCheckout(), isMiniCartCheckout);
        }
    });
}

function loadSDK(paypalCheckoutInstance, paypalIntent, commit, flow, onLoad) {
    paypalCheckoutInstance.getClientId().then(function (id) {
        const vault = flow === CONST.VAULT_FLOW;
        var importRequest = {};
        var disableFunding = !creditEnabled ? "credit" : "";
        var enableFunding = "";
        importRequest["client-id"] = id;
        importRequest.commit = commit;
        importRequest.intent = paypalIntent === "sale" || vault ? "capture" : paypalIntent;
        importRequest.components = "buttons,messages,funding-eligibility";

        if(typeof isPayLaterForNonUsCountriesEnabled != 'undefined' && isPayLaterForNonUsCountriesEnabled) {
            enableFunding = "paylater";
        }
        if (vault){
            importRequest.vault = vault;
        }
        if (disableFundingForPage !== "") {
            disableFunding += disableFunding !== "" ? "," + disableFundingForPage : disableFundingForPage;
        }
        if (disableFunding !== ""){
            importRequest["disable-funding"] = disableFunding;
        }
        if(enableFunding !== ""){
            importRequest["enable-funding"] = enableFunding;
        }
        if (typeof currency !== 'undefined' && currency !== '') {
            importRequest.currency = currency;
        }
        if (typeof braintreeLocale != 'undefined' && braintreeLocale !== '') {
            importRequest.locale = braintreeLocale;
        }

        if (typeof addPaymentMethodsPage !== 'undefined' && typeof shoppingMiniCart === 'undefined') {
            importRequest.dataAttributes = {'data-namespace': 'addPaymentMethodPaypalSdk'};
        }

        paypalCheckoutInstance.loadPayPalSDK(importRequest, function () {
            onLoad();

        });
    });
}

function createClientInstance(errorDivId, onClientIstanceLoaded) {
    if ((typeof clientToken != 'undefined' && clientToken !== '')
        && (typeof braintree != 'undefined' && braintree !== '')) {
        braintree.client.create({
            authorization: clientToken
        }, function (clientErr, clientInstance) {
            if (clientErr) {
                handleClientError(clientErr);
                return;
            }

            if (typeof advancedFraudToolsEnabled != 'undefined'){
                createDataCollector({
                    client: clientInstance,
                    paypal: JSON.parse(advancedFraudToolsEnabled)
                })
            }

            client = clientInstance;
            onClientIstanceLoaded();
        });
    } else {
        var errorsComponent = $(errorDivId);
        errorsComponent.empty();
        if (errorDivId === CONST.GLOBAL_MESSAGES){
            errorsComponent.append(createErrorDiv(ACC.addons.braintreeb2baddon['braintree.message.use.saved.payments']));
        } else if (errorDivId === CONST.PAYPAL_EXPRESS_ERROR_ID) {
            errorsComponent.append(createErrorDiv(ACC.addons.braintreeb2baddon['braintree.message.try.refresh']));
        }
    }
}


function handleClientError(error) {
    if (error != null && (typeof error != 'undefined' || error !== 'undefined')) {
        // skip validation error: use paypal method
        if (error.message !== 'User did not enter a payment method') {
            var errorMsg;
            if(isHostedFieldsError(error)) {
                errorMsg = ACC.addons.braintreeb2baddon['braintree.message.invalid.card'];
            } else {
                errorMsg = (typeof getErrorMessage(error.code.toLowerCase()) === "undefined")
                    ? ACC.addons.braintreeb2baddon['braintree.message.error.provider'] + ' ' + error.message
                    : getErrorMessage(error.code.toLowerCase());
            }

            showCustomAbsoluteErrorMessage(errorMsg);
        }
    }
}

function isHostedFieldsError(error) {
    if (
        error.code === CONST.HOSTED_FIELDS_FIELDS_EMPTY
        || error.code === CONST.HOSTED_FIELDS_FIELDS_INVALID
        || error.code === CONST.HOSTED_FIELDS_FAILED_TOKENIZATION
        || error.code === CONST.HOSTED_FIELDS_TOKENIZATION_NETWORK_ERROR
    ) {
        return true;
    }
    return false;
}

function handleLPMFailedError(error) {
    if (typeof error != 'undefined' || error !== 'undefined') {

        const detailedMessage = getDetailedMessageIfExist(error);
        if (error.code === 'LOCAL_PAYMENT_POPUP_CLOSED' || error.code === 'LOCAL_PAYMENT_WINDOW_CLOSED') {
            console.error('Customer closed Local Payment popup.');
        } else if (detailedMessage !== undefined) {
            console.error('LPM failed!', error);
            const errorMsg = getErrorMessageByDetailedMessage(detailedMessage);

            showCustomAbsoluteErrorMessage(errorMsg);
        } else {
            console.error('LPM failed!', error);

            const errorMsg = ACC.addons.braintreeb2baddon['braintree.message.lpm.failed'];

            showCustomAbsoluteErrorMessage(errorMsg);
        }
    }
}

function getDetailedMessageIfExist(error) {
    if (error.details !== undefined
        && error.details.originalError !== undefined
        && error.details.originalError.details !== undefined
        && error.details.originalError.details.originalError !== undefined
        && error.details.originalError.details.originalError.error !== undefined
        && error.details.originalError.details.originalError.error.message !== undefined) {
        return error.details.originalError.details.originalError.error.message;
    } else {
        return undefined;
    }
}

function getErrorMessageByDetailedMessage(detailedMessage) {
    if (detailedMessage.startsWith('Currency code')) {
        return ACC.addons.braintreeb2baddon['braintree.message.lpm.invalid.currency'];
    } else if (detailedMessage.startsWith('Country code')) {
        return ACC.addons.braintreeb2baddon['braintree.message.lpm.invalid.country'];
    } else {
        return ACC.addons.braintreeb2baddon['braintree.message.lpm.error.general'];
    }
}

function createDataCollector(configObj) {
    braintree.dataCollector.create(configObj, function (error, dataCollectorInstance) {
        if (error) {
            handleClientError(error);
            return;
        } else {
            deviceData = dataCollectorInstance.deviceData;
        }
    });
}

function getPaypalFlow() {
    if (storeInVault && (typeof addPaymentMethodsPage !== 'undefined' || !shouldRenderPayPalChangePaymentButton)) {
        return CONST.VAULT_FLOW;
    }
    return CONST.CHECKOUT_FLOW;
}

function createErrorDiv(message) {
    var errorComponent = $(HTML.DIV).attr(HTML.CLASS, CONST.ERROR_ALERT_NEGATIVE_CSS_CLASS);
    errorComponent.prepend(message);
    return errorComponent;
}

function resetHostedFields() {
    $(CONST.NUMBER_ID).empty();
    $(CONST.EXPIRATION_DATE_ID).empty();
    $(CONST.CVV_ID).empty();
    $(CONST.CARD_HOLDER_ID).empty();
}
