import type { OptInContentBlockerAllEvent, OptInContentBlockerEvent } from "@devowl-wp/headless-content-unblocker";
import {
    HTML_ATTRIBUTE_COOKIE_IDS,
    HTML_ATTRIBUTE_GOT_CLICKED,
    HTML_ATTRIBUTE_GOT_CLICKED_VALUE,
    OPT_IN_CONTENT_BLOCKER,
    OPT_IN_CONTENT_BLOCKER_ALL,
} from "@devowl-wp/headless-content-unblocker";
import { idx } from "@devowl-wp/react-utils";

import { getOtherOptionsFromWindow } from "../../utils/getOtherOptionsFromWindow.js";

type Handler = {
    className: string;
    delay?: number;
    optIn?: (
        $element: JQuery<HTMLDivElement>,
        detail: Pick<OptInContentBlockerEvent, "gotClicked">,
        handler: any,
    ) => void;
};

const MEMORIZE_ELEMENTOR_HANDLER_PROPERTY = "rcb-overwritten";

function overwrite(prototype: any, { delay, optIn }: Handler) {
    const { onInit, [MEMORIZE_ELEMENTOR_HANDLER_PROPERTY]: already } = prototype;

    if (already) {
        return;
    }
    prototype[MEMORIZE_ELEMENTOR_HANDLER_PROPERTY] = true;

    // `onInit` is called for each element, even it is blocked
    prototype.onInit = function (...args: any[]) {
        const $element = this.$element as JQuery<HTMLDivElement>;
        const element = $element.get(0);

        if ($element.attr(HTML_ATTRIBUTE_COOKIE_IDS)) {
            $element.attr(MEMORIZE_ELEMENTOR_HANDLER_PROPERTY, "1");
            element.addEventListener(OPT_IN_CONTENT_BLOCKER, (({ detail }: CustomEvent<OptInContentBlockerEvent>) => {
                optIn?.($element, detail, this);
            }) as any);
            element.addEventListener(OPT_IN_CONTENT_BLOCKER_ALL, ((e: CustomEvent<OptInContentBlockerAllEvent>) => {
                setTimeout(() => onInit.apply(this, args), delay || 0);
            }) as any);
        } else {
            optIn?.(
                $element,
                { gotClicked: $element.attr(HTML_ATTRIBUTE_GOT_CLICKED) === HTML_ATTRIBUTE_GOT_CLICKED_VALUE },
                this,
            );
            return onInit.apply(this, args);
        }
    };
}

/**
 * Overwrite Elementor modules and wait until consent is given for blocked module.
 * Passed `handlers` can be widget names like `video.default` or class names `VideoPlaylistHandler`
 * for dynamic modules.
 */
function hijackElementorOnInit(handlers: Array<Handler>) {
    const { wrapFn, consent } = window.consentApi;
    wrapFn(
        {
            // See https://github.com/elementor/elementor/blob/2ee675caf03908f8c66e16298e923a24b206df54/assets/dev/js/frontend/frontend.js#L319-L322
            object: () => idx(window as any, (window) => window.elementorFrontend),
            key: "initModules",
        },
        ({ objectResolved }) => {
            wrapFn(
                {
                    object: objectResolved.elementsHandler,
                    key: "addHandler",
                },
                ({ args: [handlerClass] }) => {
                    for (const handler of handlers) {
                        if (handlerClass.name === handler.className) {
                            overwrite(handlerClass.prototype, handler);
                        }
                    }
                    return true;
                },
            );

            // [Plugin Comp] https://github.com/kobizz/dialogs-manager (used by Elementor)
            // When using a Elementor Popup which also can be closed through a "outside click",
            // it will also be closed when the cookie banner is visible and clicking on "Accept all".
            // We need to prevent this by ignoring our cookie banner in the "outside click" handler.
            wrapFn(
                {
                    object: objectResolved,
                    key: "getDialogsManager",
                },
                ({ callOriginal }) => {
                    const dialogInstance = callOriginal();

                    // https://github.com/kobizz/dialogs-manager/blob/01067d16ae3db87d4e86ab3b84b1248122819725/src/js/dialogs-manager.js#L85-L95
                    wrapFn(
                        {
                            object: dialogInstance,
                            key: "createWidget",
                        },
                        ({ original, args: [widgetType, properties = {}], that }) => {
                            // Add ignore statement: https://github.com/kobizz/dialogs-manager/blob/01067d16ae3db87d4e86ab3b84b1248122819725/src/js/dialogs-manager.js#L255-L261
                            const ignore = `#${
                                getOtherOptionsFromWindow().pageRequestUuid4
                            },.rcb-db-container,.rcb-db-overlay`;
                            properties.hide = properties.hide || {};
                            const { hide } = properties;
                            hide.ignore = hide.ignore || "";
                            hide.ignore = [...hide.ignore.split(","), ignore].join(",");

                            const widgetInstance = original.apply(that, [widgetType, properties]);
                            return widgetInstance;
                        },
                    );
                    return dialogInstance;
                },
            );

            return true;
        },
    );

    // [Plugin Comp] Elementor PRO (https://github.com/elementor/elementor/issues/7151#issuecomment-1576438684)
    wrapFn(
        {
            object: () => idx(window as any, (window) => window.elementorProFrontend),
            key: "initModules",
        },
        () => {
            wrapFn(
                {
                    object: () => idx(window as any, (window) => window.elementorFrontend.storage),
                    key: "set",
                },
                ({ args: [key] }) => {
                    if (key) {
                        return consent("local", "elementor", "*");
                    }
                    return undefined;
                },
            );

            return true;
        },
    );
}

export { hijackElementorOnInit, MEMORIZE_ELEMENTOR_HANDLER_PROPERTY };
