import useAppSelector from "@insite/client-framework/Common/Hooks/useAppSelector";
import type { SpreedlyConfig } from "@insite/client-framework/Services/SettingsService";
import { useEffect, useState } from "react";

export type SpreedlyIframeStyles = {
    base?: string;
};

export type SpreedlyCreditCardData = {
    first_name?: string;
    last_name?: string;
    full_name?: string;
    month?: number;
    year?: number;
    email?: string;
    address1?: string;
    address2?: string;
    city?: string;
    state?: string;
    zip?: string;
    country?: string;
    phone_number?: string;
    company?: string;
    shipping_address1?: string;
    shipping_address2?: string;
    shipping_city?: string;
    shipping_state?: string;
    shipping_zip?: string;
    shipping_country?: string;
    shipping_phone_number?: string;
    eligible_for_card_updater?: boolean;
    metadata?: Record<string, any>;
};

export type BillingAddressData = {
    firstName?: string;
    lastName?: string;
    email?: string;
    address1?: string;
    address2?: string;
    city?: string;
    state?: string;
    zip?: string;
    country?: string;
    phoneNumber?: string;
    company?: string;
};

type TokenData = {
    card_type: string;
    first_name: string;
    first_six_digits: string;
    full_name: string;
    issuer_identification_number: string;
    last_four_digits: string;
    token: string;
};

export type SpreedlyIFrame = {
    setup: (
        environmentKeyOrConfig: string | SpreedlyConfig,
        creditCardNumber: string,
        secureNumber: string,
        style: SpreedlyIframeStyles | undefined,
        callback: () => void,
    ) => void;
    setupCvvOnly: (
        environmentKeyOrConfig: string | SpreedlyConfig,
        token: string,
        cardType: string,
        secureNumber: string,
        style: SpreedlyIframeStyles | undefined,
        onReady: () => void,
    ) => void;
    tokenize: (requiredFields: SpreedlyCreditCardData) => void;
    recache: () => void;
    validate: () => void;
    onValidation: (callback: (data: CardInfo) => void) => void;
    onError: (callback: (errors: string) => void) => void;
    onTokenize: (callback: (token: string, tokenData: TokenData) => void) => void;
    onRecache: (callback: (token: string, tokenData: TokenData) => void) => void;
    onCardTypeChanged: (callback: (cardType: string) => void) => void;
    isValid: () => boolean;
    getBrowserInfo: () => string;
    startThreeDSLifecycle: (transactionToken: string, successCallback: () => void, failedCallback: () => void) => void;
    isSpreedly: () => boolean;
};

declare let Spreedly: SpreedlyType | null;

type LifecycleConstructor = new (config: LifecycleConfig) => Lifecycle;

interface ThreeDS {
    serialize: (browserSize: string, acceptHeader: string) => string;
    Lifecycle: LifecycleConstructor; // This now explicitly has a construct signature
}

export type SpreedlyType = {
    init: (environmentKey: string, config: any) => void;
    on: {
        (eventName: "ready" | "errors" | "validation", callback: (data: any) => void): void;
        (
            eventName: "fieldEvent",
            callback: (name: string, type: string, activeEl: any, inputProperties: CardInfo) => void,
        ): void;
        (eventName: "paymentMethod" | "recache", callback: (token: string, paymentMethod: TokenData) => void): void;
        (eventName: "3ds:status", callback: (threeDSStatusEvent: any) => void): void;
    };
    validate: () => void;
    tokenizeCreditCard: (requiredFields: SpreedlyCreditCardData) => void;
    setStyle: (name: string, value: string) => void;
    setFieldType: (name: string, type: string) => void;
    setNumberFormat: (format: string) => void;
    removeHandlers: () => void;
    ThreeDS: ThreeDS;
    recache: () => void;
    setRecache: (token: string, options: any) => void;
};

type LifecycleConfig = {
    environmentKey: string;
    hiddenIframeLocation: string;
    challengeIframeLocation: string;
    transactionToken: string;
    challengeIframeClasses?: string;
};

