import { createRoot } from "react-dom/client";

import type { ApplyInteractiveEvent, OptInAllEvent } from "@devowl-wp/cookie-consent-web-client";
import {
    APPLY_INTERACTIVE_EVENT,
    BANNER_PRE_DECISION_SHOW_EVENT,
    OPT_IN_ALL_EVENT,
    createTcfModelFromCookie,
    decideToUnblockTcfBlockedContent,
    transformTcfSrc,
} from "@devowl-wp/cookie-consent-web-client";
import {
    UnblockWatcher,
    applyJQueryEventInitiator,
    applyNativeEventListenerInitiator,
} from "@devowl-wp/headless-content-unblocker";
import type { StylesheetElementToggleEvent } from "@devowl-wp/react-cookie-banner";
import { STYLESHEET_ELEMENT_TOGGLE_EVENT, getSsrPoweredByLink } from "@devowl-wp/react-cookie-banner";
import { yieldLazyLoad, yieldMainThread } from "@devowl-wp/react-utils";

import { getOtherOptionsFromWindow } from "../../utils/getOtherOptionsFromWindow.js";
import { customUnblockTriggers } from "../comp/customTriggers.js";
import { applyContentBlockRatioCssPlainCss } from "../comp/ratioClassPlainCss.js";
import { getCookieConsentManager } from "../getCookieConsentManager.js";

import type { BlockerDefinition } from "../../types/blocker.js";

/**
 * Listen to opt-in of all cookies so we can enable the blocked content or
 * create an visual content blocker. It also listens if no / or invalid consent is given.
 */
