import { parents } from "@devowl-wp/react-utils";
import { resolveNoneComputedStyle } from "@devowl-wp/resolve-none-computed-style";

import {
    HTML_ATTRIBUTE_CONFIRM,
    HTML_ATTRIBUTE_RESET_PARENT,
    HTML_ATTRIBUTE_RESET_PARENT_IS_RATIO_CONTAINER,
    HTML_ATTRIBUTE_RESET_PARENT_VALUE,
    HTML_ATTRIBUTE_WAS_PREVIOUSLY_STRICT_HIDDEN,
} from "../dom/htmlAttributes.js";

import type { CSSProperties } from "react";

// TODO: make configurable
const KNOWN_RATIO_CLASSES = [
    // General
    "-aspect-ratio",
    "ratio-",
    // Gutenberg
    "wp-block-embed__wrapper",
    // Cornerstone
    "x-frame-inner",
    // Fusion
    "fusion-video",
    // Oxygen
    "video-wrapper",
    // https://www.elegantthemes.com/gallery/extra/
    "video_wrapper",
    // Breakdance
    "ee-video-container",
    // Flatsome UX Builder
    "video-fit",
    // Kadence
    "kadence-video-intrinsic",
];

const RESET_STYLE_IMPORTANT: Record<string, any> = {
    "max-height": "initial",
    height: "auto",
    padding: 0,
    "aspect-ratio": "initial",
    "box-sizing": "border-box",
};

const RESET_STYLE: Record<string, any> = {
    width: "100%",
};

const MEMORIZE_OLD_STYLE = "consent-cb-memo-style";

/**
 * Example: Vimeo responsive video.
 *
 * ```html
 * <div style="padding:56.25% 0 0 0;position:relative;">
 *  <iframe style="position:absolute;top:0;left:0;width:100%;height:100%;"></iframe>
 * </div>
 * ```
 *
 * Example 2: Oxygen responsive video wrapper
 *
 * ```html
 * <div style="aspect-ratio:16/9">
 *  <iframe></iframe>
 * </div>
 * ```
 */
function isPotentialVisualWithinResponsiveContainer(node: HTMLElement) {
    const { parentElement } = node;

    if (!parentElement) {
        return false;
    }

    const parentComputedStyle = getComputedStyle(parentElement);
    if (/\d+\s*\/\s*\d+/g.test(parentComputedStyle["aspectRatio"])) {
        return true;
    }

    const { position: nodePosition } = getComputedStyle(node);
    const { position: parentPosition } = parentComputedStyle;
    const {
        clientWidth,
        clientHeight,
        style: { paddingTop, paddingBottom },
    } = parentElement;
    const calculatedRatio = (clientHeight / clientWidth) * 100;

    return (
        (nodePosition === "absolute" &&
            parentPosition === "relative" &&
            (paddingTop.indexOf("%") > -1 ||
                paddingBottom.indexOf("%") > -1 ||
                (calculatedRatio >= 56 && calculatedRatio <= 57))) ||
        parents(node, undefined, 2).filter(isKnownRatioClass).length > 0
    );
}

function isKnownRatioClass(element: HTMLElement) {
    return KNOWN_RATIO_CLASSES.filter((c) => element.className.indexOf(c) > -1).length > 0;
}

/**
 * Modify parent element with some styles, e. g. Elementor puts some padding into it.
 * We need to enforce the style via inline-style because some themes use `!important` in their
 * classes, too.
 *
 * @param state If `true`, the reset-style will be forced and vice-versa
 */
async function probablyResetParentContainerForVisual(contentBlocker: HTMLElement, state: boolean) {
    const { parentElement } = contentBlocker;
    const iterateParents = parents(contentBlocker, undefined, 3); // Go max three levels upwards

    // Never reset parent container for visual if we are in confirm mode as the visual content blocker is
    // rendered on click over the original, blocked content.
    if (contentBlocker.hasAttribute(HTML_ATTRIBUTE_CONFIRM)) {
        return;
    }

    for (const parent of iterateParents) {
        // Calculate if this HTML element is a ratio container and can be reset
        if (!parent.hasAttribute(HTML_ATTRIBUTE_RESET_PARENT_IS_RATIO_CONTAINER)) {
            const isFirstItemWithinResponsiveContainer =
                parent === parentElement && isPotentialVisualWithinResponsiveContainer(contentBlocker);
            const isRatioContainer =
                isFirstItemWithinResponsiveContainer ||
                isKnownRatioClass(parent) ||
                [0, "0%", "0px"].indexOf(await resolveNoneComputedStyle(parent, "height")) > -1;
            parent.setAttribute(HTML_ATTRIBUTE_RESET_PARENT_IS_RATIO_CONTAINER, isRatioContainer ? "1" : "0");
        }

        if (state && parent.getAttribute(HTML_ATTRIBUTE_RESET_PARENT_IS_RATIO_CONTAINER) === "1") {
            // Check the `display:none!important` style
            const previouslyStrictHidden = parent.getAttribute(HTML_ATTRIBUTE_WAS_PREVIOUSLY_STRICT_HIDDEN) === "1";
            let style = parent.getAttribute("style") || "";
            parent.removeAttribute(HTML_ATTRIBUTE_WAS_PREVIOUSLY_STRICT_HIDDEN);
            if (!previouslyStrictHidden) {
                style = style.replace(/display:\s*none\s*!important;/, "");
            }

            // Apply our new styles with memorization
            parent.setAttribute(HTML_ATTRIBUTE_RESET_PARENT, HTML_ATTRIBUTE_RESET_PARENT_VALUE);
            parent.setAttribute(MEMORIZE_OLD_STYLE, style);
            for (const property in RESET_STYLE_IMPORTANT) {
                parent.style.setProperty(property, (RESET_STYLE_IMPORTANT as any)[property], "important");
            }

            for (const property in RESET_STYLE) {
                parent.style.setProperty(property, (RESET_STYLE as any)[property]);
            }

            // Special use case: Positioning
            if ((window.getComputedStyle(parent).position as CSSProperties["position"]) === "absolute") {
                parent.style.setProperty("position", "static", "important");
            }
        } else if (!state && parent.hasAttribute(HTML_ATTRIBUTE_RESET_PARENT)) {
            parent.setAttribute("style", parent.getAttribute(MEMORIZE_OLD_STYLE) || "");
            parent.removeAttribute(MEMORIZE_OLD_STYLE);
            parent.removeAttribute(HTML_ATTRIBUTE_RESET_PARENT);
        }
    }
}

export { isPotentialVisualWithinResponsiveContainer, probablyResetParentContainerForVisual };