type Lifecycle = {
    constructor: () => Lifecycle;
    start: () => void;
};

type CardInfo = {
    cardType: string;
    validNumber: boolean;
    validCvv: boolean;
    numberLength: number;
    cvvLength: number;
    iin: string;
};

type ErrorItem = {
    attribute: string;
    key: string;
    message: string;
};

type ErrorArray = ErrorItem[];

export type ThreeDSModalStyles = {
    fitToModal?: string;
};

const defaultThreeDSModalStyles: ThreeDSModalStyles = {
    fitToModal: `
        width: 500px;
        height: 600px;
        border: 1px solid #000;
        background: #fff;
    `,
};

export const useSpreedlyFrame = (customThreeDSModalStyles?: ThreeDSModalStyles) => {
    const websiteSettings = useAppSelector(state => state.context.settings.settingsCollection.websiteSettings);
    const [isSpreedlyScriptLoaded, setIsSpreedlyScriptLoaded] = useState(false);

    useEffect(() => {
        if (websiteSettings.useSpreedlyDropIn) {
            const script = document.createElement("script");
            script.src = "https://core.spreedly.com/iframe/iframe-v1.min.js";
            script.async = true;
            script.onload = () => {
                setIsSpreedlyScriptLoaded(true);
            };

            document.body.appendChild(script);
            const threeDSDivs = document.createElement("div");
            threeDSDivs.innerHTML = `<div id="spreedly-device-fingerprint" style="display: none"></div>
                <div id="spreedly-challenge-modal" style="display: none; justify-content: center; align-items: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 2000; background-color: rgba(0, 0, 0, 0.5);">
                    <div id="spreedly-challenge"></div>
                </div>`;
            document.body.insertAdjacentElement("afterbegin", threeDSDivs);
            const threeDSStyles = document.createElement("style");
            const fitToModalStyles = customThreeDSModalStyles?.fitToModal || defaultThreeDSModalStyles.fitToModal;

            threeDSStyles.innerHTML = `.fitToModal {
                    ${fitToModalStyles}
                }`;
            document.body.insertAdjacentElement("afterbegin", threeDSStyles);

            return () => {
                document.body.removeChild(script);
                document.body.removeChild(threeDSDivs);
                document.body.removeChild(threeDSStyles);
            };
        }
    }, []);

    return isSpreedlyScriptLoaded;
};

const threeDsCallbacks = {
    environmentKey: "",
    success: () => {},
    failed: () => {},
};

const SpreedlyCardMaping = [
    { type: "visa", name: "Visa" },
    { type: "master", name: "Mastercard" },
    { type: "american_express", name: "American Express" },
    { type: "diners_club", name: "Diners Club" },
    { type: "discover", name: "Discover" },
    { type: "confiable", name: "Confiable" },
    { type: "jcb", name: "JCB" },
    { type: "maestro", name: "Maestro" },
    { type: "dankort", name: "Dankort" },
    { type: "carnet", name: "Carnet" },
    { type: "elo", name: "Elo" },
    { type: "alelo", name: "Alelo" },
    { type: "cabal", name: "Cabal" },
    { type: "naranja", name: "Naranja" },
    { type: "creditel", name: "Creditel" },
    { type: "unionpay", name: "UnionPay" },
    { type: "alia", name: "Alia" },
    { type: "synchrony", name: "Synchrony" },
    { type: "routex", name: "Routex" },
    { type: "mada", name: "Mada" },
    { type: "passcard", name: "Passcard" },
    { type: "edenred", name: "Edenred" },
    { type: "anda", name: "Anda" },
    { type: "tarjeta_d", name: "Tarjeta_d" },
    { type: "sodexo", name: "Sodexo" },
    { type: "panal", name: "Panal" },
    { type: "verve", name: "Verve" },
    { type: "tuya", name: "Tuya" },
    { type: "uatp", name: "UATP" },
    { type: "patagonia_365", name: "Patagonia 365" },
    { type: "tarjeta_sol", name: "Tarjeta Sol" },
];

