"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkPopupDismissalOnAction = checkPopupDismissalOnAction;
exports.checkActionabilityAt = checkActionabilityAt;
exports.attemptPopupDismissal = attemptPopupDismissal;
exports.maybeAddPopupLogic = maybeAddPopupLogic;
const fs = __importStar(require("fs"));
const resourceUtil_1 = require("./resourceUtil");
const messaging_1 = require("../core/messaging/messaging");
const logUtils_1 = require("./logUtils");
const loggingProvider_1 = require("../providers/logging/loggingProvider");
const ACTIONABILITY_CHECK_SCRIPT_LOCATION = (0, resourceUtil_1.findResource)('actionabilityCheck.js');
const EMBEDDED_POPUP_SCRIPT_LOCATION = (0, resourceUtil_1.findResource)('popupDismissal.js');
const NO_CLICKABLE_POINT_ERROR = 'Unable to find a clickable point for the element';
const POPUP_ERROR_MESSAGE_HINT = 'intercepts pointer events';
let embeddedPopupScript;
let embeddedActionabilityLogic;
async function checkPopupDismissalOnAction(elementHandle, action, executionContext, recheckOnFail = true) {
    try {
        await action();
    }
    catch (error) {
        if (error.message.includes(POPUP_ERROR_MESSAGE_HINT)) {
            const hasPopup = await isElementWithPopup(elementHandle);
            if (hasPopup) {
                return true;
            }
            const dismissalResult = await attemptPopupDismissal(elementHandle, executionContext);
            if (recheckOnFail) {
                return checkPopupDismissalOnAction(elementHandle, action, executionContext, false);
            }
            if (!dismissalResult) {
                (0, logUtils_1.logWebUIAndCliOutput)(`Found element may not be actionable`, loggingProvider_1.LogLevel.Warn, executionContext, {
                    executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
                });
                (0, logUtils_1.logInternal)(`Found element might not be actionable. ${error.toString()}`);
            }
            return dismissalResult;
        }
    }
    return true;
}
async function checkActionabilityAt(elementHandle, executionContext, recheckOnFail = true) {
    let point = await elementHandle.clickablePoint();
    if (point === undefined) {
        throw new Error(NO_CLICKABLE_POINT_ERROR);
    }
    const frame = await elementHandle.frame();
    if (frame?.parentFrame()) {
        const element = await frame.getFrameOwnerElement();
        if (element !== undefined) {
            const box = await element.boundingBox();
            if (!box) {
                throw new Error('Element not connected');
            }
            point = { x: point.x - box.x, y: point.y - box.y };
        }
    }
    await maybeAddActionabilityLogic(elementHandle);
    const result = await elementHandle.evaluate((node, point) => mablCheckActionability(node, point), { ...point });
    if (result !== 'done') {
        if (typeof result === 'object' && 'actionabilityDescription' in result) {
            (0, logUtils_1.logInternal)(`${result.actionabilityDescription} intercepts pointer events. Trying to auto-dismiss popups.`);
            const hasPopup = await isElementWithPopup(elementHandle);
            if (hasPopup) {
                return {
                    result: 'done',
                    clickablePoint: point,
                };
            }
            await attemptPopupDismissal(elementHandle, executionContext);
            if (recheckOnFail) {
                return checkActionabilityAt(elementHandle, executionContext, false);
            }
            throw new Error(`Element not clickable at point.`);
        }
        throw new Error(`Element not at point. ${result}`);
    }
    return { result, clickablePoint: point };
}
async function attemptPopupDismissal(element, executionContext) {
    const frame = await element.frame();
    if (frame === undefined) {
        return false;
    }
    await maybeAddPopupLogic(element);
    const { dismissedStatus, elementsInFront, error } = await element.evaluate((element) => globalThis.popupDismissal.detectAndDismissPopupCandidates(element));
    if (error) {
        (0, logUtils_1.logInternal)(`Error while trying to dismiss popups: ${error}`);
    }
    if (!dismissedStatus) {
        if (elementsInFront) {
            (0, logUtils_1.logInternal)('Failed to auto dismiss and there are still elements in front. Sending escape key to body.');
            const bodyElement = await frame.$('BODY');
            if (bodyElement) {
                await bodyElement.press('Escape');
            }
            const elementsInFront = await element.evaluate((element) => globalThis.popupDismissal.elementsInFrontCount(element));
            if (elementsInFront) {
                (0, logUtils_1.logInternal)('The popup did not dismiss using the escape key.');
                return false;
            }
        }
        else {
            (0, logUtils_1.logInternal)('Failed to auto dismiss. No elements found in front of target element.');
            return false;
        }
    }
    (0, logUtils_1.logWebUIAndCliOutput)(`A popup was detected and dismissed`, loggingProvider_1.LogLevel.Info, executionContext, {
        executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
    });
    return true;
}
async function maybeAddPopupLogic(element) {
    const hasPopupDismissalLogic = await isPopupDismissalInTarget(element);
    if (!hasPopupDismissalLogic) {
        if (!embeddedPopupScript) {
            embeddedPopupScript = fs.readFileSync(EMBEDDED_POPUP_SCRIPT_LOCATION, 'utf8');
        }
        await element.evaluate(embeddedPopupScript);
    }
}
async function maybeAddActionabilityLogic(element) {
    const hasActionabilityCheckLogic = await isActionabilityLogicInTarget(element);
    if (!hasActionabilityCheckLogic) {
        if (!embeddedActionabilityLogic) {
            embeddedActionabilityLogic = fs.readFileSync(ACTIONABILITY_CHECK_SCRIPT_LOCATION, 'utf8');
        }
        await element.evaluate(embeddedActionabilityLogic);
    }
}
function isActionabilityLogicInTarget(element) {
    return element.evaluate(() => !!globalThis.mablCheckActionability);
}
function isPopupDismissalInTarget(element) {
    return element.evaluate(() => !!globalThis.popupDismissal);
}
function isElementWithPopup(element) {
    return element.evaluate((element) => element.getAttribute('aria-expanded') === 'true' &&
        element.getAttribute('aria-haspopup') === 'true');
}
