import { htmlDecode } from "@devowl-wp/react-utils";

import { HTML_ATTRIBUTE_COOKIE_IDS, HTML_ATTRIBUTE_INLINE_STYLE, HTML_TAG_CONSENT_SCRIPT } from "./htmlAttributes.js";
import { putStyleInlineToDom } from "./putStyleInlineToDom.js";
import { decideToUnblock } from "../checker/decideToUnblock.js";

/**
 * Find consent URLs and check for consent and return new style string.
 *
 * @see https://regex101.com/r/9FJWnR/1
 * @param style
 * @param checker
 */
function transformInlineStyle(style: string, checker: Parameters<typeof decideToUnblock>[3]): [string, number] {
    let noConsentCount = 0;
    const newStyle = style.replace(
        /(url\s*\(["'\s]*)([^"]+dummy\.(?:png|css))\?consent-required=([0-9,]+)&consent-by=(\w+)&consent-id=(\d+)&consent-original-url=([^-]+)-/gm,
        (
            m,
            urlPrefix: string,
            maskedUrl: string,
            requiredString: string,
            by: string,
            blockerId: string,
            base64EncodedUrl: string,
        ) => {
            const { consent } = decideToUnblock(by, requiredString, +blockerId, checker);

            if (!consent) {
                noConsentCount++;
            }

            return consent ? `${urlPrefix}${htmlDecode(atob(decodeURIComponent(base64EncodedUrl)))}` : m;
        },
    );

    return [newStyle, noConsentCount];
}

/**
 * Get all blocked rules within a inline CSS style and check, if they can be enabled by a consent.
 */
function transformInlineStyleRules(checker: Parameters<typeof decideToUnblock>[3]) {
    let nodes: HTMLElement[];

    // First: Iterate all `style`'s
    nodes = Array.prototype.slice.call(document.querySelectorAll(`[${HTML_ATTRIBUTE_INLINE_STYLE}]`)) as HTMLElement[];

    for (const node of nodes) {
        // Get the inline style (can be inside attribute when it should be placed to DOM the first time)
        const isFirstTransition = node.tagName.toLowerCase() === HTML_TAG_CONSENT_SCRIPT;
        const innerHTML = isFirstTransition ? node.getAttribute(HTML_ATTRIBUTE_INLINE_STYLE) : node.innerHTML;
        const [newInnerHTML, noConsentCount] = transformInlineStyle(innerHTML, checker);

        // Put pack to DOM
        if (isFirstTransition) {
            node.setAttribute(HTML_ATTRIBUTE_INLINE_STYLE, newInnerHTML);
            putStyleInlineToDom(node as HTMLScriptElement);
        } else {
            if (node.innerHTML !== newInnerHTML) {
                node.innerHTML = newInnerHTML;
            }

            if (noConsentCount === 0) {
                node.removeAttribute(HTML_ATTRIBUTE_INLINE_STYLE);
            }
        }
    }

    // Second: All elements with `style=` attribute
    nodes = Array.prototype.slice.call(
        document.querySelectorAll(`[style*="${HTML_ATTRIBUTE_COOKIE_IDS}"]`),
    ) as HTMLElement[];

    for (const node of nodes) {
        node.setAttribute("style", transformInlineStyle(node.getAttribute("style"), checker)[0]);
    }
}

export { transformInlineStyleRules };
