"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssertMobileExecutor = void 0;
const Email_1 = require("@mablhq-internal/runtime/api/entities/Email");
const messaging_1 = require("@mablhq-internal/runtime/core/messaging/messaging");
const mablscriptFind_1 = require("@mablhq/test-model/mablscriptFind");
const JavaScriptAction_1 = require("@mablhq/test-model/actions/JavaScriptAction");
const ConditionDescriptor_1 = require("@mablhq/test-model/types/ConditionDescriptor");
const ExtractDescriptor_1 = require("@mablhq/test-model/types/ExtractDescriptor");
const GetCurrentLocationDescriptor_1 = require("@mablhq/test-model/types/GetCurrentLocationDescriptor");
const GetVariableDescriptor_1 = require("@mablhq/test-model/types/GetVariableDescriptor");
const loggingProvider_1 = require("@mablhq-internal/runtime/providers/logging/loggingProvider");
const logUtils_1 = require("@mablhq-internal/runtime/util/logUtils");
const execution_1 = require("@mablhq-internal/runtime/execution");
const execution_2 = require("@mablhq-internal/runtime/execution");
const execution_3 = require("@mablhq-internal/runtime/execution");
const execution_4 = require("@mablhq-internal/runtime/execution");
const execution_5 = require("@mablhq-internal/runtime/execution");
const execution_6 = require("@mablhq-internal/runtime/execution");
const FindMobileElement_1 = require("../actions/FindMobileElement");
const execution_7 = require("@mablhq-internal/runtime/execution");
const GetViewportDescriptor_1 = require("@mablhq/test-model/types/GetViewportDescriptor");
const GetVariableValue_1 = require("@mablhq/test-model/actions/GetVariableValue");
class AssertMobileExecutor {
    retryBehavior;
    constructor(retryBehavior) {
        this.retryBehavior = retryBehavior;
    }
    async run(step, executionContext) {
        const failureRetryConfiguration = execution_6.AssertExecutor.getEffectiveFailureRetryConfiguration(step, this.retryBehavior);
        const runAssertAction = () => (0, execution_3.runActionWithElementReadyRetriesForMobile)(() => this.runAssertion(step, executionContext), executionContext, {
            timeoutMs: failureRetryConfiguration?.maxTimeMs,
            retryCount: execution_6.AssertExecutor.getElementReadyRetryCount(step, failureRetryConfiguration),
        }, execution_6.AssertExecutor.isStepAiAssertion(step)
            ? execution_6.AI_ASSERTIONS_TRANSIENT_ENDPOINT_ERROR_MESSAGES
            : undefined);
        const executionWithRetriesResult = failureRetryConfiguration
            ? await (0, execution_3.retryActionWithFailedStatusRetry)(runAssertAction, executionContext, failureRetryConfiguration)
            : await runAssertAction();
        await executionContext.observeDeferredFunction(execution_6.AssertExecutor.makeDeferredObservationProperties(step, executionWithRetriesResult));
        return executionWithRetriesResult;
    }
    static getFindHintGenerator(condition) {
        if (condition.conditionType === ConditionDescriptor_1.ConditionType.Presence) {
            if (condition.presenceType === ConditionDescriptor_1.PresenceEvaluationType.NOT_PRESENT) {
                return { elementExpectation: mablscriptFind_1.ElementExpectation.MISSING };
            }
            return { elementExpectation: mablscriptFind_1.ElementExpectation.PRESENT };
        }
        return;
    }
    async runAssertion(step, executionContext) {
        const maybeUnavailableResult = execution_6.AssertExecutor.getResultForUnavailableAssertions(step, executionContext);
        if (maybeUnavailableResult) {
            return maybeUnavailableResult;
        }
        if ((0, GetViewportDescriptor_1.isGetViewportDescriptor)(step.target)) {
            if (execution_6.AssertExecutor.isStepAiAssertion(step)) {
                const evaluationResult = await execution_6.AssertExecutor.evaluateAIPromptCondition(step.condition, executionContext);
                return execution_6.AssertExecutor.evaluationResultToStepResult(evaluationResult, step.onFailure, executionContext.log);
            }
            throw new Error('Cannot run non-AI assertion without a find descriptor');
        }
        if (step.count ||
            ((0, mablscriptFind_1.isFindCustomDescriptor)(step.target) &&
                step.target.findType === mablscriptFind_1.FindType.FIND_ALL)) {
            return this.assertAgainstCount(step, executionContext);
        }
        const findHintGenerator = AssertMobileExecutor.getFindHintGenerator(step.condition);
        const assertItem = await AssertMobileExecutor.getItem(step, executionContext, findHintGenerator);
        const { item, boundingBox, itemDescription } = assertItem;
        const finalItem = await AssertMobileExecutor.filterToVisible(step, executionContext, assertItem);
        if (item && !finalItem && !(0, ConditionDescriptor_1.isAbsenceExpected)(step.condition)) {
            return execution_6.AssertExecutor.evaluationResultToStepResult(new execution_1.Failure(`The assertion target was not visible.`), step.onFailure, executionContext.log);
        }
        const itemValue = await AssertMobileExecutor.getItemValue(step, finalItem, executionContext, itemDescription);
        if (step.extract && itemValue === undefined) {
            return execution_6.AssertExecutor.evaluationResultToStepResult(new execution_1.Failure(`The assertion target was not found.`), step.onFailure, executionContext.log);
        }
        let value;
        if (itemValue instanceof execution_6.VariableValue) {
            value = itemValue.value;
        }
        else if (!step.extract) {
            value = !!finalItem ? execution_7.Presence.present : execution_7.Presence.notPresent;
        }
        else {
            value = itemValue;
        }
        let evaluationResult;
        if (execution_6.AssertExecutor.isStepAiAssertion(step)) {
            if (boundingBox === undefined) {
                return execution_6.AssertExecutor.evaluationResultToStepResult(new execution_1.Failure(`The assertion target was not found.`), step.onFailure, executionContext.log);
            }
            evaluationResult = await execution_6.AssertExecutor.evaluateAIPromptCondition(step.condition, executionContext, boundingBox);
        }
        else {
            evaluationResult = execution_1.Condition.evaluate(step.condition, value, executionContext.variables);
        }
        return execution_6.AssertExecutor.evaluationResultToStepResult(evaluationResult, step.onFailure, executionContext.log, boundingBox);
    }
    async assertAgainstCount(step, executionContext) {
        if (!(0, mablscriptFind_1.isMobileFindCustomDescriptor)(step.target)) {
            throw new Error('Invalid find for mobile element count assertion');
        }
        const findResult = await FindMobileElement_1.FindMobileElement.runMultipleResultFind(step.target, executionContext);
        return execution_6.AssertExecutor.assertElementCount(step, executionContext, {
            elements: findResult.elements,
        });
    }
    static async getItem(step, executionContext, findHintGenerator) {
        if ((0, GetViewportDescriptor_1.isGetViewportDescriptor)(step.target)) {
            throw new Error('Cannot get item for viewport assertions');
        }
        if ((0, GetCurrentLocationDescriptor_1.isGetCurrentLocationDescriptor)(step.target)) {
            throw new Error('Cannot assert against URL for mobile execution');
        }
        if ((0, JavaScriptAction_1.isJavaScriptActionDescriptor)(step.target)) {
            const snippetResult = await execution_2.EvaluateJavaScript.run(step.target.javaScript, executionContext);
            return {
                item: new execution_6.VariableValue(snippetResult.value),
                itemDescription: `the javascript script`,
            };
        }
        if ((0, GetVariableDescriptor_1.isGetVariableDescriptor)(step.target)) {
            const variableAction = GetVariableValue_1.GetVariableValue.buildAction(step.target.variable);
            const value = executionContext.variables.get(variableAction.variable.name);
            return {
                item: new execution_6.VariableValue(value),
                itemDescription: `variable ${variableAction.variable.name}`,
            };
        }
        if (step.target.findType === mablscriptFind_1.FindType.FIND_EMAIL) {
            return {
                item: await execution_5.FindEmail.run(step.target.findTarget, executionContext),
                itemDescription: `email`,
            };
        }
        if (step.target.findType === mablscriptFind_1.FindType.FIND_COOKIE) {
            throw new Error('Cannot assert against cookies for mobile execution');
        }
        const findResult = await FindMobileElement_1.FindMobileElement.run({ ...step, find: step.target }, executionContext, findHintGenerator);
        const { boundingBox, mobileElement, webViewElementSelector } = findResult;
        if ((0, FindMobileElement_1.isWebViewElementHandle)(findResult) && !!webViewElementSelector) {
            return {
                item: webViewElementSelector,
                boundingBox,
                itemDescription: (0, mablscriptFind_1.humanizeBrowserElementSelector)(webViewElementSelector),
            };
        }
        return {
            item: mobileElement,
            boundingBox,
            itemDescription: await AssertMobileExecutor.getElementDescription(mobileElement),
        };
    }
    static async getElementDescription(element) {
        let elementTagName;
        try {
            elementTagName = await element?.getTagName();
        }
        catch (error) {
            console.error('Error getting element tag name:', error);
            elementTagName = 'unknown';
        }
        let elementText;
        try {
            elementText = await element?.getText();
        }
        catch (error) {
            console.error('Error getting element text:', error);
        }
        let text = `the <${elementTagName}> element`;
        if (elementText) {
            text += ` with text "${(0, execution_1.normalizeText)(elementText, true)}"`;
        }
        return text;
    }
    static async filterToVisible(step, executionContext, assertItem) {
        const { item, boundingBox } = assertItem;
        if (!boundingBox) {
            return item;
        }
        if (AssertMobileExecutor.getFindType(step) === mablscriptFind_1.FindType.FIND_ONE &&
            FindMobileElement_1.FindMobileElement.isWebdriverIOElement(item)) {
            const windowSize = await executionContext.mobileTestEnvironment.getWindowSize();
            if (boundingBox.x >= 0 &&
                boundingBox.x <= windowSize.width &&
                boundingBox.y >= 0 &&
                boundingBox.y <= windowSize.height) {
                return item;
            }
            return undefined;
        }
        return item;
    }
    static getFindType(step) {
        return step.target.findType;
    }
    static async getItemValue(step, finalItem, executionContext, itemDescription) {
        if (!finalItem) {
            return undefined;
        }
        if (finalItem instanceof execution_6.VariableValue) {
            return finalItem;
        }
        else if (FindMobileElement_1.FindMobileElement.isWebdriverIOElement(finalItem)) {
            if (!step.extract) {
                return undefined;
            }
            const attributeName = (0, execution_4.getAttributeName)(step.extract);
            const result = await finalItem.getAttribute(attributeName);
            (0, logUtils_1.logWebUI)(`Extracted "${attributeName}" attribute "${(0, execution_1.normalizeText)(result ?? '', true)}" from ${itemDescription}.`, loggingProvider_1.LogLevel.Info, executionContext, { executionPhase: messaging_1.ExecutionPhase.DURING_ACTION });
            return result;
        }
        else if (!!step.extract && (0, mablscriptFind_1.isStandardSelector)(finalItem)) {
            return execution_4.ExtractAttribute.run(step.extract, finalItem, executionContext, itemDescription);
        }
        else if (step.extract?.extractType === ExtractDescriptor_1.ExtractType.EXTRACT_EMAIL_ATTRIBUTE &&
            (0, Email_1.isEmail)(finalItem)) {
            return execution_4.ExtractAttribute.run(step.extract, finalItem.input_properties, executionContext, itemDescription);
        }
        throw new Error(`Unsupported assertion type: [${step.extract?.extractType}]`);
    }
}
exports.AssertMobileExecutor = AssertMobileExecutor;