function listenOptInForContentBlocker() {
    let acceptedCookies: OptInAllEvent["services"] = [];
    let tcfData: ReturnType<typeof createTcfModelFromCookie>;
    let tcfStringForVendors: string;
    const other = getOtherOptionsFromWindow();

    /**
     * Avoid the following error when no visual content blocker is available on the website:
     *
     * The resource banner-pro-blocker-ui.pro.js was preloaded using link preload but not used within a few
     * seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded
     * intentionally.
     */
    const WebsiteBlockerDynamicImport = import(
        /* webpackChunkName: "blocker-ui", webpackMode: "lazy-once" */ "../../components/websiteBlocker.js"
    ).then(({ WebsiteBlocker }) => WebsiteBlocker);

    const {
        frontend: { blocker: allBlocker },
        visualParentSelectors,
        multilingualSkipHTMLForTag,
        dependantVisibilityContainers,
        disableDeduplicateExceptions,
        pageRequestUuid4,
    } = other;

    const watcher = new UnblockWatcher<BlockerDefinition>({
        checker: (by, consentTransport, blockerId) => {
            const blocker = allBlocker.filter(({ id }) => id === blockerId)?.[0];

            if (by === "services" || !by) {
                for (const {
                    service: { id },
                } of acceptedCookies) {
                    if (consentTransport[`${id}`]) {
                        consentTransport[`${id}`] = {
                            consent: true,
                        };
                    }
                }
            }

            if (process.env.IS_TCF === "1" && by === "tcfVendors") {
                if (!tcfData) {
                    // No consent given yet, fallback to our decision (e.g. Country Bypass and GDPR does not apply for TCF)
                    const tcfAllowAll = !!getCookieConsentManager().getUserDecision(true);
                    for (const item of Object.values(consentTransport)) {
                        item.consent = tcfAllowAll;
                    }
                } else {
                    decideToUnblockTcfBlockedContent(tcfData.model, tcfData.gvl, consentTransport);
                }
            }

            return {
                blocker,
            };
        },
        overwriteAttributeValue: (value, originalAttribute) => {
            let newValue = value;
            if (process.env.IS_TCF === "1" && originalAttribute === "src") {
                tcfStringForVendors = tcfStringForVendors || tcfData?.tcfStringForVendors();
                newValue = transformTcfSrc(newValue, tcfStringForVendors, tcfData?.gvl);
            }

            return { value: newValue };
        },
        overwriteAttributeNameWhenMatches: [
            {
                // [Plugin Comp] LayTheme
                matches: ".type-video>.video>.ph>%s",
                node: "iframe",
                attribute: "data-src",
                to: "src",
            },
            {
                // [Plugin Comp] Perfmatters lazy loading
                matches: `[data-ll-status="loading"]`,
                node: "iframe",
                attribute: "data-src",
                to: "src",
            },
        ],
        transactionClosed: (unblockedNodes) => {
            customUnblockTriggers(unblockedNodes);
        },
        visual: {
            visualParentSelectors,
            dependantVisibilityContainers,
            disableDeduplicateExceptions,
            unmount: (contentBlocker) => {
                ((contentBlocker as any).reactRoot as ReturnType<typeof createRoot>)?.unmount();
            },
            busy: (contentBlocker) => {
                contentBlocker.style.pointerEvents = "none";
                contentBlocker.style.opacity = "0.4";
            },
            mount: ({ container, blocker, onClick, thumbnail, paintMode, blockedNode, createBefore }) => {
                // Do not translate this content blocker with an output buffer plugin like TranslatePress or Weglot
                if (multilingualSkipHTMLForTag) {
                    container.setAttribute(multilingualSkipHTMLForTag, "1");
                }

                const blockerDef: BlockerDefinition = {
                    ...blocker,
                    visualThumbnail: thumbnail || blocker.visualThumbnail,
                };

                container.classList.add("wp-exclude-emoji");

                const WebsiteBlocker = yieldLazyLoad(WebsiteBlockerDynamicImport);

                // Yield main thread
                const root = createRoot(container);
                root.render(
                    <WebsiteBlocker
                        container={container}
                        blockedNode={blockedNode}
                        createBefore={createBefore}
                        poweredLink={getSsrPoweredByLink(`${pageRequestUuid4}-powered-by`)}
                        blocker={blockerDef}
                        paintMode={paintMode}
                        setVisualAsLastClickedVisual={onClick}
                    />,
                );

                (container as any).reactRoot = root;
            },
        },
        customInitiators: (ownerDocument, defaultView) => {
            // [Plugin comp]: TODO: extract to configuration
            applyNativeEventListenerInitiator(ownerDocument, "gform/postRender"); // Gravity Forms
            applyJQueryEventInitiator(ownerDocument, defaultView, "elementor/frontend/init");
            applyJQueryEventInitiator(ownerDocument, defaultView, "tcb_after_dom_ready"); // Thrive Architect
            applyJQueryEventInitiator(ownerDocument, ownerDocument, "mylisting/single:tab-switched");
            applyJQueryEventInitiator(ownerDocument, ownerDocument, "hivepress:init"); // HivePress
            applyJQueryEventInitiator(ownerDocument, ownerDocument, "wpformsReady"); // WPForms
            applyJQueryEventInitiator(ownerDocument, ownerDocument, "tve-dash.load", {
                // Mark requests as already sent as blocked modules in Thrive never gets loaded again
                onBeforeExecute: () => {
                    const { TVE_Dash } = window as any;
                    TVE_Dash.ajax_sent = true;
                },
            }); // Thrive Leads
        },
        delegateClick: {
            same: [
                // Ultimate Video (WP Bakery Page Builder)
                ".ultv-video__play",
                // Elementor
                ".elementor-custom-embed-image-overlay",
                // Themify
                ".tb_video_overlay",
                // Premium Addons for Elementor
                ".premium-video-box-container",
                // https://themeforest.net/item/norebro-creative-multipurpose-wordpress-theme/20834703
                ".norebro-video-module-sc",
                // WP Video Lightbox
                'a[rel="wp-video-lightbox"]',
                // WP YouTube Lyte
                '[id^="lyte_"]',
                // https://github.com/paulirish/lite-youtube-embed
                "lite-youtube",
                // https://github.com/luwes/lite-vimeo-embed
                "lite-vimeo",
                // https://avada.theme-fusion.com/design-elements/lightbox-element/
                ".awb-lightbox",
                // Impreza (WP Bakery Page Builder)
                ".w-video-h",
                // https://themenectar.com/salient/
                ".nectar_video_lightbox",
            ],
            nextSibling: [
                // JetElements for Element
                ".jet-video__overlay",
                // Elementor
                ".elementor-custom-embed-image-overlay",
                // BeaverBuilder PowerPack Videos
                ".pp-video-image-overlay",
                // Oxygen
                ".ou-video-image-overlay",
            ],
            parentNextSibling: [
                // Divi Page Builder
                {
                    selector: ".et_pb_video_overlay",
                    hide: true,
                },
            ],
        },
    });

    document.addEventListener(APPLY_INTERACTIVE_EVENT, (({
        detail: { services },
    }: CustomEvent<ApplyInteractiveEvent>) => {
        acceptedCookies = services;
    }) as any);

    document.addEventListener(OPT_IN_ALL_EVENT, (({ detail: { services } }: CustomEvent<OptInAllEvent>) => {
        acceptedCookies = services;

        if (process.env.IS_TCF === "1") {
            // Access lazily through Proxy
            const { tcf, tcfMetadata } = other.frontend;
            tcfData = createTcfModelFromCookie(tcf, tcfMetadata, getCookieConsentManager().getOption("tcfCookieName"));
        }

        yieldMainThread().then(() => watcher.start());
    }) as any);

    document.addEventListener(BANNER_PRE_DECISION_SHOW_EVENT, (() => {
        acceptedCookies = [];
        watcher.start();
    }) as any);

    let appliedRatioCss = false;
    document.addEventListener(STYLESHEET_ELEMENT_TOGGLE_EVENT, (async ({
        detail: {
            stylesheet: {
                isExtension,
                settings: { reuse },
            },
            active,
        },
    }: CustomEvent<StylesheetElementToggleEvent>) => {
        if (active && !appliedRatioCss && !isExtension && reuse === "react-cookie-banner") {
            applyContentBlockRatioCssPlainCss();
            appliedRatioCss = true;
        }
    }) as any);
}

export { listenOptInForContentBlocker };
