import {
    BillingAddressData,
    getSpreedlyFrame,
    SpreedlyIFrame,
    SpreedlyIframeStyles,
    SpreedlyType,
} from "@insite/client-framework/Common/Hooks/useSpreedlyFrame";
import {
    IFrame,
    TokenEx as TokenExType,
    TokenExIframeStyles,
    TokenExPCIIframeConfig,
} from "@insite/client-framework/Common/Hooks/useTokenExFrame";
import mergeToNew from "@insite/client-framework/Common/mergeToNew";
import { isNonEmptyString } from "@insite/client-framework/Common/Utilities/StringUtilities";
import validateCreditCard from "@insite/client-framework/Common/Utilities/validateCreditCard";
import logger from "@insite/client-framework/Logger";
import { SpreedlyConfig, TokenExConfig } from "@insite/client-framework/Services/SettingsService";
import siteMessage from "@insite/client-framework/SiteMessage";
import translate from "@insite/client-framework/Translate";
import {
    convertSpreedlyCardType,
    convertTokenExCardType,
} from "@insite/content-library/Widgets/SavedPayments/PaymentUtilities";
import Button, { ButtonPresentationProps } from "@insite/mobius/Button";
import Checkbox, { CheckboxPresentationProps } from "@insite/mobius/Checkbox";
import CheckboxGroup, { CheckboxGroupComponentProps } from "@insite/mobius/CheckboxGroup";
import Clickable, { ClickableProps } from "@insite/mobius/Clickable";
import GridContainer, { GridContainerProps } from "@insite/mobius/GridContainer";
import GridItem, { GridItemProps } from "@insite/mobius/GridItem";
import QuestionCircle from "@insite/mobius/Icons/QuestionCircle";
import LazyImage, { LazyImageProps } from "@insite/mobius/LazyImage";
import Link from "@insite/mobius/Link";
import Modal, { ModalPresentationProps } from "@insite/mobius/Modal";
import Select, { SelectPresentationProps } from "@insite/mobius/Select";
import SpreedlyFrame, { SpreedlyFramePresentationProps } from "@insite/mobius/SpreedlyFrame";
import TextField, { TextFieldPresentationProps } from "@insite/mobius/TextField";
import TokenExFrame, { TokenExFramePresentationProps } from "@insite/mobius/TokenExFrame";
import Typography, { TypographyPresentationProps, TypographyProps } from "@insite/mobius/Typography";
import InjectableCss from "@insite/mobius/utilities/InjectableCss";
import React, { forwardRef, Ref, useEffect, useImperativeHandle, useRef, useState } from "react";
import styled, { css } from "styled-components";

interface OwnProps {
    canSaveCard?: boolean;
    iframe?: "TokenEx" | "Paymetric" | "Square" | "Paystand" | "Spreedly";
    paymetricFrameRef?: React.Ref<HTMLIFrameElement>;
    securityCodeError?: string;
    availableCardTypes: { key: string; value: string }[];
    availableMonths: { key: string; value: number }[];
    availableYears: { key: number; value: number }[];
    extendedStyles?: CreditCardDetailsEntryStyles;
    onResetPaymentMethodClick?: () => void;
    error?: string;
    bankAccountLastFourDigits?: string;
    creditCardLastFourDigits?: string;
    isIframeLoaded?: boolean;
    variation?: "onePageCheckout";
    tokenExConfig?: TokenExConfig;
    onTokenExSessionExpired?: (token: string) => void;
    tokenExFrameStyleConfig?: TokenExIframeStyles;
    spreedlyConfig?: SpreedlyConfig;
    spreedlyFrameStyleConfig?: SpreedlyIframeStyles;
    billingData?: BillingAddressData;
    updateCardIsTokenized?: (value: boolean) => void;
    updateMaskedCardNumber?: (value: string) => void;
    onSaveCardUpdate: (value: boolean) => void;
    onCardHolderNameUpdate: (value: string) => void;
    onCardNumberUpdate: (value: string) => void;
    onCardTypeUpdate: (value: string) => void;
    onExpirationMonthUpdate: (value: number) => void;
    onExpirationYearUpdate: (value: number) => void;
    onSecurityCodeUpdate: (value: string) => void;
}

