import usePromotionCodeForm from "@insite/client-framework/Common/Hooks/usePromotionCodeForm";
import {
    BillingAddressData,
    SpreedlyIframeStyles,
    useSpreedlyFrame,
} from "@insite/client-framework/Common/Hooks/useSpreedlyFrame";
import { useStripeSession } from "@insite/client-framework/Common/Hooks/useStripeSession";
import { IFrame, TokenExIframeStyles, useTokenExFrame } from "@insite/client-framework/Common/Hooks/useTokenExFrame";
import StyledWrapper, { getStyledWrapper } from "@insite/client-framework/Common/StyledWrapper";
import siteMessage from "@insite/client-framework/SiteMessage";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import { getSettingsCollection } from "@insite/client-framework/Store/Context/ContextSelectors";
import loadSpreedlyConfig from "@insite/client-framework/Store/Context/Handlers/LoadSpreedlyConfig";
import loadTokenExConfig from "@insite/client-framework/Store/Context/Handlers/LoadTokenExConfig";
import signOut from "@insite/client-framework/Store/Context/Handlers/SignOut";
import { getBillToState } from "@insite/client-framework/Store/Data/BillTos/BillTosSelectors";
import {
    canApplyPromotionsToCart,
    canPlaceOrder,
    getCartState,
    getCurrentCartState,
} from "@insite/client-framework/Store/Data/Carts/CartsSelector";
import resetCurrentCartId from "@insite/client-framework/Store/Data/Carts/Handlers/ResetCurrentCartId";
import { getLocation } from "@insite/client-framework/Store/Data/Pages/PageSelectors";
import {
    getCurrentPromotionsDataView,
    getPromotionsDataView,
} from "@insite/client-framework/Store/Data/Promotions/PromotionsSelectors";
import { getPageLinkByPageType } from "@insite/client-framework/Store/Links/LinksSelectors";
import applyPromotion from "@insite/client-framework/Store/Pages/CheckoutReviewAndSubmit/Handlers/ApplyPromotion";
import removePromotion from "@insite/client-framework/Store/Pages/CheckoutReviewAndSubmit/Handlers/RemovePromotion";
import placeOrder from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/PlaceOrder";
import setIsPlacingOrder from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/SetIsPlacingOrder";
import setOnePageCheckoutPaymentMethod from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/SetOnePageCheckoutPaymentMethod";
import setPaymentDetailsState from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/SetPaymentDetailsState";
import preloadOrderConfirmationData from "@insite/client-framework/Store/Pages/OrderConfirmation/Handlers/PreloadOrderConfirmationData";
import translate from "@insite/client-framework/Translate";
import { PaymentMethodDto, PromotionModel } from "@insite/client-framework/Types/ApiModels";
import WidgetModule from "@insite/client-framework/Types/WidgetModule";
import usePaymentDetailsCartState from "@insite/content-library/Hooks/Payments/usePaymentDetailsCartState";
import usePaymentDetailsFormHandlers from "@insite/content-library/Hooks/Payments/usePaymentDetailsFormHandlers";
import usePaymentDetailsFormState from "@insite/content-library/Hooks/Payments/usePaymentDetailsFormState";
import usePaymentDetailsFormValidation from "@insite/content-library/Hooks/Payments/usePaymentDetailsFormValidation";
import CreditCardDetailsEntry, {
    CreditCardDetailsEntryStyles,
    CreditCardDetailsValidation,
} from "@insite/content-library/Widgets/CheckoutReviewAndSubmit/CreditCardDetailsEntry";
import ECheckDetailsEntry, {
    ECheckDetailsEntryStyles,
    Validatable,
} from "@insite/content-library/Widgets/CheckoutReviewAndSubmit/ECheckDetailsEntry";
import PaymentProfileBillingAddress from "@insite/content-library/Widgets/CheckoutReviewAndSubmit/PaymentProfileBillingAddress";
import SavedPaymentProfileEntry, {
    SavedPaymentProfileEntryStyles,
    SavedPaymentProfileValidation,
} from "@insite/content-library/Widgets/CheckoutReviewAndSubmit/SavedPaymentProfileEntry";
import Button, { ButtonPresentationProps } from "@insite/mobius/Button";
import Checkbox from "@insite/mobius/Checkbox";
import { BaseTheme } from "@insite/mobius/globals/baseTheme";
import GridContainer, { GridContainerProps } from "@insite/mobius/GridContainer";
import GridItem, { GridItemProps } from "@insite/mobius/GridItem";
import Link, { LinkPresentationProps } from "@insite/mobius/Link";
import LoadingOverlay from "@insite/mobius/LoadingOverlay";
import Select, { SelectPresentationProps } from "@insite/mobius/Select";
import { generateSpreedlyFrameStyleConfig } from "@insite/mobius/SpreedlyFrame";
import Tag, { TagPresentationProps } from "@insite/mobius/Tag";
import TextField, { TextFieldPresentationProps } from "@insite/mobius/TextField";
import ToasterContext, { HasToasterContext, withToaster } from "@insite/mobius/Toast/ToasterContext";
import { generateTokenExFrameStyleConfig } from "@insite/mobius/TokenExFrame";
import Typography, { TypographyPresentationProps } from "@insite/mobius/Typography";
import { HasHistory, withHistory } from "@insite/mobius/utilities/HistoryContext";
import InjectableCss from "@insite/mobius/utilities/InjectableCss";
import VisuallyHidden from "@insite/mobius/VisuallyHidden";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { connect, ResolveThunks } from "react-redux";
import { css, ThemeProps, withTheme } from "styled-components";

