import MiniCartBootstap from './modules/ContextBootstrap/MiniCartBootstap';
import SingleProductBootstap from './modules/ContextBootstrap/SingleProductBootstap';
import CartBootstrap from './modules/ContextBootstrap/CartBootstap';
import CheckoutBootstap from './modules/ContextBootstrap/CheckoutBootstap';
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
import Renderer from './modules/Renderer/Renderer';
import ErrorHandler from './modules/ErrorHandler';
import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer";
import MessageRenderer from "./modules/Renderer/MessageRenderer";
import Spinner from "./modules/Helper/Spinner";
import {
    getCurrentPaymentMethod,
    ORDER_BUTTON_SELECTOR,
    PaymentMethods
} from "./modules/Helper/CheckoutMethodState";
import {hide, setVisible, setVisibleByClass} from "./modules/Helper/Hiding";
import {isChangePaymentPage} from "./modules/Helper/Subscriptions";
import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler";
import FormSaver from './modules/Helper/FormSaver';
import FormValidator from "./modules/Helper/FormValidator";
import {loadPaypalScript} from "./modules/Helper/ScriptLoading";

// TODO: could be a good idea to have a separate spinner for each gateway,
// but I think we care mainly about the script loading, so one spinner should be enough.
const buttonsSpinner = new Spinner(document.querySelector('.ppc-button-wrapper'));
const cardsSpinner = new Spinner('#ppcp-hosted-fields');