export const getSpreedlyFrame = () => {
    let validCardData = false;
    const setup = (
        environmentKeyOrConfig: string | SpreedlyConfig,
        creditCardNumber: string,
        secureNumber: string,
        style: SpreedlyIframeStyles | undefined,
        onReady: () => void,
    ) => {
        if (!Spreedly) {
            return;
        }

        Spreedly.removeHandlers();
        Spreedly.on("ready", () => {
            if (style?.base) {
                Spreedly.setStyle("number", style.base);
                Spreedly.setStyle("cvv", style.base);
            }

            Spreedly.setFieldType("number", "text");
            Spreedly.setNumberFormat("prettyFormat");
            onReady();
        });
        Spreedly.on("3ds:status", onThreeDsStatusUpdates);

        // Support both old (string) and new (config object) formats
        if (typeof environmentKeyOrConfig === "string") {
            // Backward compatibility - use old format without secure parameters
            Spreedly.init(environmentKeyOrConfig, { numberEl: creditCardNumber, cvvEl: secureNumber });
            threeDsCallbacks.environmentKey = environmentKeyOrConfig;
        } else {
            // New format with secure tokenization parameters
            const config = environmentKeyOrConfig;
            Spreedly.init(config.environmentKey, {
                numberEl: creditCardNumber,
                cvvEl: secureNumber,
                nonce: config.nonce,
                timestamp: config.timestamp,
                certificateToken: config.certificateToken,
                signature: config.signature,
            });
            threeDsCallbacks.environmentKey = config.environmentKey;
        }
    };

    const setupCvvOnly = (
        environmentKeyOrConfig: string | SpreedlyConfig,
        token: string,
        cardType: string,
        secureNumber: string,
        style: SpreedlyIframeStyles | undefined,
        onReady: () => void,
    ) => {
        if (!Spreedly) {
            return;
        }

        const fakeDiv = document.createElement("div");
        fakeDiv.innerHTML = `<div id="fake-spreedlyCardNumber" style="display: none"></div>`;
        document.body.append(fakeDiv);
        const spreedlyCardType =
            SpreedlyCardMaping.find(o => o.name.toLowerCase() === cardType.toLowerCase())?.type ||
            cardType.toLowerCase().replace(" ", "_");
        Spreedly.removeHandlers();
        Spreedly.on("ready", () => {
            Spreedly.setRecache(token, {
                card_type: spreedlyCardType,
            });

            if (style?.base) {
                Spreedly.setStyle("cvv", style.base);
            }

            onReady();
        });
        Spreedly.on("3ds:status", onThreeDsStatusUpdates);

        // Support both old (string) and new (config object) formats
        if (typeof environmentKeyOrConfig === "string") {
            // Backward compatibility - use old format without secure parameters
            Spreedly.init(environmentKeyOrConfig, { numberEl: "fake-spreedlyCardNumber", cvvEl: secureNumber });
            threeDsCallbacks.environmentKey = environmentKeyOrConfig;
        } else {
            // New format with secure tokenization parameters
            const config = environmentKeyOrConfig;
            Spreedly.init(config.environmentKey, {
                numberEl: "fake-spreedlyCardNumber",
                cvvEl: secureNumber,
                nonce: config.nonce,
                timestamp: config.timestamp,
                certificateToken: config.certificateToken,
                signature: config.signature,
            });
            threeDsCallbacks.environmentKey = config.environmentKey;
        }
    };

    const onThreeDsStatusUpdates = (threeDSStatusEvent: any) => {
        const challengeModal = document.getElementById("spreedly-challenge-modal")!;
        const challengeIframe = document.getElementById("spreedly-challenge")!;
        if (threeDSStatusEvent.action === "succeeded") {
            // finish your checkout and redirect to success page
            challengeModal.style.display = "none";
            challengeIframe.innerHTML = "";
            threeDsCallbacks.success();
        } else if (threeDSStatusEvent.action === "error") {
            // present an error to the user to retry
            challengeModal.style.display = "none";
            challengeIframe.innerHTML = "";
            threeDsCallbacks.failed();
        } else if (threeDSStatusEvent.action === "finalization-timeout") {
            // present an error to the user to retry
            challengeModal.style.display = "none";
            challengeIframe.innerHTML = "";
            threeDsCallbacks.failed();
        } else if (threeDSStatusEvent.action === "challenge") {
            // show the challenge-modal
            challengeModal.style.display = "flex";
        }
    };

    const tokenize = (requiredFields: SpreedlyCreditCardData) => {
        Spreedly?.tokenizeCreditCard(requiredFields);
    };

    const recache = () => {
        Spreedly?.recache();
    };

    const validate = () => {
        Spreedly?.validate();
    };

    const onTokenize = (callback: (token: string, tokenData: TokenData) => void) => {
        Spreedly?.on("paymentMethod", (token, paymentMethod) => {
            callback(token, paymentMethod);
        });
    };

    const onRecache = (callback: (token: string, tokenData: TokenData) => void) => {
        Spreedly?.on("recache", (token, paymentMethod) => {
            callback(token, paymentMethod);
        });
    };

    const onError = (callback: (message: string) => void) => {
        Spreedly?.on("errors", (errors: ErrorArray) => {
            let errorMessage = "";
            for (let i = 0; i < errors.length; i++) {
                if (i !== 0) {
                    errorMessage += ", ";
                }
                errorMessage += `${errors[i]["message"]}`;
            }
            callback(errorMessage);
        });
    };

    const onValidation = (callback: (data: CardInfo) => void) => {
        Spreedly?.on("validation", (data: CardInfo) => {
            if (data.validNumber === undefined) {
                validCardData = data.validCvv;
            } else {
                validCardData = data.validNumber && data.validCvv;
            }
            callback(data);
        });
    };

    const onCardTypeChanged = (callback: (message: string) => void) => {
        Spreedly!.on("fieldEvent", function (name, type, activeEl, inputProperties) {
            if (inputProperties?.cardType) {
                if (inputProperties.validNumber === undefined) {
                    validCardData = inputProperties.validCvv;
                } else {
                    validCardData = inputProperties.validNumber && inputProperties.validCvv;
                }
                callback(inputProperties.cardType);
            }
        });
    };

    const isValid = () => validCardData;

    const getBrowserInfo = () => {
        return Spreedly!.ThreeDS.serialize("04", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    };

    const startThreeDSLifecycle = (
        transactionToken: string,
        successCallback: () => void,
        failedCallback: () => void,
    ) => {
        threeDsCallbacks.success = successCallback;
        threeDsCallbacks.failed = failedCallback;
        if (Spreedly) {
            Spreedly.removeHandlers();
            Spreedly.on("3ds:status", onThreeDsStatusUpdates);
        }

        const lifecycle = new Spreedly!.ThreeDS.Lifecycle({
            environmentKey: threeDsCallbacks.environmentKey,

            // The DOM node that you'd like to inject hidden iframes
            hiddenIframeLocation: "spreedly-device-fingerprint", // (required)

            // The DOM node that you'd like to inject the challenge flow
            challengeIframeLocation: "spreedly-challenge", // (required)

            // The token for the transaction - used to poll for state
            transactionToken, // (required)

            // The css classes that you'd like to apply to the challenge iframe.
            challengeIframeClasses: "fitToModal", // (optional)
        });

        lifecycle.start();
    };

    const isSpreedly = () => {
        if (typeof Spreedly === "undefined") {
            return false;
        }
        return true;
    };

    const spreedlyIFrame: SpreedlyIFrame = {
        setup,
        setupCvvOnly,
        tokenize,
        recache,
        validate,
        onTokenize,
        onRecache,
        onError,
        onValidation,
        onCardTypeChanged,
        isValid,
        getBrowserInfo,
        startThreeDSLifecycle,
        isSpreedly,
    };

    return spreedlyIFrame;
};
