import {
    darkenBgImage,
    fixBackgroundColorAlpha,
    handleDraculaFont,
    getConfig,
    handleImageBehaviour,
    handleVideoBehaviour,
    imageReplacements,
    invertInlineSvg,
    reloadDisqus,
    videoReplacements
} from "./functions";

const {isPro, settings = {}, isAdmin} = dracula;

let {
    darkenBackgroundImages = true,
    lowBrightnessImages = true,
    grayscaleImages = true,
    invertImages = true,
    invertInlineSVG = false,
    lowBrightnessVideos = true,
    grayscaleVideos = true,
    videos = [],
    excludes = ['.dracula-ignore'],
    hides = [],
    colorType = 'dynamic',
    showTooltip,
    lightTooltipText = 'Toggle Dark Mode',
    darkTooltipText = 'Toggle Light Mode',
} = settings;

// Add .dracula-ignore to excludes
if (!excludes.includes('.dracula-ignore')) {
    excludes.push('.dracula-ignore');
}

// Clean and combine for closest()
excludes = excludes
    .filter(Boolean)
    .map(s => s.trim())
    .join(', ');

const app = {

    secondaryBgColor: "",

    hasProcessRunsOnce: false,

    // MutationObserver to watch for changes in the DOM
    observer: new MutationObserver(function (mutationsList) {
        app.initProcesses();
    }),

    // MutationObserver to watch for changes in the dark mode state
    darkModeStateObserver: new MutationObserver((mutationsList) => {
        mutationsList.forEach((mutation) => {
            if (mutation.type === "attributes" && mutation.attributeName === "data-dracula-scheme") {

                const isDarkMode = document.documentElement.getAttribute("data-dracula-scheme") === "dark";

                if (isAdmin) {
                    localStorage.setItem("dracula_mode_admin", isDarkMode ? "dark" : "light");

                    setTimeout(() => {
                        app.handleToggleClass(isDarkMode);
                    }, 100);
                } else {

                    document
                        .querySelectorAll("*:not(head, title, link, meta, script, style, defs, filter, .dracula-ignore *)")
                        .forEach((element) => {

                            if (element.classList.contains("dracula-processed")) {

                                if (element.closest(excludes)) {
                                    return;
                                }

                                if (darkenBackgroundImages) {
                                    darkenBgImage(element);
                                }

                                if (element.nodeName.toLowerCase() === "img") {
                                    if ((lowBrightnessImages || grayscaleImages || invertImages)) {
                                        handleImageBehaviour(element);
                                    }
                                }

                                if (invertInlineSVG && element.nodeName.toLowerCase() === "svg") {
                                    invertInlineSvg(element);
                                }

                                if (lowBrightnessVideos || grayscaleVideos) {

                                    if (element.nodeName.toLowerCase() === "video") {
                                        handleVideoBehaviour(element);
                                    }

                                    if (element.nodeName.toLowerCase() === "iframe" && element.getAttribute("src") != null) {
                                        const srcAttribute = element.getAttribute("src");

                                        if (
                                            srcAttribute.includes("youtube") ||
                                            srcAttribute.includes("vimeo") ||
                                            srcAttribute.includes("dailymotion")
                                        ) {
                                            handleVideoBehaviour(element);
                                        }
                                    }
                                }

                                if (element.hasAttribute("data-dracula_alpha_bg")) {
                                    fixBackgroundColorAlpha(element);
                                }

                            }
                        });

                    setTimeout(() => {

                        // Reload disqus
                        reloadDisqus();

                        // Handle Image Replacements
                        imageReplacements();

                        // Handle Video Replacements
                        if (isPro && videos.length) {
                            videoReplacements();
                        }

                    }, 100);

                    localStorage.setItem("dracula_mode", isDarkMode ? "dark" : "light");
                }

                app.handleToggleClass(isDarkMode);

            }
        });
    }),

    elementsClassObserver: new MutationObserver((mutationsList) => {
        if (document.readyState === "loading") return;

        for (const mutation of mutationsList) {
            const target = mutation.target;

            if (!target.classList.contains("dracula-processed")) continue;

            const currentClassList = target.classList.toString();
            const preservedClasses = target.dataset.dracula_preserved_classes;

            if (preservedClasses && preservedClasses === currentClassList) continue;

            target.dataset.dracula_preserved_classes = currentClassList;

            // Reprocess the element
            app.elementsClassObserver.disconnect();
            target.classList.remove("dracula-processed");

            app.processElement(target);

            // Re-observe all elements (except excluded tags)
            document.querySelectorAll("*:not(head, title, link, meta, script, style, defs, filter)").forEach((element) => {
                app.elementsClassObserver.observe(element, {
                    attributes: true,
                    attributeFilter: ["class"],
                });
            });

            // Only reprocess the first matching mutation
            break;
        }
    }),

    initProcesses: () => {
        app.hasProcessRunsOnce = true;
        document.querySelectorAll('* :not(html, head, title, link, meta, script, style, defs, filter, .dracula-processed, mark, .dracula-ignore *)').forEach(app.processElement);
    },

    processElement: function (element) {
        const computedStyle = window.getComputedStyle(element, null);

        if (
            element.classList.contains("dracula-style-all") ||
            element.classList.contains("dracula-style-bg-txt") ||
            element.classList.contains("dracula-style-bg-border") ||
            element.classList.contains("dracula-style-txt-border") ||
            element.classList.contains("dracula-style-bg") ||
            element.classList.contains("dracula-style-txt") ||
            element.classList.contains("dracula-style-border") ||
            element.classList.contains("dracula-style-secondary-bg")
        ) {
            element.classList.remove("dracula-style-all");
            element.classList.remove("dracula-style-bg-txt");
            element.classList.remove("dracula-style-bg-border");
            element.classList.remove("dracula-style-txt-border");
            element.classList.remove("dracula-style-bg");
            element.classList.remove("dracula-style-txt");
            element.classList.remove("dracula-style-border");
            element.classList.remove("dracula-style-secondary-bg");
        }

        let nodeName = element.nodeName.toLowerCase();

        let backgroundColor = computedStyle.backgroundColor;
        let color = computedStyle.color;
        let borderColor = computedStyle.borderColor;
        let backgroundImage = computedStyle.backgroundImage;

        if (nodeName === "body" && (backgroundColor === "rgba(0, 0, 0, 0)" || backgroundColor === "rgba(255, 255, 255, 0)")) {
            element.style.setProperty("background-color", "rgb(255, 255, 255)");
            backgroundColor = window.getComputedStyle(element, null).backgroundColor;
        }

        // If the element is excluded, skip processing
        if (element.closest(excludes)) {
            return;
        }

        const hideSelectors = (hides || []).filter(s => s && s.trim() !== "");

        if (hideSelectors.length > 0 && element.matches(hideSelectors.join(", "))) {
            element.classList.add("dracula-style-hidden");
            element.classList.remove("dracula-processed");
            app.elementsClassObserver.disconnect();

            return;
        }

        let hasBackgroundImgURL = false;
        if (backgroundImage !== "none" && backgroundImage.includes("url")) {
            hasBackgroundImgURL = true;

            if (darkenBackgroundImages) {
                darkenBgImage(element);
            }
        }

        if (backgroundColor !== "rgba(0, 0, 0, 0)" && backgroundColor !== "rgba(255, 255, 255, 0)" && !hasBackgroundImgURL) {

            if (!element.hasAttribute("data-dracula_secondary_bg_finder")) {
                element.dataset.dracula_secondary_bg_finder = backgroundColor;
            }

            if (app.secondaryBgColor !== "") {
                let isSecondaryBgColorDifferent = app.secondaryBgColor !== element.dataset.dracula_secondary_bg_finder;
                if (isSecondaryBgColorDifferent) {
                    element.classList.add("dracula-style-secondary_bg");
                }

                delete element.dataset.dracula_secondary_bg_finder;
            }
        }

        if (
            backgroundColor !== "rgba(0, 0, 0, 0)" &&
            color !== "rgba(0, 0, 0, 0)" &&
            borderColor !== "rgba(0, 0, 0, 0)" &&
            backgroundColor !== "rgba(255, 255, 255, 0)" &&
            color !== "rgba(255, 255, 255, 0)" &&
            borderColor !== "rgba(255, 255, 255, 0)" &&
            hasBackgroundImgURL === false
        ) {
            element.classList.add("dracula-style-all");
        } else {
            if (
                backgroundColor !== "rgba(0, 0, 0, 0)" &&
                color !== "rgba(0, 0, 0, 0)" &&
                backgroundColor !== "rgba(255, 255, 255, 0)" &&
                color !== "rgba(255, 255, 255, 0)" &&
                hasBackgroundImgURL === false
            ) {
                element.classList.add("dracula-style-bg-txt");
            } else {
                if (
                    backgroundColor !== "rgba(0, 0, 0, 0)" &&
                    borderColor !== "rgba(0, 0, 0, 0)" &&
                    backgroundColor !== "rgba(255, 255, 255, 0)" &&
                    borderColor !== "rgba(255, 255, 255, 0)" &&
                    hasBackgroundImgURL === false
                ) {
                    element.classList.add("dracula-style-bg-border");
                } else {
                    if (
                        color !== "rgba(0, 0, 0, 0)" &&
                        borderColor !== "rgba(0, 0, 0, 0)" &&
                        color !== "rgba(255, 255, 255, 0)" &&
                        borderColor !== "rgba(255, 255, 255, 0)"
                    ) {
                        element.classList.add("dracula-style-txt-border");
                    } else {
                        if (
                            backgroundColor !== "rgba(0, 0, 0, 0)" &&
                            backgroundColor !== "rgba(255, 255, 255, 0)" &&
                            hasBackgroundImgURL === false
                        ) {
                            element.classList.add("dracula-style-bg");
                        } else {
                            if (
                                color !== "rgba(0, 0, 0, 0)" &&
                                color !== "rgba(255, 255, 255, 0)"
                            ) {
                                element.classList.add("dracula-style-txt");
                            } else if (
                                borderColor !== "rgba(0, 0, 0, 0)" &&
                                borderColor !== "rgba(255, 255, 255, 0)"
                            ) {
                                element.classList.add("dracula-style-border");
                            }
                        }
                    }
                }
            }
        }

        if (
            backgroundImage !== "none" &&
            !hasBackgroundImgURL &&
            !element.classList.contains("dracula-style-all") &&
            !element.classList.contains("dracula-style-bg-txt") &&
            !element.classList.contains("dracula-style-bg-border") &&
            !element.classList.contains("dracula-style-bg")
        ) {
            element.classList.add("dracula-style-bg");
        }

        // and no dracula-style-button ancestor
        if (nodeName === "a") {
            element.classList.add("dracula-style-link");
        }

        if (
            nodeName === "input" ||
            nodeName === "select" ||
            nodeName === "textarea"
        ) {
            element.classList.add("dracula-style-form-element");
        }

        // Treat the below classes as buttons
        // This is to ensure that the buttons are styled correctly
        const hasBtnClass = [
            "elementor-button",
            "gspb-buttonbox",
            "fusion-button",
            "wp-element-button",
            "ct-header-search",
            "stk-button"
        ].some(cls => element.classList.contains(cls));

        if (nodeName === "button" || hasBtnClass || element.type === "submit") {
            element.classList.add("dracula-style-button");
            element.classList.remove("dracula-style-secondary-bg");
            element.classList.remove("dracula-style-all");
            element.classList.remove("dracula-style-link");
        }

        // Handle Image Brightness & Inversion
        if ((lowBrightnessImages || grayscaleImages || invertImages) && element.nodeName.toLowerCase() === "img") {
            handleImageBehaviour(element);
        }

        if (invertInlineSVG && element.nodeName.toLowerCase() === "svg") {
            invertInlineSvg(element);
        }

        if (lowBrightnessVideos || grayscaleVideos) {

            if (element.nodeName.toLowerCase() === "video") {
                handleVideoBehaviour(element);
            }

            if (element.nodeName.toLowerCase() === "iframe" && element.getAttribute("src") != null) {
                const srcAttribute = element.getAttribute("src");

                if (
                    srcAttribute.includes("youtube") ||
                    srcAttribute.includes("vimeo") ||
                    srcAttribute.includes("dailymotion")
                ) {
                    handleVideoBehaviour(element);
                }
            }
        }

        if (backgroundColor.includes("rgba")) {
            element.dataset.dracula_alpha_bg = backgroundColor;
            fixBackgroundColorAlpha(element);
        }

        setTimeout(function () {
            app.elementsClassObserver.observe(element, {
                attributes: true,
                attributeFilter: ["class"],
            });
        }, 0);

        element.classList.add("dracula-processed");
    },

    initObserver: () => {

        app.observer.observe(document, {
            attributes: false,
            childList: true,
            characterData: false,
            subtree: true,
        });

        app.darkModeStateObserver.observe(document.documentElement, {
            attributes: true,
        });

        if (document.readyState !== "loading") {

            if (!app.hasProcessRunsOnce) {
                app.initProcesses();
            }

            app.implementSecondaryBg();
            app.recheckOnCssLoadedLater();

            app.enableDarkModeAfterLoad();

        } else {
            document.addEventListener("DOMContentLoaded", function () {

                if (!app.hasProcessRunsOnce) {
                    app.initProcesses();
                }

                app.implementSecondaryBg();
                app.recheckOnCssLoadedLater();

                app.enableDarkModeAfterLoad();

            });
        }

    },

    implementSecondaryBg: () => {
        if (colorType !== 'static') return;

        let maxAreaElement = null;
        let maxArea = 0;

        const elements = document.querySelectorAll("* :not(head, title, link, meta, script, style, defs, filter)");

        for (let i = 0; i < elements.length; i++) {
            const element = elements[i];
            if (element.hasAttribute("data-dracula_secondary_bg_finder")) {
                const secondaryBgColor = element.dataset.dracula_secondary_bg_finder;
                if (
                    secondaryBgColor !== "transparent" &&
                    secondaryBgColor !== "rgba(0, 0, 0, 0)"
                ) {
                    const boundingRect = element.getBoundingClientRect();
                    const area = boundingRect.width * boundingRect.height;
                    if (area > maxArea) {
                        maxArea = area;
                        maxAreaElement = secondaryBgColor;
                    }
                }
            }
        }

        for (let i = 0; i < elements.length; i++) {
            const element = elements[i];

            if (element.hasAttribute("data-dracula_secondary_bg_finder")) {
                if (
                    element.classList.contains("dracula-style-all") ||
                    element.classList.contains("dracula-style-bg-txt") ||
                    element.classList.contains("dracula-style-bg-border") ||
                    element.classList.contains("dracula-style-bg")
                ) {
                    const isDifferentSecondaryBg = maxAreaElement !== element.dataset.dracula_secondary_bg_finder;

                    if (isDifferentSecondaryBg) {
                        element.classList.add("dracula-style-secondary-bg");
                    }
                }

                delete element.dataset.dracula_secondary_bg_finder;
            }
        }

        app.secondaryBgColor = maxAreaElement;
    },

    recheckOnCssLoadedLater: () => {
        if (colorType !== 'static') return;

        document.querySelectorAll(".dracula-style-txt-border, .dracula_style-txt, .dracula_style-border").forEach(function (element) {
            const computedStyle = window.getComputedStyle(element, null);
            const backgroundColor = computedStyle.backgroundColor;

            if (backgroundColor !== "rgba(0, 0, 0, 0)" && backgroundColor !== "rgba(255, 255, 255, 0)") {
                app.processElement(element);
            }
        });
    },

    handleToggleClass: (isDarkMode) => {

        document.querySelectorAll('.dracula-toggle').forEach((toggle) => {
            if (isDarkMode) {
                toggle.classList.add("mode-dark");
                toggle.classList.remove("mode-light");
                if (!!showTooltip) {
                    document.querySelectorAll('.dracula-tooltip').forEach((tooltip) => {
                        tooltip.innerText = darkTooltipText;
                    });
                }
            } else {
                toggle.classList.add("mode-light");
                toggle.classList.remove("mode-dark");
                if (!!showTooltip) {
                    document.querySelectorAll('.dracula-tooltip').forEach((tooltip) => {
                        tooltip.innerText = lightTooltipText;
                    });
                }
            }
        });

        // font change on the toggle
        handleDraculaFont(isDarkMode);
    },

    /**
     * Enable dark mode after page load if it was enabled before
     * This fixed the design issues
     */
    enableDarkModeAfterLoad: () => {
        if ('static' === colorType || dracula.isAdmin) return;

        if (draculaDarkMode.isEnabled()) {
            draculaDarkMode.disable();

            const config = getConfig();
            draculaDarkMode.enable(config);
        }

    },
}

export default app;