const mapStateToProps = (state: ApplicationState) => {
    const { cartId, isPlacingOrder, paymentDetailsState, placeOrderErrorMessage } = state.pages.onePageCheckout;
    const cartState = cartId ? getCartState(state, cartId) : getCurrentCartState(state);
    const {
        websiteSettings,
        productSettings: { enableVat },
        cartSettings: { bypassCvvForSavedCards },
    } = getSettingsCollection(state);
    const promotionsDataView = cartId ? getPromotionsDataView(state, cartId) : getCurrentPromotionsDataView(state);
    const promotions = promotionsDataView.value;
    return {
        cartState,
        websiteSettings,
        billToState: getBillToState(state, cartState.value ? cartState.value.billToId : undefined),
        tokenExConfigs: state.context.tokenExConfigs,
        savedPaymentsPageLink: getPageLinkByPageType(state, "SavedPaymentsPage"),
        session: state.context.session,
        signInPageLink: getPageLinkByPageType(state, "SignInPage"),
        location: getLocation(state),
        enableVat,
        bypassCvvForSavedCards,
        isPlacingOrder,
        paymentDetailsState,
        placeOrderErrorMessage,
        confirmationPageLink: getPageLinkByPageType(state, "OnePageCheckoutConfirmationPage"),
        canApplyPromosToCart: canApplyPromotionsToCart(cartState?.value),
        promotionErrorMessage: state.pages.checkoutReviewAndSubmit.promotionErrorMessage,
        promotions,
        canPlaceOrder: canPlaceOrder(cartState.value),
        spreedlyConfig: state.context.spreedlyConfig,
    };
};

const mapDispatchToProps = {
    loadTokenExConfig,
    preloadOrderConfirmationData,
    resetCurrentCartId,
    setOnePageCheckoutPaymentMethod,
    signOut,
    setPaymentDetailsState,
    placeOrder,
    setIsPlacingOrder,
    applyPromotion,
    removePromotion,
    loadSpreedlyConfig,
};

type Props = ReturnType<typeof mapStateToProps> &
    ResolveThunks<typeof mapDispatchToProps> &
    ThemeProps<BaseTheme> &
    HasHistory &
    HasToasterContext;

export interface OnePageCheckoutPaymentDetailsStyles {
    form?: InjectableCss;
    fieldset?: InjectableCss;
    paymentDetailsGridContainer?: GridContainerProps;
    paymentDetailsHeadingGridItem?: GridItemProps;
    paymentDetailsHeadingText?: TypographyPresentationProps;
    paymentdMethodInfoGridItem?: GridItemProps;
    paymentdMethodNameText?: TypographyPresentationProps;
    paymentdMethodCreditCardInfoText?: TypographyPresentationProps;
    editGridItem?: GridItemProps;
    editLink?: LinkPresentationProps;
    paymentMethodAndPONumberContainer?: GridContainerProps;
    paymentMethodGridItem?: GridItemProps;
    paymentMethodSelect?: SelectPresentationProps;
    paymentProfileBillingAddress?: any;
    paymentProfileExpiredErrorWrapper?: InjectableCss;
    paymentProfileExpiredErrorText?: TypographyPresentationProps;
    paymentProfileEditCardLink?: LinkPresentationProps;
    vatNumberGridItem?: GridItemProps;
    vatNumberText?: TextFieldPresentationProps;
    poNumberGridItem?: GridItemProps;
    poNumberText?: TextFieldPresentationProps;
    emptyGridItem?: GridItemProps;
    adyenDropInGridItem?: GridItemProps;
    mainContainer?: GridContainerProps;
    savedPaymentProfile?: SavedPaymentProfileEntryStyles;
    creditCardDetailsGridItem?: GridItemProps;
    eCheckDetailsGridItem?: GridItemProps;
    creditCardDetails?: CreditCardDetailsEntryStyles;
    creditCardAddressGridItem?: GridItemProps;
    eCheckDetailsEntryStyles?: ECheckDetailsEntryStyles;
    saveAndContinueButtonGridItem: GridItemProps;
    saveAndContinueButton?: ButtonPresentationProps;
    saveCardCheckboxGridItem: GridItemProps;
    promotionsCodeGridItem?: GridItemProps;
    promotionTagsGridContainerItem?: GridItemProps;
    promotionTagsGridContainer?: GridContainerProps;
    promotionCodeGridContainer?: GridContainerProps;
    promotionTagsGridItem?: GridItemProps;
    promotionCodeTag?: TagPresentationProps;
    promotionCodeTagName?: TypographyPresentationProps;
    addDiscountGridItem?: GridItemProps;
    addDiscountButton?: ButtonPresentationProps;
    enterPromotionCodeGridItem?: GridItemProps;
    enterPromotionCodeTextField?: TextFieldPresentationProps;
    enterPromotionCodeApplyButton?: ButtonPresentationProps;
    appliedPromoCodeOverviewText?: TypographyPresentationProps;
    discountLabel?: TypographyPresentationProps;
    poNumberLabelText?: TypographyPresentationProps;
    poNumberValueText?: TypographyPresentationProps;
    vatNumberLabelText?: TypographyPresentationProps;
    vatNumberValueText?: TypographyPresentationProps;
}