export interface CreditCardDetailsEntryStyles {
    creditCardDetailsContainer?: GridContainerProps;
    creditCardDetailsHeadingGridItem?: GridItemProps;
    creditCardDetailsHeading?: TypographyPresentationProps;
    saveCardGridItem?: GridItemProps;
    saveCardCheckboxGroup?: CheckboxGroupComponentProps;
    saveCardCheckbox?: CheckboxPresentationProps;
    cardHolderNameGridItem?: GridItemProps;
    cardHolderNameText?: TextFieldPresentationProps;
    cardNumberGridItem?: GridItemProps;
    cardNumberTokenExFrameWrapper?: InjectableCss;
    cardNumberTokenExFrame?: TokenExFramePresentationProps;
    cardNumberSpreedlyFrameWrapper?: InjectableCss;
    cardNumberSpreedlyFrame?: SpreedlyFramePresentationProps;
    cardNumberText?: TextFieldPresentationProps;
    cardTypeGridItem?: GridItemProps;
    cardTypeTokenExLabel?: TypographyPresentationProps;
    cardTypeTokenExText?: TypographyPresentationProps;
    cardTypeSpreedlyLabel?: TypographyPresentationProps;
    cardTypeSpreedlyText?: TypographyPresentationProps;
    cardTypeSelect?: SelectPresentationProps;
    expirationDateGridItem?: GridItemProps;
    expirationDateContainer?: GridContainerProps;
    expirationMonthGridItem?: GridItemProps;
    expirationMonthGridItemOnePageCheckout?: GridItemProps;
    expirationMonthSelect?: SelectPresentationProps;
    expirationYearGridItem?: GridItemProps;
    expirationYearGridItemOnePageCheckout?: GridItemProps;
    expirationYearSelect?: SelectPresentationProps;
    securityCodeGridItem?: GridItemProps;
    securityCodeGridItemOnePageCheckout?: GridItemProps;
    securityCodeTokenExFrameWrapper?: InjectableCss;
    securityCodeTokenExFrame?: TokenExFramePresentationProps;
    securityCodeSpreedlyFrameWrapper?: InjectableCss;
    securityCodeSpreedlyFrame?: SpreedlyFramePresentationProps;
    securityCodeText?: TextFieldPresentationProps;
    securityCodeHelpModal?: ModalPresentationProps;
    securityCodeHelpModalImageWrapper?: InjectableCss;
    securityCodeHelpImage?: LazyImageProps;
    paymetricGridItem?: GridItemProps;
    paymetricIframe?: InjectableCss;
    squareGridItem?: GridItemProps;
    squareContainer?: InjectableCss;
    paystandCardContainer?: InjectableCss;
    paystandGridItem?: GridItemProps;
    paymentMethodButton?: ButtonPresentationProps;
    creditCardInfoText?: TypographyPresentationProps;
    paystandCardHeaderText?: TypographyPresentationProps;
    paystandCardText?: TypographyPresentationProps;
    errorText?: TypographyPresentationProps;
    resetPaymentMethodGridItem?: GridItemProps;
    onePageCheckoutSecurityCodeClickable?: ClickableProps;
}

export const creditCardDetailsEntryStyles: CreditCardDetailsEntryStyles = {
    creditCardDetailsContainer: { gap: 10 },
    creditCardDetailsHeadingGridItem: { width: 12 },
    creditCardDetailsHeading: { variant: "h5" },
    saveCardGridItem: { width: 12 },
    cardHolderNameGridItem: { width: 12 },
    cardNumberGridItem: { width: 12 },
    cardNumberTokenExFrameWrapper: {
        css: css`
            height: inherit;
        `,
    },
    cardNumberSpreedlyFrameWrapper: {
        css: css`
            height: inherit;
        `,
    },
    cardTypeGridItem: {
        width: 12,
        css: css`
            flex-direction: column;
        `,
    },
    cardTypeTokenExLabel: { weight: 600 },
    cardTypeSpreedlyLabel: { weight: 600 },
    expirationDateGridItem: { width: 12 },
    expirationDateContainer: { gap: 10 },
    expirationMonthGridItem: { width: 6 },
    expirationMonthGridItemOnePageCheckout: { width: 4 },
    expirationYearGridItem: { width: 6 },
    expirationYearGridItemOnePageCheckout: { width: 4 },
    securityCodeGridItem: { width: 12 },
    securityCodeGridItemOnePageCheckout: { width: 4 },
    securityCodeTokenExFrameWrapper: {
        css: css`
            height: inherit;
        `,
    },
    securityCodeSpreedlyFrameWrapper: {
        css: css`
            height: inherit;
        `,
    },
    securityCodeHelpModalImageWrapper: {
        css: css`
            display: flex;
            justify-content: center;
        `,
    },
    paymetricGridItem: {
        width: 12,
    },
    paymetricIframe: {
        css: css`
            width: 100%;
        `,
    },
    squareGridItem: {
        width: 12,
    },
    squareContainer: {
        css: css`
            width: 100%;
        `,
    },
    paystandCardContainer: {
        css: css`
            width: 100%;
            display: flex;
            flex-direction: column;
            margin-bottom: 10px;
        `,
    },
    paystandGridItem: {
        width: 12,
        css: css`
            flex-direction: column;
        `,
    },
    resetPaymentMethodGridItem: {
        width: 12,
        css: css`
            flex-direction: column;
        `,
    },
    errorText: {
        color: "danger",
        weight: 600,
    },
    paystandCardHeaderText: {
        weight: 600,
    },
    onePageCheckoutSecurityCodeClickable: {
        as: "span",
        css: css`
            display: inline-flex;
        `,
    },
};

