import { decideToUnblock } from "./decideToUnblock.js";
import {
    HTML_ATTRIBUTE_BLOCKER_ID,
    HTML_ATTRIBUTE_BY,
    HTML_ATTRIBUTE_CONDITION,
    HTML_ATTRIBUTE_COOKIE_IDS,
    HTML_TAG_CONSENT_SCRIPT,
} from "../dom/htmlAttributes.js";

import type { BlockerCheckerCallback } from "./decideToUnblock.js";
import type { VisualConfiguration } from "../visual/createVisual.js";

type Row = {
    node: HTMLElement;
    consent: boolean;
    /**
     * A visual content blocker is also discovered as blocked content. This
     * flag simply indicates if this is a visual content blocker, or not.
     */
    isVisualCb: boolean;
    blocker?: VisualConfiguration;
    priority: number;
};

const FIND_BLOCKED_NODES_PRIORITY_SCRIPT = 10;
const FIND_BLOCKED_NODES_PRIORITY_NON_SCRIPT = 0;

/**
 * Read all HTML elements and check, if they can be enabled by a consent.
 */
function findBlockedNodes<ExtendedBlockerDefinition extends VisualConfiguration = VisualConfiguration>(
    checker: BlockerCheckerCallback<ExtendedBlockerDefinition>,
) {
    const result = [] as Row[];
    const nodes = Array.prototype.slice.call(
        document.querySelectorAll(`[${HTML_ATTRIBUTE_COOKIE_IDS}]`),
    ) as HTMLElement[];

    for (const node of nodes) {
        const { blocker, consent } = decideToUnblock<ExtendedBlockerDefinition>(
            node.getAttribute(HTML_ATTRIBUTE_BY),
            node.getAttribute(HTML_ATTRIBUTE_COOKIE_IDS),
            +node.getAttribute(HTML_ATTRIBUTE_BLOCKER_ID),
            checker,
            (node.getAttribute(HTML_ATTRIBUTE_CONDITION) as "and" | "or") || "and",
        );

        const isVisualCb = node.className.indexOf("rcb-content-blocker") > -1;
        result.push({
            node,
            consent,
            isVisualCb,
            blocker,
            priority:
                node.tagName.toLowerCase() === HTML_TAG_CONSENT_SCRIPT
                    ? FIND_BLOCKED_NODES_PRIORITY_SCRIPT
                    : FIND_BLOCKED_NODES_PRIORITY_NON_SCRIPT,
        });
    }

    // Sort blockable nodes by priority, e.g. always execute scripts at the end
    result.sort(({ priority: a }, { priority: b }) => a - b);

    return result;
}

export { findBlockedNodes, FIND_BLOCKED_NODES_PRIORITY_NON_SCRIPT, FIND_BLOCKED_NODES_PRIORITY_SCRIPT };
