"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.elementsByHigherZIndex = elementsByHigherZIndex;
exports.fireDoubleClickEvent = fireDoubleClickEvent;
exports.fireClickEvent = fireClickEvent;
exports.fireRightClickEvent = fireRightClickEvent;
exports.getOverlappingElements = getOverlappingElements;
exports.getAllElementsAbove = getAllElementsAbove;
exports.detectAndDismissPopupCandidates = detectAndDismissPopupCandidates;
exports.elementsInFrontCount = elementsInFrontCount;
const DISMISSAL_WAIT_TIME_MS = 750;
const ZINDEX_AUTO = 'auto';
const MINIMUM_POPUP_DISPLAY_COVERAGE_PX = 0.95;
const CLOSE_VALUES = [
    'close',
    'dismiss',
    'exit',
    'not at this time',
    'no thanks',
    'no, thanks',
    'i, agree',
    'i agree',
];
const ATTRIBUTES_TO_CHECK = [
    { attributeName: 'className', matchFunction: matchOnAnyAppearance },
    { attributeName: 'name', matchFunction: matchOnAnyAppearance },
    { attributeName: 'id', matchFunction: matchOnAnyAppearance },
    { attributeName: 'href', matchFunction: matchOnAnyAppearance },
    { attributeName: 'innerText', matchFunction: matchWithWordBoundary },
];
function elementsByHigherZIndex(aboveIndex) {
    return Array.from(document.querySelectorAll('body *')).reduce((obj, element) => {
        const zIndex = parseFloat(globalThis.getComputedStyle(element).zIndex);
        const aboveIndexNumber = typeof aboveIndex === 'number' ? aboveIndex : parseFloat(aboveIndex);
        if (!Number.isNaN(zIndex) && zIndex >= aboveIndexNumber) {
            if (obj.has(zIndex)) {
                obj.get(zIndex).push(element);
            }
            else {
                obj.set(zIndex, [element]);
            }
        }
        return obj;
    }, new Map());
}
function getEffectiveZIndex(element) {
    const isRootElement = (element) => element === undefined || element.tagName.toLowerCase() === 'html';
    let current = element;
    while (!isRootElement(current)) {
        const zIndex = globalThis.getComputedStyle(current).zIndex;
        if (zIndex && zIndex !== ZINDEX_AUTO) {
            return parseInt(zIndex, 10);
        }
        current = current?.parentElement ? current.parentElement : undefined;
    }
    return 0;
}
function getDescendantElements(childElement) {
    let children = [childElement];
    const childNodes = Array.from(childElement.children);
    childNodes.forEach((el) => {
        children = children.concat(getDescendantElements(el));
    });
    return children;
}
function matchOnAnyAppearance(attribute, matchVal) {
    return attribute.toLowerCase().includes(matchVal);
}
function matchWithWordBoundary(attribute, matchVal) {
    const regExp = new RegExp(`\\b${matchVal}\\b`);
    return !!attribute.toLowerCase().match(regExp);
}
function checkElementAttributes(element) {
    let matches = 0;
    ATTRIBUTES_TO_CHECK.forEach((attr) => {
        CLOSE_VALUES.forEach((closeVal) => {
            if (element.getAttribute(attr.attributeName) &&
                attr.matchFunction(element.getAttribute(attr.attributeName), closeVal)) {
                matches += 1;
            }
        });
    });
    return matches;
}
function checkElementDomCoverage(element) {
    if (element.getBoundingClientRect) {
        const bBox = element.getBoundingClientRect();
        const yCoverage = bBox.width / globalThis.innerWidth;
        const xCoverage = bBox.height / globalThis.innerHeight;
        return (yCoverage > MINIMUM_POPUP_DISPLAY_COVERAGE_PX &&
            xCoverage > MINIMUM_POPUP_DISPLAY_COVERAGE_PX);
    }
    return false;
}
function developCloseCandidates(candidates) {
    const candidatesByZIndex = [];
    candidates.forEach((candidate) => {
        const domCovering = [];
        const actionableElements = [];
        candidate.elements.forEach((element) => {
            if (element?.offsetParent ||
                element.tagName?.toUpperCase() === 'SVG') {
                if (['BUTTON', 'A', 'DIV', 'SVG'].includes(element.tagName?.toUpperCase()) &&
                    checkElementAttributes(element) &&
                    !actionableElements.includes(element)) {
                    actionableElements.push(element);
                }
                else if (checkElementDomCoverage(element)) {
                    domCovering.push(element);
                }
            }
        });
        candidatesByZIndex.push({
            zIndex: candidate.zIndex,
            actionableElements,
            domCovering,
            dismissedStatus: false,
            elementsInFront: 0,
        });
    });
    return candidatesByZIndex;
}
function dispatchMouseEvent(targetElement, eventType, leftEdgeClick = false) {
    const boundingBox = targetElement.getBoundingClientRect();
    const halfHeight = boundingBox.height / 2;
    const xDistance = leftEdgeClick ? 1 : boundingBox.width / 2;
    const button = eventType === 'contextmenu' ? 2 : 0;
    const mouseEvent = new MouseEvent(eventType, {
        bubbles: true,
        cancelable: true,
        view: window,
        detail: 0,
        screenX: boundingBox.left + xDistance,
        screenY: boundingBox.top + halfHeight,
        clientX: targetElement.clientLeft + xDistance,
        clientY: targetElement.clientTop + halfHeight,
        ctrlKey: false,
        altKey: false,
        shiftKey: false,
        metaKey: false,
        button,
        relatedTarget: undefined,
    });
    targetElement.dispatchEvent(mouseEvent);
}
function fireDoubleClickEvent(targetElement) {
    fireClickEvent(targetElement, false);
    fireClickEvent(targetElement, false);
    dispatchMouseEvent(targetElement, 'dblclick');
}
function fireClickEvent(targetElement, leftEdgeClick = false) {
    dispatchMouseEvent(targetElement, 'click', leftEdgeClick);
}
function fireRightClickEvent(targetElement, leftEdgeClick = false) {
    dispatchMouseEvent(targetElement, 'contextmenu', leftEdgeClick);
}
function getCenterCoordinates(rect) {
    return {
        x: rect.left + rect.width / 2,
        y: rect.top + rect.height / 2,
    };
}
function getOverlappingElements(element) {
    const { x, y } = getCenterCoordinates(element.getBoundingClientRect());
    const elementsAtPoint = document.elementsFromPoint(x, y);
    const targetIndex = elementsAtPoint.findIndex((iterElement) => iterElement === element);
    return targetIndex ? elementsAtPoint.slice(0, targetIndex) : [];
}
function sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}
function getAllElementsAbove(targetElement, elements) {
    if (elements.length === 0) {
        return [];
    }
    const targetZindex = getEffectiveZIndex(targetElement);
    const zIndexAboveTarget = Math.min(...elements?.map((el) => getEffectiveZIndex(el)));
    if (zIndexAboveTarget <= targetZindex) {
        return [];
    }
    const higherElementsByZIndex = elementsByHigherZIndex(zIndexAboveTarget);
    const allElementsByZIndex = Array.from(higherElementsByZIndex).map(([item, higherElements]) => {
        const allElements = higherElements
            .flatMap(getDescendantElements)
            .reverse();
        return { zIndex: item, elements: allElements };
    });
    allElementsByZIndex.sort((a, b) => b.zIndex - a.zIndex);
    return allElementsByZIndex;
}
async function detectAndDismissPopupCandidates(element) {
    try {
        let higherElements = getOverlappingElements(element);
        const elementsInFrontCount = higherElements.length;
        if (elementsInFrontCount === 0) {
            return {
                elementsInFront: 0,
                dismissedStatus: false,
            };
        }
        const clickCandidates = async function (candidates, leftEdgeClick) {
            for (const candidate of candidates) {
                fireClickEvent(candidate, leftEdgeClick);
                await sleep(DISMISSAL_WAIT_TIME_MS);
                if (getOverlappingElements(element).length < elementsInFrontCount) {
                    return true;
                }
            }
            return false;
        };
        let allElements = getAllElementsAbove(element, higherElements);
        const candidatesByZIndex = developCloseCandidates(allElements);
        for (const candidate of candidatesByZIndex) {
            let matched = await clickCandidates(candidate.domCovering, true);
            if (!matched) {
                matched = await clickCandidates(candidate.actionableElements, false);
            }
            if (matched) {
                return {
                    dismissedStatus: true,
                    elementsInFront: 0,
                };
            }
        }
        higherElements = getOverlappingElements(element);
        allElements = getAllElementsAbove(element, higherElements);
        const candidates = developCloseCandidates(allElements);
        const result = candidates.length
            ? candidates[0]
            : {
                zIndex: 0,
                elementsInFront: 0,
                dismissedStatus: false,
                domCovering: [],
                actionableElements: [],
            };
        result.elementsInFront = higherElements.length;
        result.dismissedStatus = false;
        return {
            dismissedStatus: result.dismissedStatus,
            elementsInFront: result.elementsInFront,
        };
    }
    catch (error) {
        return {
            elementsInFront: 0,
            dismissedStatus: false,
            error: error.toString(),
        };
    }
}
function elementsInFrontCount(element) {
    return getOverlappingElements(element).length;
}
