import {
    getSpreedlyFrame,
    SpreedlyIFrame,
    SpreedlyIframeStyles,
    SpreedlyType,
} from "@insite/client-framework/Common/Hooks/useSpreedlyFrame";
import {
    IFrame,
    TokenEx as TokenExType,
    TokenExCvvOnlyIframeConfig,
    TokenExIframeStyles,
} 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 logger from "@insite/client-framework/Logger";
import { SpreedlyConfig, TokenExConfig } from "@insite/client-framework/Services/SettingsService";
import translate from "@insite/client-framework/Translate";
import { PaymentMethodDto } from "@insite/client-framework/Types/ApiModels";
import { convertApiDataToTokenExCardType } from "@insite/content-library/Widgets/SavedPayments/PaymentUtilities";
import LazyImage, { LazyImageProps } from "@insite/mobius/LazyImage";
import Link from "@insite/mobius/Link";
import Modal, { ModalPresentationProps } from "@insite/mobius/Modal";
import SpreedlyFrame, { SpreedlyFramePresentationProps } from "@insite/mobius/SpreedlyFrame";
import TextField, { TextFieldPresentationProps } from "@insite/mobius/TextField";
import TokenExFrame, { TokenExFramePresentationProps } from "@insite/mobius/TokenExFrame";
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 {
    iframe?: "TokenEx" | "Paymetric" | "Square" | "Paystand" | "Spreedly";
    extendedStyles?: SavedPaymentProfileEntryStyles;
    onSecurityCodeUpdate: (value: string) => void;
    tokenExConfig?: TokenExConfig;
    tokenExFrameStyleConfig?: TokenExIframeStyles;
    spreedlyConfig?: SpreedlyConfig;
    spreedlyFrameStyleConfig?: SpreedlyIframeStyles;
    paymentMethodDto: PaymentMethodDto;
    updateCardIsTokenized?: (value: boolean) => void;
}

export interface SavedPaymentProfileEntryStyles {
    text?: TextFieldPresentationProps;
    tokenExFrameWrapper?: InjectableCss;
    tokenExFrame?: TokenExFramePresentationProps;
    securityCodeHelpModal?: ModalPresentationProps;
    securityCodeHelpModalImageWrapper?: InjectableCss;
    securityCodeHelpImage?: LazyImageProps;
    securityCodeSpreedlyFrameWrapper?: InjectableCss;
    securityCodeSpreedlyFrame?: SpreedlyFramePresentationProps;
}

export const savedPaymentProfileEntryStyles: SavedPaymentProfileEntryStyles = {
    tokenExFrameWrapper: {
        css: css`
            height: inherit;
        `,
    },
    securityCodeHelpModalImageWrapper: {
        css: css`
            display: flex;
            justify-content: center;
        `,
    },
    securityCodeSpreedlyFrameWrapper: {
        css: css`
            height: 40px;
        `,
    },
};

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

declare const TokenEx: TokenExType;
let tokenExIframe: IFrame;

declare const Spreedly: SpreedlyType;
let spreedlyIFrame: SpreedlyIFrame | undefined;

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