export const onePageCheckoutPaymentDetailsStyles: OnePageCheckoutPaymentDetailsStyles = {
    fieldset: {
        css: css`
            margin: 0;
            padding: 0;
            border: 0;
        `,
    },
    paymentDetailsGridContainer: { gap: 20 },
    paymentDetailsHeadingGridItem: {
        width: 10,
        css: css`
            align-items: center;
        `,
    },
    paymentDetailsHeadingText: {
        variant: "h5",
        css: css`
            margin-bottom: 0;
        `,
    },
    paymentdMethodInfoGridItem: {
        width: 12,
        css: css`
            flex-direction: column;
        `,
    },
    editGridItem: {
        width: 2,
        css: css`
            justify-content: end;
            align-items: center;
        `,
    },
    paymentMethodAndPONumberContainer: {
        gap: 10,
        css: css`
            margin-bottom: 8px;
        `,
    },
    paymentMethodGridItem: {
        width: 6,
        css: css`
            flex-direction: column;
        `,
    },
    paymentProfileExpiredErrorWrapper: {
        css: css`
            display: flex;
            width: 100%;
        `,
    },
    paymentProfileExpiredErrorText: { color: "danger" },
    paymentProfileEditCardLink: {
        css: css`
            margin-left: 1rem;
        `,
    },
    vatNumberGridItem: { width: 6 },
    poNumberGridItem: { width: 6 },
    emptyGridItem: { width: 6 },
    creditCardDetailsGridItem: { width: 12 },
    eCheckDetailsGridItem: { width: [12, 12, 12, 6, 6] },
    creditCardAddressGridItem: {
        width: [12, 12, 12, 6, 6],
        css: css`
            flex-direction: column;
        `,
    },
    adyenDropInGridItem: { width: 12 },
    saveAndContinueButtonGridItem: {
        width: 12,
    },
    saveAndContinueButton: {
        css: css`
            margin-top: 1em;
        `,
    },
    saveCardCheckboxGridItem: {
        width: 12,
    },
    promotionsCodeGridItem: {
        width: 12,
    },
    promotionCodeGridContainer: {
        gap: 10,
    },
    promotionTagsGridContainer: {
        gap: 2,
    },
    promotionTagsGridContainerItem: {
        width: 12,
    },
    promotionTagsGridItem: {
        width: 12,
    },
    addDiscountGridItem: {
        width: 12,
    },
    addDiscountButton: {
        variant: "secondary",
    },
    enterPromotionCodeGridItem: {
        width: 6,
    },
    enterPromotionCodeApplyButton: {
        variant: "secondary",
    },
    promotionCodeTag: {
        css: css`
            & > button {
                min-height: 0;
            }
        `,
    },
    promotionCodeTagName: {
        css: css`
            align-self: flex-end;
            padding-bottom: 15px;
        `,
    },
    appliedPromoCodeOverviewText: {
        css: css`
            padding-bottom: 15px;
        `,
    },
    poNumberLabelText: {
        css: css`
            padding-top: 15px;
        `,
    },
    vatNumberLabelText: {
        css: css`
            padding-top: 15px;
        `,
    },
};

let tokenExAccountNumberIframe: IFrame | undefined;
let tokenExFrameStyleConfig: TokenExIframeStyles;
let spreedlyFrameStyleConfig: SpreedlyIframeStyles;

const styles = onePageCheckoutPaymentDetailsStyles;
const StyledForm = getStyledWrapper("form");
const StyledFieldSet = getStyledWrapper("fieldset");