const CardNumberTokenExFrameWrapper = styled.div<InjectableCss>`
    ${({ css }) => css}
`;
const SecurityCodeTokenExFrameWrapper = styled.div<InjectableCss>`
    ${({ css }) => css}
`;
const SecurityCodeImageWrapper = styled.div<InjectableCss>`
    ${({ css }) => css}
`;

const PaymetricIframe = styled.iframe<InjectableCss>`
    ${({ css }) => css}
`;

const SquareContainer = styled.div<InjectableCss>`
    ${({ css }) => css}
`;

const PaystandCardContainer = styled.div<InjectableCss>`
    ${({ css }) => css}
`;

const CardNumberSpreedlyFrameWrapper = styled.div<InjectableCss>`
    ${({ css }) => css}
`;
const SecurityCodeSpreedlyFrameWrapper = styled.div<InjectableCss>`
    ${({ css }) => css}
`;

declare const TokenEx: TokenExType;
declare const Spreedly: SpreedlyType;
let spreedlyIFrame: SpreedlyIFrame | undefined;
let tokenExIframe: IFrame;

export interface CreditCardDetailsValidation {
    validate: () => boolean;
    tokenize: () => void;
}

const isMonthAndYearBeforeToday = (month: number, year: number) => {
    const today = new Date();
    const currentMonth = today.getMonth();
    const currentYear = today.getFullYear();
    return year < currentYear || (year === currentYear && month < currentMonth + 1);
};