const SavedPaymentProfileEntry = (
    {
        iframe,
        onSecurityCodeUpdate,
        extendedStyles,
        tokenExConfig,
        tokenExFrameStyleConfig,
        spreedlyConfig,
        spreedlyFrameStyleConfig,
        paymentMethodDto,
        updateCardIsTokenized,
    }: OwnProps,
    ref: Ref<SavedPaymentProfileValidation>,
) => {
    const [styles] = useState(() => mergeToNew(savedPaymentProfileEntryStyles, extendedStyles));
    const [isSecurityCodeModalOpen, setIsSecurityCodeModalOpen] = useState(false);
    const [isTokenExIframeLoaded, setIsTokenExIframeLoaded] = useState(false);
    const [securityCode, setSecurityCode] = useState("");
    const [securityCodeError, setSecurityCodeError] = useState("");
    const securityCodeRef = useRef<HTMLInputElement>(null);
    const [spreedlyReady, setSpreedlyReady] = useState(false);

    if (iframe === "Square") {
        return null;
    }

    const onSecurityCodeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setSecurityCode(event.currentTarget.value);
        onSecurityCodeUpdate(event.currentTarget.value);
    };

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

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

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

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

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

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

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

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

        const iframeConfig: TokenExCvvOnlyIframeConfig = {
            authenticationKey: config.authenticationKey,
            cardType: convertApiDataToTokenExCardType(paymentMethodDto!.cardType),
            cvv: true,
            cvvOnly: true,
            inputType: "text",
            origin: config.origin,
            styles: tokenExFrameStyleConfig,
            timestamp: config.timestamp,
            token: paymentMethodDto!.name,
            tokenExID: config.tokenExId,
            tokenScheme: paymentMethodDto!.tokenScheme,
            enableAriaRequired: true,
            title: translate("Security code"),
            customDataLabel: translate("Security code"),
        };

        tokenExIframe = new TokenEx.Iframe("ppTokenExSecurityCode", iframeConfig);
        tokenExIframe.load();
        tokenExIframe.on("load", _ => setIsTokenExIframeLoaded(true));
        tokenExIframe.on("tokenize", _ => updateCardIsTokenized?.(true));
        tokenExIframe.on("validate", data => {
            if (data.isValid) {
                setSecurityCodeError("");
            } else {
                if (data.validator === "required") {
                    setSecurityCodeError(translate("Security code is required."));
                }
                if (data.validator === "format") {
                    setSecurityCodeError(translate("Security code is invalid."));
                }
            }
        });
        tokenExIframe.on("error", data => {
            logger.error(data);
            if (
                Date.now() - lastTokenExReinitTime > tokenExReinitTimeout &&
                ++currentTokenExErrorCount <= tokenExErrorsLimit
            ) {
                lastTokenExReinitTime = Date.now();
                setTimeout(() => {
                    setUpTokenExIFrameCvvOnly(config);
                }, tokenExReinitTimeout);
            }
        });
    };

    useEffect(() => {
        if (typeof Spreedly === "undefined" || !spreedlyConfig) {
            return;
        }

        spreedlyIFrame = getSpreedlyFrame();
        spreedlyIFrame.setupCvvOnly(
            spreedlyConfig,
            paymentMethodDto.name,
            paymentMethodDto.cardType,
            "spreedlySecurityCode",
            spreedlyFrameStyleConfig,
            () => {
                setSpreedlyReady(true);
            },
        );

        spreedlyIFrame.onRecache(() => {
            updateCardIsTokenized?.(true);
        });
        spreedlyIFrame.onError(message => {
            setSecurityCodeError(message);
        });
        spreedlyIFrame.onValidation(data => {
            if (data.validCvv) {
                setSecurityCodeError("");
            } else if (data.cvvLength === 0) {
                setSecurityCodeError(translate("Security code is required."));
            } else {
                setSecurityCodeError(translate("Security code is invalid."));
            }
        });
        spreedlyIFrame.onCardTypeChanged(() => {});
    }, [spreedlyConfig, typeof Spreedly]);

    useImperativeHandle(
        ref,
        () => ({
            validate,
            tokenize,
        }),
        [resolvedShouldUseTokenEx, resolvedShouldUseSpreedly, securityCode],
    );

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

        if (resolvedShouldUseSpreedly && spreedlyIFrame?.isValid()) {
            spreedlyIFrame?.recache();
        }
    };

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

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

        const securityCodeResult = validateSecurityCode(securityCode);

        if (securityCodeResult.securityCodeEmpty || !securityCodeResult.securityCodeValid) {
            securityCodeRef.current?.focus();
        }

        return !securityCodeResult.securityCodeEmpty && securityCodeResult.securityCodeValid;
    };

    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 };
    };

    return (
        <>
            {resolvedShouldUseTokenEx ? (
                <TokenExFrame
                    {...styles.tokenExFrame}
                    label={translate("Security code")}
                    hint={securityCodeHint}
                    tokenExIFrameContainer={
                        <TokenExFrameWrapper {...styles.tokenExFrameWrapper} id="ppTokenExSecurityCode" />
                    }
                    disabled={!isTokenExIframeLoaded}
                    required
                    error={securityCodeError}
                    data-test-selector="checkoutReviewAndSubmit_securityCode"
                />
            ) : resolvedShouldUseSpreedly ? (
                <SpreedlyFrame
                    {...styles.securityCodeSpreedlyFrame}
                    label={translate("Security code")}
                    hint={securityCodeHint}
                    spreedlyIFrameContainer={
                        <SecurityCodeSpreedlyFrameWrapper
                            {...styles.securityCodeSpreedlyFrameWrapper}
                            id="spreedlySecurityCode"
                        />
                    }
                    disabled={!spreedlyReady}
                    required
                    error={securityCodeError}
                    data-test-selector="checkoutReviewAndSubmit_securityCode"
                />
            ) : (
                <TextField
                    {...styles.text}
                    label={translate("Security code")}
                    hint={securityCodeHint}
                    value={securityCode}
                    onChange={onSecurityCodeChange}
                    required
                    minLength={3}
                    maxLength={4}
                    error={securityCodeError}
                    data-test-selector="checkoutReviewAndSubmit_securityCode"
                    ref={securityCodeRef}
                />
            )}
            <Modal
                headline={securityCodeHelpLabel}
                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>
        </>
    );
};

export default forwardRef(SavedPaymentProfileEntry);
