import { getDefaultDecision, prepareTcfString } from "@devowl-wp/cookie-consent-web-client";
import { yieldMainThread } from "@devowl-wp/react-utils";

import { resetConsentChangeHash } from "./useBannerActionLinks.js";

import type { BannerContext } from "../../contexts/banner.js";

const cancelEvent = (e: MouseEvent) => {
    if (!e) {
        return;
    }

    e.preventDefault();

    // Some themes (e.g. AKEA) do have listeners on each link separately and do a custom redirect
    e.stopPropagation();
};

const toggleWithActiveAction = (
    contextValue: BannerContext["contextValue"],
    type: BannerContext["initialState"]["activeAction"],
): BannerContext["initialState"] =>
    Object.assign(contextValue, {
        activeAction: type,
        individualPrivacyOpen: true,
        refreshSiteAfterSave: type === "change" ? 2000 : false,
        visible: true,
    });

function useBannerStateContextCallbacks(): Pick<
    BannerContext["provider"]["modifiers"],
    "onClose" | "openBanner" | "openHistory" | "revokeConsent" | "onSave" | "updateCookieChecked" | "updateGroupChecked"
> {
    return {
        onClose: (state) => {
            Object.assign(state, {
                visible: false,
                // This should be set always by you again as it is one-time
                refreshSiteAfterSave: false,
            });
        },
        openHistory: (contextValue, e) => {
            toggleWithActiveAction(contextValue, "history");
            cancelEvent(e);
        },
        openBanner: (contextValue, e) => {
            toggleWithActiveAction(contextValue, "change");
            cancelEvent(e);
        },
        revokeConsent: ({ onPersistConsent, onApplyConsent, isTcf, tcf, isGcm, groups }, successMessage, e) => {
            // Do not rely on current state of `groups` because it can be altered through history change
            const buttonClicked = "shortcode_revoke";

            let tcfString: () => string;
            if (process.env.IS_TCF === "1" && isTcf && tcf?.model) {
                tcfString = prepareTcfString(tcf.model, buttonClicked);
            }

            const defaultConsent = getDefaultDecision(groups, true);

            onPersistConsent({
                consent: defaultConsent,
                gcmConsent: isGcm ? [] : undefined,
                buttonClicked,
                tcfString,
            })
                .then(() => onApplyConsent())
                .then(() => {
                    successMessage && alert(successMessage);

                    resetConsentChangeHash();

                    // Always reload (always wait 2s to ensure opt-out scripts are exeucted)
                    // Fix iOS / Safari bug when we want to reload the page after a POST request
                    setTimeout(() => window.location.reload(), 2000);
                });

            cancelEvent(e);
        },
        onSave: (contextValue, markAsDoNotTrack, buttonClicked) => {
            const { refreshSiteAfterSave } = contextValue;

            const appliedConsent = yieldMainThread().then(async () => {
                const {
                    onPersistConsent,
                    onApplyConsent,
                    activeAction,
                    consent,
                    tcf,
                    isTcf,
                    isGcm,
                    gcmConsent,
                    recorder,
                } = contextValue;

                // TCF compatibility: persist TCF string
                let tcfString: () => string;
                if (process.env.IS_TCF === "1" && isTcf && tcf?.model) {
                    tcfString = prepareTcfString(tcf.model, buttonClicked);
                    await yieldMainThread();
                }

                // Google Consent Mode compatibility, only allow
                let useGcmConsent: typeof gcmConsent;
                if (process.env.PLUGIN_CTX === "pro" && isGcm) {
                    switch (buttonClicked) {
                        case "ind_all":
                        case "ind_custom":
                        case "main_all":
                        case "main_custom":
                            useGcmConsent = gcmConsent;
                            break;
                        default:
                            useGcmConsent = [];
                            break;
                    }
                }

                return onPersistConsent({
                    consent,
                    gcmConsent: useGcmConsent,
                    markAsDoNotTrack,
                    buttonClicked,
                    tcfString,
                    recorderJsonString: recorder ? JSON.stringify(recorder.createReplay()) : undefined,
                    uiView: activeAction === "change" ? "change" : activeAction !== "revoke" ? "initial" : undefined,
                }).then(() => onApplyConsent());
            });

            if (refreshSiteAfterSave) {
                appliedConsent.then(() => {
                    resetConsentChangeHash();

                    // Fix iOS / Safari bug when we want to reload the page after a POST request
                    setTimeout(() => window.location.reload(), refreshSiteAfterSave || /* BC */ 2000);
                });
            } else {
                Object.assign(contextValue, { visible: false });
            }
        },
        updateCookieChecked: (contextValue, groupId, id, checked) => {
            const { consent, isGcm, groups, updateGcmConsentTypeChecked } = contextValue;
            if (!consent[groupId]) {
                consent[groupId] = [];
            }

            const cookieIds = consent[groupId];
            const indexOf = cookieIds.indexOf(id);
            if (checked && indexOf === -1) {
                cookieIds.push(id);
            } else if (!checked && indexOf > -1) {
                cookieIds.splice(indexOf, 1);
            }

            // Delete group entirely when no cookies selected
            if (!cookieIds.length) {
                delete consent[groupId];
            }

            /* onlypro:start */
            if (process.env.PLUGIN_CTX === "pro" && isGcm) {
                const acceptedServices = groups
                    .map(({ id: groupId, items }) =>
                        items.filter(({ id: serviceId }) => consent[groupId]?.indexOf(serviceId) > -1),
                    )
                    .flat();
                for (const gcmConsentType of groups
                    .find(({ id }) => id === groupId)
                    .items.find(({ id: serviceId }) => id === serviceId).googleConsentModeConsentTypes) {
                    if (checked) {
                        // Automatically check all GCM consent purposes whne a service got accepted
                        updateGcmConsentTypeChecked(gcmConsentType, true);
                    } else if (
                        // We only uncheck this consent purpose when it is not requested by another accepted service
                        !acceptedServices.some(({ googleConsentModeConsentTypes }) =>
                            googleConsentModeConsentTypes.includes(gcmConsentType),
                        )
                    ) {
                        updateGcmConsentTypeChecked(gcmConsentType, false);
                    }
                }
            }
            /* onlypro:end */
        },
        updateGroupChecked: (contextValue, groupId, checked) => {
            const { groups, updateCookieChecked } = contextValue;

            for (const item of groups.find(({ id }) => id === groupId).items) {
                updateCookieChecked(groupId, item.id, checked);
            }
        },
    };
}

export { useBannerStateContextCallbacks };
