"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TapExecutor = void 0;
exports.performTapAction = performTapAction;
exports.performLongPressAction = performLongPressAction;
const TapStepDescriptor_1 = require("@mablhq/test-model/types/mobile/TapStepDescriptor");
const execution_1 = require("@mablhq-internal/runtime/execution");
const execution_2 = require("@mablhq-internal/runtime/execution");
const FindMobileElement_1 = require("../actions/FindMobileElement");
const executorUtils_1 = require("./executorUtils");
const webViewUtil_1 = require("../util/webViewUtil");
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
class TapExecutor {
    run(step, executionContext) {
        return (0, execution_1.runActionWithElementReadyRetriesForMobile)(() => this.findAndTap(step, executionContext), executionContext);
    }
    async findAndTap(step, executionContext) {
        const foundTarget = await FindMobileElement_1.FindMobileElement.run(step, executionContext);
        await executionContext.observeDeferredFunction();
        if (!foundTarget.mobileElement?.elementId && !foundTarget.boundingBox) {
            return {
                status: execution_2.StepStatus.Failed,
                logInfo: 'Element not found',
            };
        }
        const driver = executionContext.mobileTestEnvironment.getDriver();
        const platform = executionContext.mobileTestEnvironment.getConfig().app.platformName;
        const parentWebViewFoundTarget = (0, FindMobileElement_1.isWebViewElementHandle)(foundTarget)
            ? await getParentWebViewTarget(executionContext, step)
            : undefined;
        let parentWebViewElRect;
        if (parentWebViewFoundTarget) {
            if (!parentWebViewFoundTarget.mobileElement) {
                return {
                    status: execution_2.StepStatus.Failed,
                    logInfo: 'Parent webview element not found',
                };
            }
            parentWebViewElRect = parentWebViewFoundTarget.boundingBox;
        }
        await executionContext.contextSwitcher.switchToElementContext(foundTarget.context);
        let boundingBox;
        try {
            let elRect;
            if (parentWebViewElRect) {
                elRect = await getWebViewElementCoordinatesTranslatedToNative(driver, foundTarget, parentWebViewElRect, platform);
            }
            else if (foundTarget.boundingBox) {
                elRect = foundTarget.boundingBox;
            }
            if (!elRect) {
                return {
                    status: execution_2.StepStatus.Failed,
                    logInfo: 'Element not found, could not determine bounding box',
                };
            }
            boundingBox = elRect;
            const targetCoordinates = (0, executorUtils_1.getTargetWithinElementCoordinates)(elRect, step.coordinatesWithinElement);
            switch (step.type) {
                case TapStepDescriptor_1.TapType.long:
                    await performLongPressAction(driver, targetCoordinates, step.pressDurationMillis);
                    break;
                default:
                    await performTapAction(driver, platform, targetCoordinates.x, targetCoordinates.y);
            }
        }
        finally {
            await executionContext.contextSwitcher.switchToNativeContext();
        }
        executionContext.mobileTestEnvironment
            .maybeUpdateAppUnderTestId()
            .catch((error) => executionContext.log.error('Could not check and potentially update the App ID of the app under test', error));
        return {
            status: execution_2.StepStatus.Passed,
            boundingBox,
        };
    }
}
exports.TapExecutor = TapExecutor;
async function getParentWebViewTarget(executionContext, step) {
    const { webView, ...nativeFindDescriptor } = step.find;
    const nativeWebViewTapStepDescriptor = {
        ...step,
        find: nativeFindDescriptor,
    };
    return FindMobileElement_1.FindMobileElement.run(nativeWebViewTapStepDescriptor, executionContext);
}
async function getWebViewElementCoordinatesTranslatedToNative(driver, foundTarget, parentWebViewElRect, platform) {
    if (!foundTarget.webViewElementBoundingClientRect) {
        throw new Error('Expected webViewElementBoundingClientRect to be populated');
    }
    const webViewWindowCoordinatesInfo = await driver.execute(() => ({
        visualViewportWidth: window.visualViewport?.width ?? window.outerWidth,
        visualViewportOffsetLeft: window.visualViewport?.offsetLeft ?? 0,
        visualViewportOffsetTop: window.visualViewport?.offsetTop ?? 0,
        visualViewportScale: window.visualViewport?.scale ?? 1,
    }));
    return (0, webViewUtil_1.translateWebViewElementCoordinatesToNativeCoordinates)(foundTarget.webViewElementBoundingClientRect, webViewWindowCoordinatesInfo, parentWebViewElRect, platform);
}
const ANDROID_TAP_DURATION_MS = 150;
async function performTapAction(driver, platform, xCoordinate, yCoordinate) {
    switch (platform) {
        case mablApi_1.MobilePlatformEnum.Android:
            return performLongPressAction(driver, { x: xCoordinate, y: yCoordinate }, ANDROID_TAP_DURATION_MS);
        case mablApi_1.MobilePlatformEnum.Ios:
            return driver.execute('mobile: tap', { x: xCoordinate, y: yCoordinate });
        default:
            throw new Error(`Unable to perform tap action on unknown platform: ${platform}`);
    }
}
async function performLongPressAction(driver, coordinates, pressDurationMillis) {
    const longPressDurationMillis = 1000;
    return driver.performActions([
        {
            type: 'pointer',
            id: 'finger1',
            parameters: { pointerType: 'touch' },
            actions: [
                {
                    type: 'pointerMove',
                    duration: 0,
                    x: coordinates.x,
                    y: coordinates.y,
                },
                { type: 'pointerDown', button: 0 },
                {
                    type: 'pause',
                    duration: pressDurationMillis ?? longPressDurationMillis,
                },
                { type: 'pointerUp', button: 0 },
            ],
        },
    ]);
}