const OnePageCheckoutPaymentDetails = ({
    cartState,
    tokenExConfigs,
    savedPaymentsPageLink,
    history,
    loadTokenExConfig,
    loadSpreedlyConfig,
    theme,
    enableVat,
    bypassCvvForSavedCards,
    setOnePageCheckoutPaymentMethod,
    isPlacingOrder,
    paymentDetailsState,
    placeOrderErrorMessage,
    signInPageLink,
    setPaymentDetailsState,
    placeOrder,
    setIsPlacingOrder,
    confirmationPageLink,
    canApplyPromosToCart,
    applyPromotion,
    promotionErrorMessage,
    promotions,
    removePromotion,
    canPlaceOrder,
    spreedlyConfig,
    billToState,
}: Props) => {
    const paymentDetailsFormState = usePaymentDetailsFormState();
    const {
        paymentMethod,
        setPaymentMethod,
        poNumber,
        vatNumber,
        saveCard,
        cardHolderName,
        cardNumber,
        cardType,
        expirationMonth,
        expirationYear,
        securityCode,
        countryId,
        paymentMethodError,
        poNumberError,
        showFormErrors,
        setShowFormErrors,
        isCardNumberTokenized,
        setIsCardNumberTokenized,
        isECheckTokenized,
        setIsECheckTokenized,
        accountHolderName,
        setAccountHolderName,
        accountNumber,
        setAccountNumber,
        routingNumber,
        setRoutingNumber,
        creditCardLastFourDigits,
        bankAccountLastFourDigits,
        isIframeLoaded,
        paymentMethodRef,
        poNumberRef,
        maskedCardNumber,
        setMaskedCardNumber,
        antiFraudSessionId,
        setAntiFraudSessionId,
    } = paymentDetailsFormState;
    const paymentDetailsCartState = usePaymentDetailsCartState(cartState, paymentMethod, countryId);
    const {
        cart,
        useTokenExGateway,
        useECheckTokenExGateway,
        paymentOptions,
        paymentMethods,
        paymentMethodDto,
        isCreditCard,
        isECheck,
        isPaymentProfile,
        useSpreedlyDropIn,
    } = paymentDetailsCartState;
    const paymentDetailsFormValidation = usePaymentDetailsFormValidation(
        paymentDetailsFormState,
        paymentDetailsCartState,
    );
    const { validatePaymentMethod, validatePONumber } = paymentDetailsFormValidation;
    const {
        handlePaymentMethodChange,
        handlePONumberChange,
        handleVatNumberChange,
        handleSaveCardChange,
        handleCardHolderNameChange,
        handleCardNumberChange,
        handleCardTypeChange,
        handleExpirationMonthChange,
        handleExpirationYearChange,
        handleSecurityCodeChange,
        handleEditCardClick,
    } = usePaymentDetailsFormHandlers({
        paymentDetailsFormState,
        paymentDetailsFormValidation,
        paymentDetailsCartState,
        history,
        savedPaymentsPageLink,
        setPaymentMethodCallback: (
            event: React.ChangeEvent<HTMLSelectElement>,
            paymentMethods: PaymentMethodDto[] | null | undefined,
        ) => {
            setOnePageCheckoutPaymentMethod({
                paymentMethod: paymentMethods?.find(method => method.name === event.target.value),
            });
        },
    });

    const antiFraudSessionIdHandler = useCallback(
        (stripeRadarSessionId: string | null) => {
            setAntiFraudSessionId(stripeRadarSessionId ?? "");
        },
        [setAntiFraudSessionId],
    );
    const tokenExLibraryIsLoaded = useTokenExFrame();
    const spreedlyLibraryIsLoaded = useSpreedlyFrame();
    const { isStripeRadarScriptLoaded } = useStripeSession(antiFraudSessionIdHandler);

    const {
        promotionCode,
        setPromotionCode,
        promotionsWithCode,
        setRemovingPromotionCode,
        isAddingDiscount,
        setIsAddingDiscount,
        isUpdatingPromotions,
        setIsUpdatingPromotions,
    } = usePromotionCodeForm(promotions);

    const applyPromotionHandler = () => {
        setIsUpdatingPromotions(true);
        applyPromotion({
            promotionCode: promotionCode.trim(),
            onComplete: (
                result: Partial<{
                    successMessage: string;
                    errorMessage: string;
                    isPromotionAppliedSuccess: boolean;
                }>,
            ) => {
                if (result.isPromotionAppliedSuccess) {
                    setPromotionCode("");
                    setIsAddingDiscount(false);
                }
                setIsUpdatingPromotions(false);
            },
        });
    };

    const removePromotionHandler = (promotion: PromotionModel) => {
        setIsUpdatingPromotions(true);
        removePromotion({
            promotionId: promotion.id,
            onComplete: () => {
                setIsUpdatingPromotions(false);
            },
        });
        setRemovingPromotionCode(promotion.promotionCode);
    };

    useEffect(() => {
        if (!isPlacingOrder || !canPlaceOrder) {
            return;
        }

        placeOrder({
            paymentMethod,
            poNumber,
            vatNumber,
            saveCard,
            cardHolderName,
            cardNumber,
            cardType,
            expirationMonth,
            expirationYear,
            securityCode,
            useBillingAddress: true,
            address1: "",
            countryId: "",
            stateId: "",
            city: "",
            postalCode: "",
            accountHolderName,
            accountNumber,
            routingNumber,
            payerId: "",
            maskedCardNumber,
            antiFraudSessionId,
            onSuccess: (cartId: string) => {
                setIsPlacingOrder({ isPlacingOrder: false });

                if (!confirmationPageLink) {
                    return;
                }

                history.push(`${confirmationPageLink.url}?cartId=${cartId}`);
            },
            onError: () => {
                setIsCardNumberTokenized(false);
                setIsPlacingOrder({ isPlacingOrder: false });
            },
            onComplete(resultProps) {
                if (resultProps.apiResult?.cart) {
                    // "this" is targeting the object being created, not the parent SFC
                    this.onSuccess?.(resultProps.apiResult.cart.id);
                } else {
                    // "this" is targeting the object being created, not the parent SFC
                    this.onError?.();
                }
            },
        });
    }, [isPlacingOrder]);

    useEffect(() => {
        if (isCardNumberTokenized || isECheckTokenized) {
            setPaymentDetailsState({ paymentDetailsState: "complete" });
        }
    }, [isCardNumberTokenized, isECheckTokenized]);

    useEffect(() => {
        if (!cartState.isLoading && cartState.value && cartState.value.paymentMethod && paymentMethod === "") {
            setPaymentMethod(cartState.value.paymentMethod.name);
        }
    }, [cartState]);

    let tokenName: string | undefined;
    const eCheckDetails = useRef<Validatable>(null);
    const creditCardDetails = useRef<CreditCardDetailsValidation>(null);
    const savedPaymentProfile = useRef<SavedPaymentProfileValidation>(null);

    if (useTokenExGateway) {
        if (isPaymentProfile) {
            tokenName = paymentMethodDto!.name;
        } else if (isCreditCard) {
            tokenName = "";
        }
    }

    if (useECheckTokenExGateway) {
        if (isECheck) {
            tokenName = "";
        }
    }

    useEffect(() => {
        if (typeof tokenName !== "undefined") {
            loadTokenExConfig({ token: tokenName, isECheck });
        }

        if (useSpreedlyDropIn && !spreedlyConfig?.environmentKey) {
            loadSpreedlyConfig();
        }
    }, [paymentMethod]);

    const handleTokenExSessionExpired = (token: string) => {
        loadTokenExConfig({ token, isECheck });
    };

    const tokenExConfig = typeof tokenName !== "undefined" ? tokenExConfigs[tokenName] : undefined;

    const saveCreditCardChangeHandler = (event: React.SyntheticEvent, value: boolean) => {
        handleSaveCardChange(value);
    };

    const toasterContext = React.useContext(ToasterContext);
    useEffect(() => {
        if (placeOrderErrorMessage === siteMessage("CreditCardInfo_MaximumCreditCardAttempts")) {
            toasterContext.addToast({
                body: placeOrderErrorMessage,
                messageType: "danger",
            });

            setTimeout(() => {
                signOut();
                signInPageLink && history.push(signInPageLink.url);
            }, 3000);
        }
    }, [placeOrderErrorMessage]);

    const handleAccountNumberIFrame = (accountNumberIFrame: IFrame) => {
        tokenExAccountNumberIframe = accountNumberIFrame;
    };

    const validateForm = () => {
        const paymentMethodValid = validatePaymentMethod(paymentMethod);
        const poNumberValid = validatePONumber(poNumber);

        if (useECheckTokenExGateway && isECheck) {
            tokenExAccountNumberIframe?.validate();
        }

        if (useSpreedlyDropIn && isStripeRadarScriptLoaded && !antiFraudSessionId) {
            return false;
        }

        let accountHolderNameValid = true;
        let accountNumberValid = true;
        let routingNumberValid = true;
        if (eCheckDetails?.current) {
            accountHolderNameValid = eCheckDetails.current.validateAccountHolderNameChange(accountHolderName);
            accountNumberValid = eCheckDetails.current.validateAccountNumberChange(accountNumber);
            routingNumberValid = eCheckDetails.current.validateRoutingNumberChange(routingNumber);
        }
        let creditCardValid = true;
        if (creditCardDetails?.current) {
            creditCardValid = creditCardDetails.current.validate();
        }

        if (savedPaymentProfile?.current) {
            creditCardValid = savedPaymentProfile?.current.validate();
        }

        if (!paymentMethodValid) {
            paymentMethodRef.current?.focus();
        } else if (!poNumberValid) {
            poNumberRef.current?.focus();
        }

        return (
            paymentMethodValid &&
            poNumberValid &&
            creditCardValid &&
            accountHolderNameValid &&
            accountNumberValid &&
            routingNumberValid
        );
    };

    const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();

        if (!validateForm()) {
            setShowFormErrors(true);
            return false;
        }

        if (
            (useTokenExGateway || useSpreedlyDropIn) &&
            ((isPaymentProfile && !bypassCvvForSavedCards) || isCreditCard)
        ) {
            creditCardDetails.current?.tokenize();
            savedPaymentProfile.current?.tokenize();
        } else if (useECheckTokenExGateway && isECheck) {
            tokenExAccountNumberIframe?.tokenize();
        } else if (useSpreedlyDropIn && isCreditCard) {
            creditCardDetails.current?.tokenize();
        } else {
            setPaymentDetailsState({ paymentDetailsState: "complete" });
        }

        return false;
    };

    if (!tokenExFrameStyleConfig) {
        tokenExFrameStyleConfig = generateTokenExFrameStyleConfig({ theme });
    }

    if (!spreedlyFrameStyleConfig && useSpreedlyDropIn) {
        spreedlyFrameStyleConfig = generateSpreedlyFrameStyleConfig({ theme });
    }

    if (
        !cart ||
        cart.requiresApproval ||
        !paymentOptions ||
        (useTokenExGateway && !tokenExLibraryIsLoaded) ||
        (useSpreedlyDropIn && !spreedlyLibraryIsLoaded)
    ) {
        return null;
    }

    const iframeName = useTokenExGateway ? "TokenEx" : useSpreedlyDropIn ? "Spreedly" : undefined;
    const billingData: BillingAddressData = {
        email: billToState.value?.email,
        firstName: billToState.value?.firstName,
        lastName: billToState.value?.lastName,
        address1: billToState.value?.address1,
        address2: billToState.value?.address2,
        country: billToState.value?.country?.abbreviation,
        state: billToState.value?.state?.abbreviation,
        city: billToState.value?.city,
        zip: billToState.value?.postalCode,
        phoneNumber: billToState.value?.phone,
        company: billToState.value?.companyName,
    };

    return (
        <GridContainer {...styles.paymentDetailsGridContainer}>
            <GridItem {...styles.paymentDetailsHeadingGridItem}>
                <Typography {...styles.paymentDetailsHeadingText} as="h2" data-test-selector="paymentDetailHeader">
                    {translate("Payment")}
                </Typography>
            </GridItem>
            {paymentDetailsState === "complete" && (
                <GridItem {...styles.editGridItem}>
                    <Link
                        {...styles.editLink}
                        onClick={() => {
                            setIsCardNumberTokenized(false);
                            setPaymentDetailsState({
                                paymentDetailsState: "active",
                            });
                        }}
                    >
                        {translate("Edit")}
                    </Link>
                </GridItem>
            )}
            {paymentDetailsState === "complete" && (
                <GridItem {...styles.paymentdMethodInfoGridItem}>
                    {promotionsWithCode.length > 0 && (
                        <>
                            <Typography {...styles.discountLabel}>{translate("Discount")}:</Typography>
                            {promotionsWithCode.map(promotion => (
                                <Typography
                                    as="p"
                                    key={`promotion-with-code-${promotion.id}`}
                                    data-test-selector={`onePageCheckout_appliedPromotionValue`}
                                    {...styles.appliedPromoCodeOverviewText}
                                >{`${promotion.promotionCode} ${promotion.name}`}</Typography>
                            ))}
                        </>
                    )}
                    <Typography as="p" {...styles.paymentdMethodNameText} data-test-selector="paymentMethodName">
                        {paymentMethodDto?.description || paymentMethodDto?.name}
                    </Typography>
                    {paymentMethodDto?.isCreditCard && (
                        <Typography
                            as="p"
                            {...styles.paymentdMethodCreditCardInfoText}
                            data-test-selector="paymentMethodCreditCardInfoText"
                        >
                            {cardType}&nbsp;
                            {`****${cardNumber.slice(-4)}`}&nbsp;
                            {`0${expirationMonth}`.slice(-2)}/{`${expirationYear}`.slice(-2)}
                        </Typography>
                    )}
                    {cart.showPoNumber && poNumber && (
                        <>
                            <Typography as="p" {...styles.poNumberLabelText} data-test-selector="poNumberLabelText">
                                {translate("PO Number")}:
                            </Typography>
                            <Typography as="p" {...styles.poNumberValueText} data-test-selector="poNumberValueText">
                                {poNumber}
                            </Typography>
                        </>
                    )}
                    {enableVat && vatNumber && (
                        <>
                            <Typography as="p" {...styles.vatNumberLabelText}>
                                {translate("VAT Number")}:
                            </Typography>
                            <Typography as="p" {...styles.vatNumberValueText}>
                                {vatNumber}
                            </Typography>
                        </>
                    )}
                </GridItem>
            )}
            {paymentDetailsState === "active" && canApplyPromosToCart && (
                <LoadingOverlay loading={isUpdatingPromotions}>
                    <GridItem {...styles.promotionsCodeGridItem}>
                        <GridContainer {...styles.promotionCodeGridContainer}>
                            {promotionsWithCode.length > 0 && (
                                <GridItem {...styles.promotionTagsGridContainerItem}>
                                    <GridContainer {...styles.promotionTagsGridContainer}>
                                        {promotionsWithCode.map(promotion => (
                                            <GridItem key={promotion.id} {...styles.promotionTagsGridItem}>
                                                <Tag
                                                    onDelete={e => {
                                                        e.preventDefault();
                                                        removePromotionHandler(promotion);
                                                    }}
                                                    data-test-selector={`promotionCode_promotionTag_${promotion.promotionCode}`}
                                                    {...styles.promotionCodeTag}
                                                >
                                                    {promotion.promotionCode}
                                                </Tag>
                                                <Typography
                                                    data-test-selector={`promotionCode_promotionName_${promotion.name}`}
                                                    {...styles.promotionCodeTagName}
                                                >
                                                    {promotion.name}
                                                </Typography>
                                            </GridItem>
                                        ))}
                                    </GridContainer>
                                </GridItem>
                            )}
                            {!isAddingDiscount && (
                                <GridItem {...styles.addDiscountGridItem}>
                                    <Button
                                        onClick={() => {
                                            setIsAddingDiscount(true);
                                        }}
                                        {...styles.addDiscountButton}
                                        data-test-selector="onePageCheckout_addDiscount"
                                    >
                                        {translate("Add Discount")}
                                    </Button>
                                </GridItem>
                            )}
                            {isAddingDiscount && (
                                <>
                                    <GridItem {...styles.enterPromotionCodeGridItem}>
                                        <TextField
                                            onChange={event => {
                                                setPromotionCode(event.target.value);
                                            }}
                                            onKeyDown={event => {
                                                if (event.keyCode === 13) {
                                                    applyPromotionHandler();
                                                    event.preventDefault();
                                                }
                                            }}
                                            data-test-selector="onePageCheckout_enterPromotionCode"
                                            value={promotionCode}
                                            placeholder={translate("Enter code")}
                                            error={promotionErrorMessage}
                                            {...styles.enterPromotionCodeTextField}
                                        />
                                    </GridItem>
                                    <GridItem {...styles.enterPromotionCodeGridItem}>
                                        <Button
                                            onClick={applyPromotionHandler}
                                            disabled={!(promotionCode?.length > 0)}
                                            {...styles.enterPromotionCodeApplyButton}
                                            data-test-selector="onePageCheckout_applyPromotionCode"
                                        >
                                            {translate("Apply")}
                                        </Button>
                                    </GridItem>
                                </>
                            )}
                        </GridContainer>
                    </GridItem>
                </LoadingOverlay>
            )}
            {paymentDetailsState === "active" && (
                <GridItem {...styles.paymentdMethodInfoGridItem}>
                    <StyledForm
                        {...styles.form}
                        id="reviewAndSubmitPaymentForm"
                        onSubmit={handleFormSubmit}
                        noValidate={true}
                        data-test-selector="reviewAndSubmitPaymentForm"
                    >
                        <StyledFieldSet {...styles.fieldset}>
                            <GridContainer {...styles.paymentMethodAndPONumberContainer}>
                                <GridItem {...styles.paymentMethodGridItem}>
                                    {paymentMethods && paymentMethods.length > 0 && (
                                        <>
                                            <Select
                                                {...styles.paymentMethodSelect}
                                                label={translate("Payment Method")}
                                                value={paymentMethod ?? paymentMethodDto?.name}
                                                onChange={handlePaymentMethodChange}
                                                required
                                                error={showFormErrors && paymentMethodError}
                                                data-test-selector="onePageCheckout_paymentMethod"
                                                ref={paymentMethodRef}
                                            >
                                                <option value="">{translate("Select Payment Method")}</option>
                                                {paymentMethods.map(method => (
                                                    <option key={method.name} value={method.name}>
                                                        {method.description || method.name}
                                                    </option>
                                                ))}
                                            </Select>
                                            {paymentMethodDto?.isPaymentProfile &&
                                                !paymentMethodDto.isPaymentProfileExpired && (
                                                    <PaymentProfileBillingAddress
                                                        address={paymentMethodDto.billingAddress}
                                                        extendedStyles={styles.paymentProfileBillingAddress}
                                                    />
                                                )}
                                            {paymentMethodDto?.isPaymentProfile &&
                                                paymentMethodDto.isPaymentProfileExpired && (
                                                    <StyledWrapper {...styles.paymentProfileExpiredErrorWrapper}>
                                                        <Typography {...styles.paymentProfileExpiredErrorText}>
                                                            {siteMessage("Checkout_PaymentProfileExpired")}
                                                        </Typography>
                                                        {savedPaymentsPageLink && (
                                                            <Link
                                                                {...styles.paymentProfileEditCardLink}
                                                                onClick={handleEditCardClick}
                                                            >
                                                                {translate("Edit Card")}
                                                            </Link>
                                                        )}
                                                    </StyledWrapper>
                                                )}
                                        </>
                                    )}
                                </GridItem>
                                {cart.showPoNumber && (
                                    <GridItem {...styles.poNumberGridItem}>
                                        <TextField
                                            {...styles.poNumberText}
                                            label={
                                                <>
                                                    <span aria-hidden>{translate("PO Number")}</span>
                                                    <VisuallyHidden>
                                                        {translate("Purchase Order Number")}
                                                    </VisuallyHidden>
                                                </>
                                            }
                                            value={poNumber}
                                            onChange={handlePONumberChange}
                                            required={cart.requiresPoNumber}
                                            maxLength={50}
                                            error={showFormErrors && poNumberError}
                                            data-test-selector="onePageCheckout_poNumber"
                                            ref={poNumberRef}
                                        />
                                    </GridItem>
                                )}
                                {enableVat && (
                                    <GridItem {...styles.vatNumberGridItem}>
                                        <TextField
                                            {...styles.vatNumberText}
                                            label={translate("VAT Number")}
                                            value={vatNumber}
                                            onChange={handleVatNumberChange}
                                            maxLength={50}
                                            data-test-selector="onePageCheckout_vatNumber"
                                        />
                                    </GridItem>
                                )}
                                {paymentMethodDto?.isPaymentProfile &&
                                    !paymentMethodDto.isPaymentProfileExpired &&
                                    !bypassCvvForSavedCards && (
                                        <GridItem width={6}>
                                            <SavedPaymentProfileEntry
                                                iframe={iframeName}
                                                onSecurityCodeUpdate={handleSecurityCodeChange}
                                                extendedStyles={styles.savedPaymentProfile}
                                                tokenExConfig={tokenExConfig}
                                                tokenExFrameStyleConfig={tokenExFrameStyleConfig}
                                                spreedlyConfig={spreedlyConfig}
                                                spreedlyFrameStyleConfig={spreedlyFrameStyleConfig}
                                                paymentMethodDto={paymentMethodDto}
                                                updateCardIsTokenized={setIsCardNumberTokenized}
                                                ref={savedPaymentProfile}
                                            />
                                        </GridItem>
                                    )}
                                {cart.showCreditCard && paymentMethodDto?.isCreditCard && (
                                    <GridItem {...styles.creditCardDetailsGridItem}>
                                        <CreditCardDetailsEntry
                                            canSaveCard={paymentOptions.canStorePaymentProfile}
                                            iframe={iframeName}
                                            onSaveCardUpdate={handleSaveCardChange}
                                            onCardHolderNameUpdate={handleCardHolderNameChange}
                                            onCardNumberUpdate={handleCardNumberChange}
                                            onCardTypeUpdate={handleCardTypeChange}
                                            onExpirationMonthUpdate={handleExpirationMonthChange}
                                            onExpirationYearUpdate={handleExpirationYearChange}
                                            onSecurityCodeUpdate={handleSecurityCodeChange}
                                            availableCardTypes={paymentOptions.cardTypes ?? []}
                                            availableMonths={paymentOptions.expirationMonths ?? []}
                                            availableYears={paymentOptions.expirationYears ?? []}
                                            extendedStyles={styles.creditCardDetails}
                                            isIframeLoaded={isIframeLoaded}
                                            bankAccountLastFourDigits={bankAccountLastFourDigits}
                                            creditCardLastFourDigits={creditCardLastFourDigits}
                                            variation="onePageCheckout"
                                            tokenExConfig={tokenExConfig}
                                            onTokenExSessionExpired={handleTokenExSessionExpired}
                                            tokenExFrameStyleConfig={tokenExFrameStyleConfig}
                                            updateCardIsTokenized={setIsCardNumberTokenized}
                                            spreedlyConfig={spreedlyConfig}
                                            spreedlyFrameStyleConfig={spreedlyFrameStyleConfig}
                                            updateMaskedCardNumber={setMaskedCardNumber}
                                            ref={creditCardDetails}
                                            billingData={billingData}
                                        />
                                    </GridItem>
                                )}
                                {cart.showECheck && paymentMethodDto?.isECheck && (
                                    <GridItem {...styles.eCheckDetailsGridItem}>
                                        <ECheckDetailsEntry
                                            iframe={useECheckTokenExGateway ? "TokenEx" : undefined}
                                            paymentMethod={paymentMethod}
                                            onAccountHolderNameChange={setAccountHolderName}
                                            onAccountNumberChange={setAccountNumber}
                                            onRoutingNumberChange={setRoutingNumber}
                                            tokenExConfig={tokenExConfig}
                                            tokenExFrameStyleConfig={tokenExFrameStyleConfig}
                                            extendedStyles={styles.eCheckDetailsEntryStyles}
                                            showFormErrors={showFormErrors}
                                            updateIsECheckTokenized={setIsECheckTokenized}
                                            updateShowFormErrors={setShowFormErrors}
                                            setAccountNumberIFrame={handleAccountNumberIFrame}
                                            ref={eCheckDetails}
                                        />
                                    </GridItem>
                                )}
                                {paymentOptions.canStorePaymentProfile && paymentMethodDto?.isCreditCard && (
                                    <GridItem {...styles.saveCardCheckboxGridItem}>
                                        <Checkbox checked={saveCard} onChange={saveCreditCardChangeHandler}>
                                            {translate("Save card information")}
                                        </Checkbox>
                                    </GridItem>
                                )}
                                <GridItem {...styles.saveAndContinueButtonGridItem}>
                                    <Button
                                        {...styles.saveAndContinueButton}
                                        data-test-selector="saveAndContinueButton"
                                    >
                                        {translate("Save & Continue")}
                                    </Button>
                                </GridItem>
                            </GridContainer>
                        </StyledFieldSet>
                    </StyledForm>
                </GridItem>
            )}
        </GridContainer>
    );
};

const widgetModule: WidgetModule = {
    component: connect(
        mapStateToProps,
        mapDispatchToProps,
    )(withToaster(withHistory(withTheme(OnePageCheckoutPaymentDetails)))),
    definition: {
        displayName: "Payment Details",
        group: "One Page Checkout",
        allowedContexts: ["OnePageCheckoutPage"],
    },
};

export default widgetModule;