const bootstrap = () => {
    const checkoutFormSelector = 'form.woocommerce-checkout';

    const errorHandler = new ErrorHandler(
        PayPalCommerceGateway.labels.error.generic,
        document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
    );
    const spinner = new Spinner();
    const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner);

    const formSaver = new FormSaver(
        PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
        PayPalCommerceGateway.ajax.save_checkout_form.nonce,
    );

    const formValidator = PayPalCommerceGateway.early_checkout_validation_enabled ?
        new FormValidator(
            PayPalCommerceGateway.ajax.validate_checkout.endpoint,
            PayPalCommerceGateway.ajax.validate_checkout.nonce,
    ) : null;

    const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, checkoutFormSelector, formSaver, formValidator, spinner, errorHandler);

    jQuery('form.woocommerce-checkout input').on('keydown', e => {
        if (e.key === 'Enter' && [
            PaymentMethods.PAYPAL,
            PaymentMethods.CARDS,
            PaymentMethods.CARD_BUTTON,
        ].includes(getCurrentPaymentMethod())) {
            e.preventDefault();
        }
    });

    const onSmartButtonClick = (data, actions) => {
        window.ppcpFundingSource = data.fundingSource;
        const requiredFields = jQuery('form.woocommerce-checkout .validate-required:visible :input');
        requiredFields.each((i, input) => {
            jQuery(input).trigger('validate');
        });

        if (PayPalCommerceGateway.basic_checkout_validation_enabled) {
            // A quick fix to get the errors about empty form fields before attempting PayPal order,
            // it should solve #513 for most of the users, but it is not a proper solution.
            // Currently it is disabled by default because a better solution is now implemented
            // (see woocommerce_paypal_payments_basic_checkout_validation_enabled,
            // woocommerce_paypal_payments_early_wc_checkout_validation_enabled filters).
            const invalidFields = Array.from(jQuery('form.woocommerce-checkout .validate-required.woocommerce-invalid:visible'));
            if (invalidFields.length) {
                const billingFieldsContainer = document.querySelector('.woocommerce-billing-fields');
                const shippingFieldsContainer = document.querySelector('.woocommerce-shipping-fields');

                const nameMessageMap = PayPalCommerceGateway.labels.error.required.elements;
                const messages = invalidFields.map(el => {
                    const name = el.querySelector('[name]')?.getAttribute('name');
                    if (name && name in nameMessageMap) {
                        return nameMessageMap[name];
                    }
                    let label = el.querySelector('label').textContent
                        .replaceAll('*', '')
                        .trim();
                    if (billingFieldsContainer?.contains(el)) {
                        label = PayPalCommerceGateway.labels.billing_field.replace('%s', label);
                    }
                    if (shippingFieldsContainer?.contains(el)) {
                        label = PayPalCommerceGateway.labels.shipping_field.replace('%s', label);
                    }
                    return PayPalCommerceGateway.labels.error.required.field
                        .replace('%s', `<strong>${label}</strong>`)
                }).filter(s => s.length > 2);

                errorHandler.clear();
                if (messages.length) {
                    errorHandler.messages(messages);
                } else {
                    errorHandler.message(PayPalCommerceGateway.labels.error.required.generic);
                }

                return actions.reject();
            }
        }

        const form = document.querySelector(checkoutFormSelector);
        if (form) {
            jQuery('#ppcp-funding-source-form-input').remove();
            form.insertAdjacentHTML(
                'beforeend',
                `<input type="hidden" name="ppcp-funding-source" value="${data.fundingSource}" id="ppcp-funding-source-form-input">`
            )
        }

        const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
        if (isFreeTrial && data.fundingSource !== 'card') {
            freeTrialHandler.handle();
            return actions.reject();
        }
    };
    const onSmartButtonsInit = () => {
        buttonsSpinner.unblock();
    };
    const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit);
    const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages);
    const context = PayPalCommerceGateway.context;
    if (context === 'mini-cart' || context === 'product') {
        if (PayPalCommerceGateway.mini_cart_buttons_enabled === '1') {
            const miniCartBootstrap = new MiniCartBootstap(
                PayPalCommerceGateway,
                renderer,
                errorHandler,
            );

            miniCartBootstrap.init();
        }
    }

    if (context === 'product' && PayPalCommerceGateway.single_product_buttons_enabled === '1') {
        const singleProductBootstrap = new SingleProductBootstap(
            PayPalCommerceGateway,
            renderer,
            messageRenderer,
            errorHandler,
        );

        singleProductBootstrap.init();
    }

    if (context === 'cart') {
        const cartBootstrap = new CartBootstrap(
            PayPalCommerceGateway,
            renderer,
            errorHandler,
        );

        cartBootstrap.init();
    }

    if (context === 'checkout') {
        const checkoutBootstap = new CheckoutBootstap(
            PayPalCommerceGateway,
            renderer,
            messageRenderer,
            spinner,
            errorHandler,
        );

        checkoutBootstap.init();
    }

    if (context === 'pay-now' ) {
        const payNowBootstrap = new PayNowBootstrap(
            PayPalCommerceGateway,
            renderer,
            messageRenderer,
            spinner,
            errorHandler,
        );
        payNowBootstrap.init();
    }

    if (context !== 'checkout') {
        messageRenderer.render();
    }
};
document.addEventListener(
    'DOMContentLoaded',
    () => {
        if (!typeof (PayPalCommerceGateway)) {
            console.error('PayPal button could not be configured.');
            return;
        }

        if (
            PayPalCommerceGateway.context !== 'checkout'
            && PayPalCommerceGateway.data_client_id.user === 0
            && PayPalCommerceGateway.data_client_id.has_subscriptions
        ) {
            return;
        }

        const paypalButtonGatewayIds = [
            PaymentMethods.PAYPAL,
            ...Object.entries(PayPalCommerceGateway.separate_buttons).map(([k, data]) => data.id),
        ]

        // Sometimes PayPal script takes long time to load,
        // so we additionally hide the standard order button here to avoid failed orders.
        // Normally it is hidden later after the script load.
        const hideOrderButtonIfPpcpGateway = () => {
            // only in checkout and pay now page, otherwise it may break things (e.g. payment via product page),
            // and also the loading spinner may look weird on other pages
            if (
                !['checkout', 'pay-now'].includes(PayPalCommerceGateway.context)
                || isChangePaymentPage()
                || (PayPalCommerceGateway.is_free_trial_cart && PayPalCommerceGateway.vaulted_paypal_email !== '')
            ) {
                return;
            }

            const currentPaymentMethod = getCurrentPaymentMethod();
            const isPaypalButton = paypalButtonGatewayIds.includes(currentPaymentMethod);
            const isCards = currentPaymentMethod === PaymentMethods.CARDS;

            setVisibleByClass(ORDER_BUTTON_SELECTOR, !isPaypalButton && !isCards, 'ppcp-hidden');

            if (isPaypalButton) {
                // stopped after the first rendering of the buttons, in onInit
                buttonsSpinner.block();
            } else {
                buttonsSpinner.unblock();
            }

            if (isCards) {
                cardsSpinner.block();
            } else {
                cardsSpinner.unblock();
            }
        }

        jQuery(document).on('hosted_fields_loaded', () => {
            cardsSpinner.unblock();
        });

        let bootstrapped = false;

        hideOrderButtonIfPpcpGateway();

        jQuery(document.body).on('updated_checkout payment_method_selected', () => {
            if (bootstrapped) {
                return;
            }

            hideOrderButtonIfPpcpGateway();
        });

        loadPaypalScript(PayPalCommerceGateway, () => {
            bootstrapped = true;

            bootstrap();
        });
    },
);
