"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;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MobileTestExecutionContext = exports.MAX_STEP_EXECUTION_TIME_MS = void 0;
exports.isMobileTestExecutionContext = isMobileTestExecutionContext;
const messaging_1 = require("@mablhq-internal/runtime/core/messaging/messaging");
const mockObserver_1 = require("@mablhq-internal/runtime/observers/mockObserver");
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 execution_7 = require("@mablhq-internal/runtime/execution");
const execution_8 = require("@mablhq-internal/runtime/execution");
const uuid_1 = require("uuid");
const execution_9 = require("@mablhq-internal/runtime/execution");
const asyncUtil_1 = require("@mablhq-internal/runtime/util/asyncUtil");
const execution_10 = require("@mablhq-internal/runtime/execution");
const MobileTestEnvironment_1 = require("../MobileTestEnvironment");
const tough_cookie_1 = require("tough-cookie");
const AndroidElementContextSwitcher_1 = __importDefault(require("./AndroidElementContextSwitcher"));
const iOSElementContextSwitcher_1 = __importDefault(require("./iOSElementContextSwitcher"));
const execution_11 = require("@mablhq-internal/runtime/execution");
const browserTypes_1 = require("@mablhq-internal/runtime/core/browserTypes");
const testsUtil_1 = require("@mablhq-internal/runtime/core/execution/testsUtil");
const loggingProvider_1 = require("@mablhq-internal/runtime/providers/logging/loggingProvider");
const resourceUtil_1 = require("@mablhq-internal/runtime/util/resourceUtil");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
exports.MAX_STEP_EXECUTION_TIME_MS = 19 * 60 * 1000;
const APPIUM_WEBDRIVER_JS_LOCATION = (0, resourceUtil_1.findResource)('webdriver.js', path.resolve(__dirname, '../../../resources'));
const withMobileStepExecutionTimeout = (promise, step, timeout) => (0, asyncUtil_1.promiseWithTimeout)(promise, timeout, step.getResolvedDescription());
class MobileTestExecutionContext extends execution_1.BaseExecutionContext {
    mobileEnvConfig;
    mablApiClient;
    testContext;
    mablEventEmitter;
    log;
    executorStore;
    testExecutionProxy;
    isMobile = true;
    mobileTestEnvironment;
    cookieJar;
    browser;
    findExecutionShortCircuited;
    recordingWhileTrainingMode = false;
    isStepExecutionInProgress = false;
    contextSwitcher;
    action;
    executionPhase;
    chromeInitHasOcurred = false;
    constructor(mobileEnvConfig, mablApiClient, testContext, mablEventEmitter, log = new execution_8.MobileConsoleLogger(), executorStore = new execution_10.MobileExecutorStore(), currentMobileTestEnv, testExecutionProxy) {
        super(execution_1.TestExecutionContextType.Mobile, mablApiClient, testContext, mablEventEmitter, log, executorStore);
        this.mobileEnvConfig = mobileEnvConfig;
        this.mablApiClient = mablApiClient;
        this.testContext = testContext;
        this.mablEventEmitter = mablEventEmitter;
        this.log = log;
        this.executorStore = executorStore;
        this.testExecutionProxy = testExecutionProxy;
        this.conditionalContext = new execution_3.ConditionalContext();
        this.variables = new execution_7.VariableContext();
        this.variables.initializeVariableState(testContext);
        this.iterationContext = new execution_5.IterationContext(this.variables);
        this.executionIndexTracker = new execution_4.ExecutionIndexTracker(this);
        this.findExecutionShortCircuited = false;
        this.stepNumberContext = new execution_6.StepNumberContext(testContext.flows);
        this.sessionId = (0, uuid_1.v4)();
        this.parentRunnerId = testContext.parentRunnerId;
        this.log.setExecutionContext(this);
        this.observeDeferredFunction = mockObserver_1.noopObserveDeferredFunction;
        this.observeAfterFunction = mockObserver_1.noopObserveAfterFunction;
        this.coreProcessMessageListener =
            this.coreProcessMessageListener.bind(this);
        this.registerExternalMessagingListener();
        this.runState = execution_1.RunState.running;
        if (currentMobileTestEnv) {
            this.mobileTestEnvironment = currentMobileTestEnv;
            this.initializeContextSwitcher();
        }
        this.action = {};
        this.executionPhase = messaging_1.ExecutionPhase.BEFORE_TEST;
        this.executionAgentBridge = new execution_2.MobileExecutionAgentBridge();
    }
    async initializeMobileTestEnvironment() {
        this.executionPhase = messaging_1.ExecutionPhase.BEFORE_TEST;
        this.mobileTestEnvironment = new MobileTestEnvironment_1.MobileTestEnvironment(this.mobileEnvConfig, this.log);
        await this.mobileTestEnvironment.initialize();
        this.initializeContextSwitcher();
        if (this.testExecutionProxy) {
            loggingProvider_1.logger.info('Starting TestExecutionProxy for mobile run');
            await this.testExecutionProxy.start();
            loggingProvider_1.logger.info(`TestExecutionProxy started at ${this.testExecutionProxy.url} with spec ${JSON.stringify(this.testExecutionProxy.proxySpec)}`);
        }
        else {
            loggingProvider_1.logger.info('No TestExecutionProxy configured for mobile run');
        }
    }
    async initializeHeadlessBrowserForSnippetExecution() {
        const browserType = this.testContext.isCloudRun &&
            this.mobileEnvConfig.app.platformName === mablApi_1.MobilePlatformEnum.Ios
            ? browserTypes_1.BrowserType.Webkit
            : browserTypes_1.BrowserType.Chrome;
        this.browser = await (0, testsUtil_1.createBrowser)(execution_11.BrowserTypeToBrowserEngine[browserType], {
            browserWidth: 500,
            browserHeight: 500,
            browserType,
            headless: !this.mobileEnvConfig.showSnippetBrowser,
            loggerFunc: (line) => this.log.info(line, (0, messaging_1.getDefaultLogMetadataForInfo)(this.testContext.test?.id, this.testContext.test?.invariant_id)),
            proxy: this.testExecutionProxy?.proxySpec,
        });
        await this.injectAppiumWebdriverCode();
    }
    async injectAppiumWebdriverCode() {
        if (!this.browser) {
            throw new Error('Could not inject appium code, there is no browser to inject into');
        }
        const frame = (await this.browser.pages())[0].mainFrame();
        await frame.evaluate(fs.readFileSync(APPIUM_WEBDRIVER_JS_LOCATION, 'utf8'));
        const sessionDriverConfig = {
            sessionId: this.mobileTestEnvironment.getDriver().sessionId,
            port: this.mobileTestEnvironment.getRunningAppiumServerPort(),
            hostname: this.mobileTestEnvironment.getRunningAppiumServerHost(),
            isMobile: true,
            isAndroid: this.mobileEnvConfig.app.platformName === mablApi_1.MobilePlatformEnum.Android,
            isIOS: this.mobileEnvConfig.app.platformName === mablApi_1.MobilePlatformEnum.Ios,
            capabilities: {},
        };
        await frame.evaluate((sessionInfo) => window.mabl.mobile.setSessionInfo(sessionInfo), sessionDriverConfig);
    }
    initializeContextSwitcher() {
        const driver = this.mobileTestEnvironment.getDriver();
        this.contextSwitcher =
            this.mobileEnvConfig.app.platformName === mablApi_1.MobilePlatformEnum.Android
                ? (this.contextSwitcher = new AndroidElementContextSwitcher_1.default(driver))
                : (this.contextSwitcher = new iOSElementContextSwitcher_1.default(driver));
    }
    async runStep(step, executor, maxTimeout) {
        if (step.isDisabled()) {
            return this.createSkippedStepResult(step);
        }
        this.action = step;
        this.conditionalContext.prepareForNextStep(step);
        if (this.conditionalContext.stepsAreActive === execution_3.StepsActiveStatus.Active) {
            const stepNumberPrefix = this.getPrefixFromStepNumberContext(step);
            this.currentStepDisplayNumber = stepNumberPrefix
                ?.trim()
                .replace(/\.\s*$/, '');
            this.log.info(this.variables.tryResolveToString(step.getResolvedDescription()), {
                stepNumberPrefix,
            });
            let result;
            try {
                this.isStepExecutionInProgress = true;
                this.executionPhase = messaging_1.ExecutionPhase.DURING_ACTION;
                result = await withMobileStepExecutionTimeout(executor.run(step.toStepDescriptor(), this), step, maxTimeout ??
                    this.mobileEnvConfig.maxStepExecutionTimeout ??
                    exports.MAX_STEP_EXECUTION_TIME_MS);
            }
            catch (e) {
                return {
                    status: execution_9.StepStatus.Failed,
                    logInfo: e.toString(),
                    step,
                };
            }
            finally {
                this.isStepExecutionInProgress = false;
            }
            this.conditionalContext.processStepResponse(step, result.status);
            this.incrementStepIndex();
            result.step = step;
            return result;
        }
        return this.createSkippedStepResult(step);
    }
    async shutdown() {
        try {
            this.isRunning = false;
            this.executionPhase = messaging_1.ExecutionPhase.AFTER_TEST;
            this.action = {
                actionCode: 'shutdown',
            };
            this.unregisterExternalMessagingListener();
            if (this.mobileTestEnvironment && !this.testContext.isCloudRun) {
                try {
                    await this.mobileTestEnvironment.shutdown(true);
                }
                catch (error) {
                    loggingProvider_1.logger.error('Error shutting down mobile test environment from execution context', error);
                }
            }
            if (this.browser?.isConnected() && !this.testContext.isCloudRun) {
                try {
                    await (0, asyncUtil_1.promiseWithTimeout)(this.browser.close(), 5_000, 'Close browser timeout');
                }
                catch (error) {
                    loggingProvider_1.logger.error(`Error while closing the browser during mobile test execution: ${error}`);
                }
            }
        }
        finally {
            await this.testExecutionProxy?.stop();
        }
    }
    getCookieJar() {
        this.cookieJar = this.cookieJar ?? new tough_cookie_1.CookieJar();
        return this.cookieJar;
    }
    isFindExecutionShortCircuited() {
        return this.findExecutionShortCircuited;
    }
    setRecordingWhileTrainingMode(recordingWhileTrainingMode) {
        this.recordingWhileTrainingMode = recordingWhileTrainingMode;
    }
    isRecordingWhileTrainingMode() {
        return this.recordingWhileTrainingMode;
    }
    setShortCircuitFindExecution() {
        this.findExecutionShortCircuited = true;
    }
    clearShortCircuitFindExecution() {
        this.findExecutionShortCircuited = false;
    }
    isRunningStep() {
        return this.isStepExecutionInProgress;
    }
    generateTestCreationContext() {
        const stepName = this.action.getStepName?.();
        return {
            mobilePlatform: this.mobileEnvConfig.app.platformName,
            step_id: this.action.stepId?.(),
            step_context: stepName ? { step_type: stepName } : undefined,
            testInvariantId: this.testContext.test.invariant_id,
            testRunId: this.testContext.runId,
            testType: this.testExecutionContextType,
            training_session_id: this.mobileEnvConfig.cloudTrainingConfiguration?.session?.id,
        };
    }
    getExecutionAgentBridge() {
        return this.executionAgentBridge;
    }
}
exports.MobileTestExecutionContext = MobileTestExecutionContext;
function isMobileTestExecutionContext(context) {
    return context.testExecutionContextType === execution_1.TestExecutionContextType.Mobile;
}