const CreditCardDetailsEntry = (
    {
        iframe,
        paymetricFrameRef,
        extendedStyles,
        canSaveCard,
        availableCardTypes,
        availableMonths,
        availableYears,
        onResetPaymentMethodClick,
        error,
        bankAccountLastFourDigits,
        creditCardLastFourDigits,
        isIframeLoaded,
        variation,
        tokenExConfig,
        onTokenExSessionExpired,
        tokenExFrameStyleConfig,
        spreedlyConfig,
        spreedlyFrameStyleConfig,
        onSaveCardUpdate,
        onCardHolderNameUpdate,
        onCardNumberUpdate,
        onCardTypeUpdate,
        onExpirationMonthUpdate,
        onExpirationYearUpdate,
        onSecurityCodeUpdate,
        updateCardIsTokenized,
        updateMaskedCardNumber,
        billingData,
    }: OwnProps,
    ref: Ref<CreditCardDetailsValidation>,
) => {
    const [isSecurityCodeModalOpen, setIsSecurityCodeModalOpen] = useState(false);
    const [isTokenExIframeLoaded, setIsTokenExIframeLoaded] = useState(false);
    const [styles] = React.useState(() => mergeToNew(creditCardDetailsEntryStyles, extendedStyles));
    const [saveCard, setSaveCard] = useState(false);
    const [cardHolderName, setCardHolderName] = useState("");
    const [cardNumber, setCardNumber] = useState("");
    const [cardType, setCardType] = useState("");
    const [possibleCardType, setPossibleCardType] = useState("");
    const [expirationMonth, setExpirationMonth] = useState(1);
    const [expirationYear, setExpirationYear] = useState(new Date().getFullYear());
    const [securityCode, setSecurityCode] = useState("");
    const [cardHolderNameError, setCardHolderNameError] = useState("");
    const [cardNumberError, setCardNumberError] = useState("");
    const [cardTypeError, setCardTypeError] = useState("");
    const [securityCodeError, setSecurityCodeError] = useState("");
    const [expirationError, setExpirationError] = useState("");
    const cardHolderNameRef = useRef<HTMLInputElement>(null);
    const cardNumberRef = useRef<HTMLInputElement>(null);
    const cardTypeRef = useRef<HTMLSelectElement>(null);
    const securityCodeRef = useRef<HTMLInputElement>(null);
    const expirationMonthRef = useRef<HTMLSelectElement>(null);
    const expirationYearRef = useRef<HTMLSelectElement>(null);
    const [spreedlyReady, setSpreedlyReady] = useState(false);
    const billingDataRef = useRef(billingData);

    const resolvedShouldUseTokenEx = iframe === "TokenEx";
    const resolvedShouldUseSpreedly = iframe === "Spreedly";

    useImperativeHandle(
        ref,
        () => ({
            validate,
            tokenize,
        }),
        [resolvedShouldUseTokenEx, cardHolderName, cardNumber, cardType, expirationMonth, expirationYear, securityCode],
    );

    useEffect(() => {
        billingDataRef.current = billingData;
    }, [billingData]);

    const tokenize = () => {
        if (resolvedShouldUseTokenEx) {
            tokenExIframe?.tokenize();
        }

        if (resolvedShouldUseSpreedly && spreedlyIFrame?.isValid()) {
            spreedlyIFrame?.tokenize({
                month: expirationMonth,
                year: expirationYear,
                full_name: cardHolderName,
                address1: billingDataRef.current?.address1,
                address2: billingDataRef.current?.address2,
                city: billingDataRef.current?.city,
                state: billingDataRef.current?.state,
                zip: billingDataRef.current?.zip,
                country: billingDataRef.current?.country,
                company: billingDataRef.current?.company,
                phone_number: billingDataRef.current?.phoneNumber,
            });
        }
    };

    const validate = () => {
        if (resolvedShouldUseTokenEx) {
            tokenExIframe?.validate();
        }

        if (resolvedShouldUseSpreedly) {
            spreedlyIFrame?.validate();
        }

        const cardHolderNameValid = validateCardHolderName(cardHolderName);
        const cardNumberResult = validateCardNumber(cardNumber);
        const cardTypeValid = validateCardType(cardType);
        const cardExpired = validateCardExpiration(expirationMonth, expirationYear);
        const securityCodeResult = validateSecurityCode(securityCode);

        if (!cardHolderNameValid) {
            cardHolderNameRef.current?.focus();
        } else if (cardNumberResult.cardNumberEmpty || !cardNumberResult.cardNumberValid) {
            cardNumberRef.current?.focus();
        } else if (!cardTypeValid) {
            cardTypeRef.current?.focus();
        } else if (cardExpired) {
            const today = new Date();
            if (expirationYear < today.getFullYear()) {
                expirationYearRef.current?.focus();
            } else {
                expirationMonthRef.current?.focus();
            }
        } else if (securityCodeResult.securityCodeEmpty || !securityCodeResult.securityCodeValid) {
            securityCodeRef.current?.focus();
        }

        return (
            cardHolderNameValid &&
            !cardNumberResult.cardNumberEmpty &&
            cardNumberResult.cardNumberValid &&
            cardTypeValid &&
            !cardExpired &&
            !securityCodeResult.securityCodeEmpty &&
            securityCodeResult.securityCodeValid
        );
    };

    const validateCardHolderName = (cardHolderName: string) => {
        const cardHolderNameValid = isNonEmptyString(cardHolderName);
        setCardHolderNameError(!cardHolderNameValid ? translate("Cardholder name is required.") : "");
        return cardHolderNameValid;
    };

    const validateCardNumber = (cardNumber: string) => {
        let cardNumberEmpty = false;
        let cardNumberValid = true;

        if (!resolvedShouldUseTokenEx && !resolvedShouldUseSpreedly) {
            cardNumberEmpty = !isNonEmptyString(cardNumber);
            cardNumberValid = validateCreditCard(cardNumber);
        }

        if (cardNumberEmpty) {
            setCardNumberError(translate("Credit card number is required."));
        } else if (!cardNumberValid) {
            setCardNumberError(translate("Credit card number is invalid."));
        } else {
            setCardNumberError("");
        }

        return { cardNumberEmpty, cardNumberValid };
    };

    const validateCardType = (cardType: string) => {
        const cardTypeValid =
            resolvedShouldUseTokenEx ||
            resolvedShouldUseSpreedly ||
            (!resolvedShouldUseTokenEx && isNonEmptyString(cardType));
        setCardTypeError(!cardTypeValid ? translate("Credit card type is required.") : "");
        return cardTypeValid;
    };

    const validateCardExpiration = (expirationMonth: number, expirationYear: number) => {
        const cardExpired = isMonthAndYearBeforeToday(expirationMonth, expirationYear);
        setExpirationError(cardExpired ? translate("Card is expired. Please enter a valid expiration date.") : "");
        return cardExpired;
    };

    const validateSecurityCode = (securityCode: string) => {
        let securityCodeEmpty = false;
        let securityCodeValid = true;

        if (!resolvedShouldUseTokenEx && !resolvedShouldUseSpreedly) {
            securityCodeEmpty = !isNonEmptyString(securityCode);
            securityCodeValid = /^\d+$/.test(securityCode);
        }

        if (securityCodeEmpty) {
            setSecurityCodeError(!securityCodeValid ? translate("Security code is required.") : "");
        } else if (!securityCodeValid) {
            setSecurityCodeError(translate("Security code is invalid."));
        } else {
            setSecurityCodeError("");
        }

        return { securityCodeEmpty, securityCodeValid };
    };
    const monthGridItemStyles =
        variation === "onePageCheckout"
            ? styles.expirationMonthGridItemOnePageCheckout
            : styles.expirationMonthGridItem;
    const yearGridItemStyles =
        variation === "onePageCheckout" ? styles.expirationYearGridItemOnePageCheckout : styles.expirationYearGridItem;
    const securityCodeGridItemStyles =
        variation === "onePageCheckout" ? styles.securityCodeGridItemOnePageCheckout : styles.securityCodeGridItem;

    const tokenExReinitTimeout = 500;
    let lastTokenExReinitTime = 0;
    const tokenExErrorsLimit = (20 * 1e3) / tokenExReinitTimeout;
    let currentTokenExErrorCount = 0;

    useEffect(() => {
        if (typeof TokenEx === "undefined" || !tokenExConfig) {
            return;
        }

        setUpTokenEx();
    }, [tokenExConfig, typeof TokenEx]);

    const setUpTokenEx = () => {
        if (!tokenExConfig) {
            return;
        }
        currentTokenExErrorCount = 0;
        setUpTokenExIFrame(tokenExConfig);
    };

    const setUpTokenExIFrame = (config: TokenExConfig) => {
        if (tokenExIframe) {
            tokenExIframe.remove();
        }

        const iframeConfig: TokenExPCIIframeConfig = {
            authenticationKey: config.authenticationKey,
            cvv: true,
            cvvContainerID: "tokenExSecurityCode",
            cvvInputType: "text",
            enablePrettyFormat: true,
            inputType: "text",
            origin: config.origin,
            pci: true,
            styles: tokenExFrameStyleConfig,
            timestamp: config.timestamp,
            tokenExID: config.tokenExId,
            tokenScheme: config.tokenScheme,
            enableAriaRequired: true,
            title: translate("Credit card information"),
            customDataLabel: translate("credit card number"),
        };

        tokenExIframe = new TokenEx.Iframe("tokenExCardNumber", iframeConfig);
        tokenExIframe.load();
        tokenExIframe.on("load", _ => setIsTokenExIframeLoaded(true));
        tokenExIframe.on("tokenize", data => {
            onCardNumberUpdate(data.token);
            onSecurityCodeUpdate("CVV");
            onCardTypeUpdate(convertTokenExCardType(data.cardType));
            updateCardIsTokenized?.(true);
        });
        tokenExIframe.on("cardTypeChange", data => {
            updatePossibleCardType(data.possibleCardType);
            onCardTypeUpdate(convertTokenExCardType(data.possibleCardType));
        });
        tokenExIframe.on("validate", data => {
            if (data.isValid) {
                setCardNumberError("");
            } else {
                if (data.validator === "required") {
                    setCardNumberError(translate("Credit card number is required."));
                }
                if (data.validator === "format") {
                    setCardNumberError(translate("Credit card number is invalid."));
                }
            }

            if (data.isCvvValid) {
                setSecurityCodeError("");
            } else {
                if (data.cvvValidator === "required") {
                    setSecurityCodeError(translate("Security code is required."));
                }
                if (data.cvvValidator === "format") {
                    setSecurityCodeError(translate("Security code is invalid."));
                }
            }
        });
        tokenExIframe.on("error", data => {
            logger.error(data);
            if (data.error.indexOf("Session expired") > -1) {
                setCardNumberError(siteMessage("Checkout_PaymentSessionExpired") as string);
                onTokenExSessionExpired?.(config.token || "");
                return;
            }

            if (
                Date.now() - lastTokenExReinitTime > tokenExReinitTimeout &&
                ++currentTokenExErrorCount <= tokenExErrorsLimit
            ) {
                lastTokenExReinitTime = Date.now();
                setTimeout(() => {
                    setUpTokenExIFrame(config);
                }, tokenExReinitTimeout);
            } else {
                setCardNumberError(translate("Something went wrong, please contact support"));
            }
        });
    };

    useEffect(() => {
        if (typeof Spreedly === "undefined" || !spreedlyConfig) {
            return;
        }
        spreedlyIFrame = getSpreedlyFrame();
        spreedlyIFrame.setup(
            spreedlyConfig,
            "spreedlyCardNumber",
            "spreedlySecurityCode",
            spreedlyFrameStyleConfig,
            () => {
                setSpreedlyReady(true);
            },
        );

        spreedlyIFrame.onTokenize((token, tokenData) => {
            setCardNumberError("");
            onCardNumberUpdate(token);
            onSecurityCodeUpdate("CVV");
            onCardTypeUpdate(convertSpreedlyCardType(tokenData.card_type));
            updateMaskedCardNumber?.(`-${tokenData.last_four_digits}`);
            updateCardIsTokenized?.(true);
        });
        spreedlyIFrame.onCardTypeChanged(possibleCardType => {
            updatePossibleCardType(convertSpreedlyCardType(possibleCardType));
            onCardTypeUpdate(convertSpreedlyCardType(possibleCardType));
        });
        spreedlyIFrame.onError(message => {
            setCardNumberError(message);
        });
        spreedlyIFrame.onValidation(data => {
            if (data.validNumber) {
                setCardNumberError("");
            } else if (data.numberLength === 0) {
                setCardNumberError(translate("Credit card number is required."));
            } else {
                setCardNumberError(translate("Credit card number is invalid."));
            }

            if (data.validCvv) {
                setSecurityCodeError("");
            } else if (data.cvvLength === 0) {
                setSecurityCodeError(translate("Security code is required."));
            } else {
                setSecurityCodeError(translate("Security code is invalid."));
            }
        });
    }, [spreedlyConfig, typeof Spreedly]);

    const handleSecurityCodeHelpLinkClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
        event.preventDefault();
        setIsSecurityCodeModalOpen(true);
    };

    const paymentMethodClickHandler = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        event.preventDefault();
        onResetPaymentMethodClick?.();
    };

    const handleSecurityCodeModalClose = (_: React.SyntheticEvent<Element, Event>) => setIsSecurityCodeModalOpen(false);

    const updateSaveCard = (value: boolean) => {
        setSaveCard(value);
        onSaveCardUpdate(value);
    };
    const updateCardHolderName = (value: string) => {
        setCardHolderName(value);
        onCardHolderNameUpdate(value);
        validateCardHolderName(value);
    };
    const updateCardNumber = (value: string) => {
        setCardNumber(value);
        onCardNumberUpdate(value);
        validateCardNumber(value);
    };
    const updateCardType = (value: string) => {
        setCardType(value);
        onCardTypeUpdate(value);
        validateCardType(value);
    };
    const updatePossibleCardType = (value: string) => {
        setPossibleCardType(value);
    };
    const updateExpirationMonth = (value: number) => {
        setExpirationMonth(value);
        onExpirationMonthUpdate(value);
    };
    const updateExpirationYear = (value: number) => {
        setExpirationYear(value);
        onExpirationYearUpdate(value);
    };
    const updateSecurityCode = (value: string) => {
        setSecurityCode(value);
        onSecurityCodeUpdate(value);
        validateSecurityCode(value);
    };
    const onSaveCardChange = (_: React.SyntheticEvent<Element, Event>, value: boolean) => updateSaveCard(value);
    const onCardHolderNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        updateCardHolderName(event.currentTarget.value);
    };
    const onCardNumberChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        updateCardNumber(event.currentTarget.value);
    };
    const onCardTypeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
        updateCardType(event.currentTarget.value);
    };
    const onExpirationMonthChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
        const month = Number(event.currentTarget.value);
        if (Number.isNaN(month)) {
            return;
        }
        updateExpirationMonth(month);
    };
    const onExpirationYearChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
        const year = Number(event.currentTarget.value);
        if (Number.isNaN(year)) {
            return;
        }
        updateExpirationYear(year);
    };
    const onSecurityCodeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        updateSecurityCode(event.currentTarget.value);
    };

    const securityCodeLabel = translate("Locate my card's security code");
    const securityCodeHint = <Link onClick={handleSecurityCodeHelpLinkClick}>{securityCodeLabel}</Link>;

    const enterButtonLabel = cardNumber.length > 0 ? "Re-Enter Payment Details" : "Enter Payment Details";

    const securityCodeRequiredLabel =
        variation === "onePageCheckout" ? (
            <Clickable
                {...styles.onePageCheckoutSecurityCodeClickable}
                onClick={() => {
                    setIsSecurityCodeModalOpen(true);
                }}
            >
                <span>&nbsp;*&nbsp;</span>
                <QuestionCircle size={16} />
            </Clickable>
        ) : undefined;

    const securityCodeInput = resolvedShouldUseTokenEx ? (
        <TokenExFrame
            {...styles.securityCodeTokenExFrame}
            label={translate("Security code")}
            hint={variation === "onePageCheckout" ? undefined : securityCodeHint}
            tokenExIFrameContainer={
                <SecurityCodeTokenExFrameWrapper {...styles.securityCodeTokenExFrameWrapper} id="tokenExSecurityCode" />
            }
            disabled={!isTokenExIframeLoaded}
            required
            error={securityCodeError}
            data-test-selector="checkoutReviewAndSubmit_securityCode"
            requiredLabel={securityCodeRequiredLabel}
        />
    ) : resolvedShouldUseSpreedly ? (
        <SpreedlyFrame
            {...styles.securityCodeSpreedlyFrame}
            label={translate("Security code")}
            hint={variation === "onePageCheckout" ? undefined : securityCodeHint}
            spreedlyIFrameContainer={
                <SecurityCodeSpreedlyFrameWrapper
                    {...styles.securityCodeSpreedlyFrameWrapper}
                    id="spreedlySecurityCode"
                />
            }
            disabled={!spreedlyReady}
            required
            error={securityCodeError}
            data-test-selector="checkoutReviewAndSubmit_securityCode"
            requiredLabel={securityCodeRequiredLabel}
        />
    ) : (
        <TextField
            {...styles.securityCodeText}
            label={translate("Security code")}
            hint={variation === "onePageCheckout" ? undefined : securityCodeHint}
            value={securityCode}
            onChange={onSecurityCodeChange}
            required
            minLength={3}
            maxLength={4}
            error={securityCodeError}
            data-test-selector="checkoutReviewAndSubmit_securityCode"
            ref={securityCodeRef}
            requiredLabel={securityCodeRequiredLabel}
        />
    );

    return (
        <GridContainer {...styles.creditCardDetailsContainer}>
            {!variation && (
                <GridItem {...styles.creditCardDetailsHeadingGridItem}>
                    <Typography {...styles.creditCardDetailsHeading} as="h2">
                        {translate("Credit Card Details")}
                    </Typography>
                </GridItem>
            )}
            {canSaveCard && !variation && (
                <GridItem {...styles.saveCardGridItem}>
                    <CheckboxGroup {...styles.saveCardCheckboxGroup}>
                        <Checkbox {...styles.saveCardCheckbox} checked={saveCard} onChange={onSaveCardChange}>
                            {translate("Save card information")}
                        </Checkbox>
                    </CheckboxGroup>
                </GridItem>
            )}
            {iframe === "Paymetric" ? (
                <GridItem {...styles.paymetricGridItem}>
                    <PaymetricIframe id="paymetricIframe" ref={paymetricFrameRef} {...styles.paymetricIframe} />
                </GridItem>
            ) : iframe === "Square" ? (
                <GridItem {...styles.squareGridItem}>
                    <SquareContainer id="square-container" {...styles.squareContainer} />
                </GridItem>
            ) : iframe === "Paystand" ? (
                <GridItem {...styles.paystandGridItem}>
                    {cardNumber.length > 0 && (
                        <PaystandCardContainer {...styles.paystandCardContainer}>
                            <Typography {...styles.paystandCardHeaderText}>
                                {translate("Payment Information")}
                            </Typography>
                            <Typography {...styles.paystandCardText}>{cardHolderName}</Typography>
                            <Typography {...styles.paystandCardText}>
                                {cardType} (...{creditCardLastFourDigits ?? bankAccountLastFourDigits})
                            </Typography>
                            {expirationMonth > 0 && expirationYear > 0 && (
                                <Typography {...styles.paystandCardText}>
                                    {expirationMonth}/{expirationYear}
                                </Typography>
                            )}
                        </PaystandCardContainer>
                    )}
                    <Button
                        className="ps-button"
                        {...styles.paymentMethodButton}
                        onClick={paymentMethodClickHandler}
                        disabled={!isIframeLoaded}
                    >
                        {translate(enterButtonLabel)}
                    </Button>
                    {error && <Typography {...styles.errorText}>{error}</Typography>}
                </GridItem>
            ) : (
                <>
                    <GridItem {...styles.cardHolderNameGridItem}>
                        <TextField
                            {...styles.cardHolderNameText}
                            label={translate("Name on Card")}
                            value={cardHolderName}
                            onChange={onCardHolderNameChange}
                            required
                            maxLength={30}
                            error={cardHolderNameError}
                            data-test-selector="checkoutReviewAndSubmit_cardHolderName"
                            ref={cardHolderNameRef}
                        />
                    </GridItem>
                    <GridItem {...styles.cardNumberGridItem}>
                        {resolvedShouldUseTokenEx ? (
                            <TokenExFrame
                                {...styles.cardNumberTokenExFrame}
                                label={translate("Card Number")}
                                tokenExIFrameContainer={
                                    <CardNumberTokenExFrameWrapper
                                        {...styles.cardNumberTokenExFrameWrapper}
                                        id="tokenExCardNumber"
                                    />
                                }
                                disabled={!isTokenExIframeLoaded}
                                required
                                error={cardNumberError}
                                data-test-selector="checkoutReviewAndSubmit_cardNumber"
                            />
                        ) : resolvedShouldUseSpreedly ? (
                            <SpreedlyFrame
                                {...styles.cardNumberSpreedlyFrame}
                                label={translate("Card Number")}
                                spreedlyIFrameContainer={
                                    <CardNumberSpreedlyFrameWrapper
                                        {...styles.cardNumberSpreedlyFrameWrapper}
                                        id="spreedlyCardNumber"
                                    />
                                }
                                disabled={!spreedlyReady}
                                required
                                error={cardNumberError}
                                data-test-selector="checkoutReviewAndSubmit_cardNumber"
                            />
                        ) : (
                            <TextField
                                {...styles.cardNumberText}
                                label={translate("Card Number")}
                                value={cardNumber}
                                onChange={onCardNumberChange}
                                required
                                maxLength={16}
                                error={cardNumberError}
                                data-test-selector="checkoutReviewAndSubmit_cardNumber"
                                ref={cardNumberRef}
                            />
                        )}
                    </GridItem>
                    <GridItem {...styles.cardTypeGridItem}>
                        {resolvedShouldUseTokenEx ? (
                            <>
                                <Typography {...styles.cardTypeTokenExLabel} id="cardTypeTokenEx">
                                    {translate("Card Type")}
                                </Typography>
                                <TokenExCardTypeDisplay
                                    {...styles.cardTypeTokenExText}
                                    possibleCardType={possibleCardType}
                                    aria-labelledby="cardTypeTokenEx"
                                />
                            </>
                        ) : resolvedShouldUseSpreedly ? (
                            <>
                                <Typography {...styles.cardTypeSpreedlyLabel} id="cardTypeSpreedly">
                                    {translate("Card Type")}
                                </Typography>
                                <SpreedlyCardTypeDisplay
                                    {...styles.cardTypeSpreedlyText}
                                    possibleCardType={possibleCardType}
                                    aria-labelledby="cardTypeSpreedly"
                                />
                            </>
                        ) : (
                            <Select
                                {...styles.cardTypeSelect}
                                label={translate("Card Type")}
                                value={cardType}
                                onChange={onCardTypeChange}
                                required
                                error={cardTypeError}
                                data-test-selector="checkoutReviewAndSubmit_cardType"
                                ref={cardTypeRef}
                            >
                                <option value="">{translate("Select Card")}</option>
                                {availableCardTypes.map(ct => (
                                    <option key={ct.value} value={ct.value}>
                                        {ct.key}
                                    </option>
                                ))}
                            </Select>
                        )}
                    </GridItem>
                    <GridItem {...styles.expirationDateGridItem}>
                        <GridContainer {...styles.expirationDateContainer}>
                            <GridItem {...monthGridItemStyles}>
                                <Select
                                    {...styles.expirationMonthSelect}
                                    label={translate("Expiration Month")}
                                    value={expirationMonth}
                                    onChange={onExpirationMonthChange}
                                    required
                                    error={expirationError}
                                    data-test-selector="checkoutReviewAndSubmit_expirationMonth"
                                    ref={expirationMonthRef}
                                >
                                    {availableMonths.map(month => (
                                        <option key={month.value} value={month.value}>
                                            {month.key}
                                        </option>
                                    ))}
                                </Select>
                            </GridItem>
                            <GridItem {...yearGridItemStyles}>
                                <Select
                                    {...styles.expirationYearSelect}
                                    label={translate("Expiration Year")}
                                    value={expirationYear}
                                    onChange={onExpirationYearChange}
                                    required
                                    data-test-selector="checkoutReviewAndSubmit_expirationYear"
                                    ref={expirationYearRef}
                                >
                                    {availableYears.map(year => (
                                        <option key={year.value} value={year.value}>
                                            {year.key}
                                        </option>
                                    ))}
                                </Select>
                            </GridItem>
                            {variation === "onePageCheckout" && (
                                <GridItem {...securityCodeGridItemStyles}>{securityCodeInput}</GridItem>
                            )}
                        </GridContainer>
                    </GridItem>
                    {!variation && <GridItem {...securityCodeGridItemStyles}>{securityCodeInput}</GridItem>}
                    <Modal
                        headline={securityCodeLabel}
                        isOpen={isSecurityCodeModalOpen}
                        handleClose={handleSecurityCodeModalClose}
                        {...styles.securityCodeHelpModal}
                    >
                        <SecurityCodeImageWrapper {...styles.securityCodeHelpModalImageWrapper}>
                            <LazyImage
                                src="/images/security_code_sample.jpg"
                                imgProps={{ width: 660, height: 264 }}
                                altText={translate("Location of security code on card")}
                                {...styles.securityCodeHelpImage}
                            />
                        </SecurityCodeImageWrapper>
                    </Modal>
                </>
            )}
        </GridContainer>
    );
};

type TokenExCardTypeDisplayProps = TypographyProps & {
    possibleCardType: string;
};

const TokenExCardTypeDisplay = ({ possibleCardType, ...otherProps }: TokenExCardTypeDisplayProps) => {
    let cardTypeDisplay = "";
    if (possibleCardType === "masterCard") {
        cardTypeDisplay = "Mastercard";
    } else if (possibleCardType === "americanExpress") {
        cardTypeDisplay = "American Express";
    } else if (possibleCardType === "discover") {
        cardTypeDisplay = "Discover";
    } else if (possibleCardType === "visa") {
        cardTypeDisplay = "Visa";
    }
    return <Typography {...otherProps}>{cardTypeDisplay}</Typography>;
};

const SpreedlyCardTypeDisplay = ({ possibleCardType, ...otherProps }: TokenExCardTypeDisplayProps) => {
    return <Typography {...otherProps}>{convertSpreedlyCardType(possibleCardType)}</Typography>;
};

export default forwardRef(CreditCardDetailsEntry);
