"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.MobileRunner = void 0;
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
const MobileTestExecutionContext_1 = require("../mobile/contexts/MobileTestExecutionContext");
const MobileTestEnvironment_1 = require("../mobile/MobileTestEnvironment");
const execution_1 = require("@mablhq-internal/runtime/execution");
const execution_2 = require("@mablhq-internal/runtime/execution");
const messaging_1 = require("@mablhq-internal/runtime/core/messaging/messaging");
const execution_3 = require("@mablhq-internal/runtime/execution");
const resourceUtil_1 = require("@mablhq-internal/runtime/util/resourceUtil");
const path = __importStar(require("path"));
const MobileFlowRunner_1 = require("../flows/MobileFlowRunner");
const asyncUtil_1 = require("@mablhq-internal/runtime/util/asyncUtil");
const iOSUtil_1 = require("../mobile/util/iOSUtil");
const androidUtil_1 = require("../mobile/util/androidUtil");
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 OBSERVER_TIMEOUT_WARNING = "Mabl's attempt to observe the state of the app during execution of this test step has failed. " +
    'The app may be unresponsive, and some information such as network activity or ' +
    'mobile screenshots may be unavailable for surrounding steps.';
class MobileRunner extends execution_3.MablRunner {
    testRunConfig;
    testExecutionProxy;
    constructor(testRunConfig) {
        super(testRunConfig.testContext, testRunConfig.runConfig);
        this.testRunConfig = testRunConfig;
        this.testExecutionProxy = testRunConfig.testExecutionProxy;
    }
    async createFlowRunner(flowController) {
        switch (flowController.flow.flow_type) {
            case mablApi_1.FlowTypeEnum.Mobile: {
                return new MobileFlowRunner_1.MobileFlowRunner(flowController);
            }
        }
        return undefined;
    }
    getObserverTimeoutWarning() {
        return OBSERVER_TIMEOUT_WARNING;
    }
    async runTest() {
        const startTime = Date.now();
        const appiumHome = this.testRunConfig.runConfig.server?.appiumHome ??
            path.resolve((0, resourceUtil_1.findNodeModulesDirectories)()[0]);
        const logger = new execution_2.MobileConsoleLogger();
        const { testContext, runConfig } = this.testRunConfig;
        const mobileEnvConfig = {
            app: runConfig.appConfig,
            androidConfiguration: runConfig.androidConfiguration,
            server: {
                ...runConfig.server,
                appiumHome,
                serverEventHandler: (event) => {
                    this.logAppiumServerEvent(runConfig.appConfig, logger, event);
                    return;
                },
            },
        };
        const mapper = new execution_1.MobileExecutorStore();
        const { observers = [], postRunCallback } = runConfig.observerConfig ?? {};
        const executionContext = new MobileTestExecutionContext_1.MobileTestExecutionContext(mobileEnvConfig, this.testRunConfig.apiClient, testContext, messaging_1.mablEventEmitter, logger, mapper, undefined, this.testExecutionProxy);
        executionContext.isPerformanceTestContext = false;
        executionContext.isCliCreated = this.testRunConfig.isCliCreated ?? false;
        executionContext.allowLocalTestBilling =
            this.testRunConfig.allowLocalTestBilling ?? false;
        executionContext.executionPhase = messaging_1.ExecutionPhase.BEFORE_TEST;
        await executionContext.initializeMobileTestEnvironment();
        logger.info('Initialized mobile test environment');
        await executionContext.mobileTestEnvironment.resetEnvironment();
        await this.initializeMobileVariables(this.testRunConfig, executionContext);
        const flowProvider = new execution_4.ApiFlowProvider(this.testRunConfig.apiClient, this.testRunConfig.runConfig.branchName);
        this.testRunConfig.testContext.setFlowProvider(flowProvider);
        const snippetProvider = new execution_6.ApiSnippetProvider(this.testRunConfig.apiClient, this.testRunConfig.testContext.test.organization_id);
        this.testRunConfig.testContext.setSnippetProvider(snippetProvider);
        this.testRunConfig.testContext.setFileUploadProvider(new execution_5.ApiFileUploadProvider(this.testRunConfig.apiClient));
        if (this.testRunConfig.runConfig.recordVideoPath) {
            logger.info('Enabling screen recording');
            await executionContext.mobileTestEnvironment.enableScreenRecording();
        }
        const finalTestStatus = await this.runFlows(startTime, executionContext);
        if (this.testRunConfig.runConfig.recordVideoPath) {
            const fileName = `mobileTestRun-${this.testRunConfig.testContext.test.id}-${Date.now()}`.replace(/:/g, '-');
            logger.info('Saving screen recording of mobile test');
            await executionContext.mobileTestEnvironment.saveScreenRecording(path.join(this.testRunConfig.runConfig.recordVideoPath, fileName));
            logger.info('Shutting down recording');
            await executionContext.mobileTestEnvironment.stopScreenRecording();
        }
        executionContext.executionPhase = messaging_1.ExecutionPhase.AFTER_TEST;
        await this.runTestObservers(executionContext, observers);
        if (postRunCallback) {
            try {
                await (0, asyncUtil_1.promiseWithTimeout)(postRunCallback(observers, executionContext), execution_3.DEFAULT_ASYNC_TIMEOUT_MILLIS, 'Post-run observers', 'observers', false);
            }
            catch (error) {
                executionContext.log.error(OBSERVER_TIMEOUT_WARNING);
            }
        }
        executionContext.executionIndexTracker.clearFlow();
        await executionContext.shutdown();
        finalTestStatus.endTime = Date.now();
        finalTestStatus.rerunConfig = {
            environmentId: testContext.environment?.id,
            credentialsId: testContext.credentials?.id,
            branch: runConfig.branchName,
        };
        return finalTestStatus;
    }
    logAppiumServerEvent(mobileAppConfig, logger, event) {
        const translatedEventText = (0, MobileTestEnvironment_1.interpretMobileServerEvent)(mobileAppConfig, event);
        logger.info(translatedEventText);
    }
    async initializeMobileVariables(testRunConfig, mobileTestExecutionContext) {
        let mobileRunDeviceInfo;
        const { testContext, runConfig } = testRunConfig;
        const { isCloudRun, runId } = testContext;
        const { mablApiClient, mobileTestEnvironment, variables } = mobileTestExecutionContext;
        if (isCloudRun && !!runId) {
            const testRun = await mablApiClient.getJourneyRun(runId);
            const mobileDeviceInfo = testRun?.journey_parameters?.mobile_device;
            const mobileAppFileId = testRun?.journey_parameters?.deployment?.mobile_app_file_id;
            let mobileAppFile;
            if (!!mobileAppFileId) {
                mobileAppFile = await mablApiClient.getMobileAppFile(mobileAppFileId);
            }
            if (mobileDeviceInfo) {
                mobileTestExecutionContext.log.debug(`Generating mobile run device info from test run${!!mobileAppFile ? ` and mobile app file [${mobileAppFileId}]` : ''}`);
                mobileRunDeviceInfo = {
                    buildName: mobileAppFile?.name,
                    buildVersion: mobileAppFile?.version,
                    deviceApiLevel: mobileDeviceInfo.android_configuration?.api_level,
                    deviceHeight: mobileDeviceInfo.screen_dimensions.height,
                    deviceName: mobileDeviceInfo.device_name,
                    deviceOsVersion: mobileDeviceInfo.os_version,
                    deviceWidth: mobileDeviceInfo.screen_dimensions.width,
                };
                testContext.setMobileRunDeviceInfo(mobileRunDeviceInfo);
                variables.initializeMobileVariables(mobileRunDeviceInfo);
                return;
            }
        }
        const deviceInfo = await mobileTestEnvironment.getMobileDeviceInfo();
        const screenSize = await mobileTestEnvironment.getDeviceScreenSizeScaled();
        const { appName, avdName, appVersion: buildVersion, path: appPath, platformName, platformVersion, udid, } = runConfig.appConfig;
        let buildName = appName;
        if (!buildName) {
            buildName = appPath ? path.basename(appPath) : undefined;
        }
        if (platformName === mablApi_1.MobilePlatformEnum.Android) {
            const { apiVersion: deviceApiLevel, platformVersion: deviceOsVersion } = deviceInfo;
            let deviceName = runConfig.appConfig.deviceName;
            if (!deviceName) {
                let androidEmulatorsLookup;
                try {
                    androidEmulatorsLookup = await (0, androidUtil_1.getListOfLocalAndroidEmulators)(true);
                }
                catch (error) {
                    console.error('Error listing emulators for getting device details', error);
                }
                const emulatorInfo = Object.values(androidEmulatorsLookup ?? {})?.find((emulator) => emulator.avdName === avdName);
                deviceName = emulatorInfo?.name ?? 'Android device';
            }
            if (deviceName?.includes('(')) {
                const regexExtract = /(.*)\((.*)\)/gm.exec(deviceName);
                if (regexExtract && regexExtract.length > 2) {
                    deviceName = regexExtract[1].trim();
                }
            }
            mobileRunDeviceInfo = {
                buildName,
                buildVersion,
                deviceApiLevel,
                deviceHeight: screenSize.height,
                deviceName,
                deviceOsVersion,
                deviceWidth: screenSize.width,
            };
        }
        else {
            let simulatorInfoResponse;
            try {
                simulatorInfoResponse = await (0, iOSUtil_1.listSimulators)();
            }
            catch (error) {
                console.error('Error listing simulators for getting device details', error);
            }
            const simulatorInfo = Object.values(simulatorInfoResponse ?? {}).find((sim) => sim.udid === udid);
            let osVersion = simulatorInfo?.osVersion;
            if (osVersion) {
                const regexExtract = /iOS (.*)/gm.exec(osVersion);
                if (regexExtract && regexExtract.length > 1) {
                    osVersion = regexExtract[1];
                }
            }
            const { name: deviceName } = deviceInfo;
            mobileRunDeviceInfo = {
                buildName,
                buildVersion,
                deviceApiLevel: undefined,
                deviceHeight: screenSize.height,
                deviceName: runConfig.appConfig.deviceName ?? simulatorInfo?.name ?? deviceName,
                deviceOsVersion: osVersion ?? platformVersion,
                deviceWidth: screenSize.width,
            };
        }
        testContext.setMobileRunDeviceInfo(mobileRunDeviceInfo);
        variables.initializeMobileVariables(mobileRunDeviceInfo);
    }
}
exports.MobileRunner = MobileRunner;
