"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.AgenticGenerationRunner = void 0;
const uuid_1 = require("uuid");
const mablscriptFind_1 = require("@mablhq/test-model/mablscriptFind");
const browserLauncher_1 = require("../../browserLauncher");
const browserTypes_1 = require("../../core/browserTypes");
const TestGenerationOrchestrator_1 = require("./TestGenerationOrchestrator");
const generation_types_1 = require("../sharedUtils/generation-types");
const FlowCache_1 = require("../cli/FlowCache");
const SnippetCache_1 = require("../cli/SnippetCache");
const humanize_1 = require("../sharedUtils/mablscript/humanize");
const messageConversion_1 = require("../sharedUtils/messageConversion");
const mablApi_1 = require("../../mablApi");
const ViewportStep_1 = __importStar(require("../sharedUtils/mablscript/canonical/ViewportStep"));
const VisitUrlStep_1 = __importDefault(require("../sharedUtils/mablscript/canonical/VisitUrlStep"));
const execution_1 = require("../../execution");
const createTrainingTestContext_1 = require("../sharedUtils/createTrainingTestContext");
const errorUtil_1 = require("../../util/errorUtil");
const utilities_1 = require("../utilities");
const saveGeneratedTest_1 = require("./saveGeneratedTest");
const deviceEmulationPresets_1 = require("./deviceEmulationPresets");
const replayStepsForValidation_1 = require("./replayStepsForValidation");
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
const MablscriptImporter_1 = __importDefault(require("../sharedUtils/mablscript/MablscriptImporter"));
const constants_1 = require("../../core/constants");
const CONTEXT_RETRY_DELAY_MS = 1_000;
const MAX_CONTEXT_SETUP_ATTEMPTS = 3;
class AgenticGenerationRunner {
    constructor(config, providers) {
        this.config = config;
        this.providers = providers;
        this.logger = providers.createLogger(config.verbose ?? false);
    }
    async run() {
        try {
            await this.createBrowserAndNavigate();
            await this.createGenerationContextWithRetry();
            const loopResult = await this.runGenerationLoop();
            const result = this.buildResult(loopResult);
            const shouldSave = this.config.autoSave || loopResult.earlyTermination?.save;
            if (shouldSave) {
                const testName = this.config.testName ??
                    this.config.existingTest?.name ??
                    'Generated Test';
                this.logger.info(result.success
                    ? `Auto-saving ${this.config.existingTest ? 'edited' : 'generated'} test...`
                    : 'Saving generated steps after failure...');
                result.saveResult = await this.saveTest(testName, {
                    testDescription: this.config.testDescription,
                    labels: this.config.labels,
                });
            }
            await this.finalizeAgentSession(loopResult, result.saveResult);
            return result;
        }
        catch (error) {
            return await this.handleError(error);
        }
        finally {
            await this.cleanup();
        }
    }
    async finalizeAgentSession(loopResult, saveResult) {
        const sessionId = this.config.agentSessionId;
        if (!sessionId) {
            this.logger.info('Skipping session finalization: no agent session ID available');
            return;
        }
        try {
            const pendingToolCall = loopResult.pendingAskForHelp
                ? {
                    toolCallId: loopResult.pendingAskForHelp.toolCallId,
                    toolName: 'execute_ask_for_help',
                    siblingToolOutputParts: [],
                }
                : loopResult.pendingCompleteTask
                    ? {
                        toolCallId: loopResult.pendingCompleteTask.toolCallId,
                        toolName: 'execute_complete_task',
                        siblingToolOutputParts: loopResult.pendingCompleteTask.siblingToolOutputParts,
                    }
                    : loopResult.pendingFailedToolCall
                        ? {
                            toolCallId: loopResult.pendingFailedToolCall.toolCallId,
                            toolName: loopResult.pendingFailedToolCall.toolName,
                            siblingToolOutputParts: loopResult.pendingFailedToolCall.siblingToolOutputParts,
                        }
                        : undefined;
            if (!pendingToolCall) {
                await this.resolveOrphanedToolCalls(sessionId, loopResult, saveResult);
                return;
            }
            const savedSuccessfully = saveResult?.success && saveResult.testId;
            const isFailedToolCall = !!loopResult.pendingFailedToolCall;
            const toolCallSuccess = isFailedToolCall ? false : !!savedSuccessfully;
            function getToolCallMessage() {
                if (isFailedToolCall) {
                    return loopResult.failureReason ?? 'Generation failed';
                }
                if (savedSuccessfully) {
                    return 'Test saved';
                }
                return (saveResult?.error || loopResult.failureReason || 'Generation failed');
            }
            const toolCallMessage = getToolCallMessage();
            const toolOutputMessage = (0, messageConversion_1.createToolOutputMessage)([
                ...pendingToolCall.siblingToolOutputParts,
                (0, messageConversion_1.createToolOutputMessagePart)({
                    toolCallId: pendingToolCall.toolCallId,
                    toolName: pendingToolCall.toolName,
                    result: {
                        success: toolCallSuccess,
                        testId: saveResult?.testId,
                        testUrl: saveResult?.testUrl,
                        message: toolCallMessage,
                    },
                }),
            ]);
            const artifacts = [];
            if (savedSuccessfully) {
                artifacts.push({
                    artifact_type: 'remote',
                    entity_kind: mablApi_1.ArtifactEntityKind.Test,
                    entity_id: saveResult.testId,
                    entity_name: saveResult.savedTest?.name,
                });
            }
            this.logger.info(`Finalizing agent session ${sessionId}${saveResult?.testId ? ` with saved test ${saveResult.testId}` : ''}`);
            await this.config.apiClient.appendAgentSessionMessage(sessionId, toolOutputMessage, artifacts.length > 0 ? artifacts : undefined);
            this.logger.info('Agent session finalized successfully');
        }
        catch (error) {
            this.logger.error('Failed to finalize agent session', error instanceof Error ? error : undefined);
        }
    }
    async resolveOrphanedToolCalls(sessionId, loopResult, saveResult) {
        const session = await this.config.apiClient.getAgentSession(sessionId);
        const allToolCalls = (0, messageConversion_1.extractToolCallsFromMessages)(session.messages, this.logger);
        const clientToolCalls = (0, messageConversion_1.filterClientSideToolCalls)(allToolCalls);
        const unansweredToolCalls = (0, messageConversion_1.filterToolCallsWithoutOutputs)(clientToolCalls, session.messages);
        const savedSuccessfully = saveResult?.success && saveResult.testId;
        const artifacts = [];
        if (savedSuccessfully) {
            artifacts.push({
                artifact_type: 'remote',
                entity_kind: mablApi_1.ArtifactEntityKind.Test,
                entity_id: saveResult.testId,
                entity_name: saveResult.savedTest?.name,
            });
        }
        if (unansweredToolCalls.length > 0) {
            this.logger.info(`Found ${unansweredToolCalls.length} unanswered tool call(s) in session — responding with error`);
            const generationFailed = loopResult.status === generation_types_1.TestGenerationStatus.Failed;
            function getErrorMessage() {
                if (generationFailed) {
                    return loopResult.failureReason ?? 'Generation failed';
                }
                if (savedSuccessfully) {
                    return 'Test saved';
                }
                return loopResult.failureReason ?? 'Generation failed';
            }
            const errorMessage = getErrorMessage();
            const toolOutputParts = unansweredToolCalls.map((tc) => (0, messageConversion_1.createToolOutputMessagePart)({
                toolCallId: tc.id,
                toolName: tc.name,
                result: {
                    success: generationFailed ? false : !!savedSuccessfully,
                    testId: saveResult?.testId,
                    testUrl: saveResult?.testUrl,
                    message: errorMessage,
                },
            }));
            const toolOutputMessage = (0, messageConversion_1.createToolOutputMessage)(toolOutputParts);
            await this.config.apiClient.appendAgentSessionMessage(sessionId, toolOutputMessage, artifacts.length > 0 ? artifacts : undefined);
        }
        else if (artifacts.length > 0) {
            this.logger.info(`No pending tool calls — associating saved test ${saveResult.testId} with session ${sessionId}`);
            const message = (0, messageConversion_1.createErrorTextMessage)(loopResult.failureReason ?? 'Generation ended');
            await this.config.apiClient.appendAgentSessionMessage(sessionId, message, artifacts);
        }
        else {
            this.logger.info('No pending tool calls to finalize — session may have ended cleanly');
        }
    }
    async createBrowserAndNavigate() {
        this.logger.info('Creating browser and navigating to URL');
        const deviceEmulation = this.config.devicePreset
            ? (0, deviceEmulationPresets_1.resolveDeviceEmulation)(this.config.devicePreset, this.config.deviceOrientation)
            : undefined;
        const hasFullConfig = !!deviceEmulation?.device_config?.screen;
        const { testContext, accountFeatures } = await (0, createTrainingTestContext_1.createTrainingTestContext)({
            workspaceId: this.config.workspaceId,
            applicationId: this.config.applicationId,
            url: this.config.url,
            apiClient: this.config.apiClient,
            idPrefix: 'gen',
            deviceEmulation: hasFullConfig ? deviceEmulation : undefined,
        });
        this.accountFeatures = accountFeatures;
        if (this.config.scenarioId) {
            try {
                const scenario = await this.config.apiClient.getScenario(this.config.scenarioId);
                testContext.setScenario(scenario);
                this.logger.info(`Loaded datatable scenario: ${scenario.name ?? this.config.scenarioId}`);
            }
            catch (error) {
                this.logger.warn(`Failed to load datatable scenario ${this.config.scenarioId}: ${error instanceof Error ? error.message : String(error)}`);
            }
        }
        if (this.config.existingTest) {
            this.logger.info(`Editing existing test: ${this.config.existingTest.name ?? this.config.testId} ` + `(${this.config.existingFlows?.length ?? 0} flows)`);
        }
        let credential = this.config.credentials;
        if (!credential && this.config.credentialsId) {
            this.logger.info('Resolving credentials for training session');
            credential = await this.config.apiClient.getCredential(this.config.credentialsId);
        }
        if (credential) {
            if (credential.cloud_only && !this.config.isCloud) {
                this.logger.warn('Cloud credentials cannot be used in local training sessions. Skipping credential setup.');
            }
            else {
                testContext.credentials = credential;
            }
        }
        const isLandscape = deviceEmulation?.orientation === 'landscape_primary' ||
            deviceEmulation?.orientation === 'landscape_secondary';
        const emulationScreen = hasFullConfig
            ? isLandscape
                ? deviceEmulation.device_config.screen?.horizontal
                : deviceEmulation.device_config.screen?.vertical
            : undefined;
        const effectiveWidth = emulationScreen?.width ??
            this.config.viewportWidth ??
            ViewportStep_1.DEFAULT_VIEWPORT_SIZE.width;
        const effectiveHeight = emulationScreen?.height ??
            this.config.viewportHeight ??
            ViewportStep_1.DEFAULT_VIEWPORT_SIZE.height;
        const browserConfig = {
            height: effectiveHeight,
            width: effectiveWidth,
            headmode: this.config.headless ? execution_1.Headmode.Headless : execution_1.Headmode.Headful,
            browserName: 'chrome',
            browserType: browserTypes_1.BrowserType.Chrome,
            disableFocusEmulation: true,
            minimizeScrollIntoView: true,
            visitUrlTimeout: 5000,
            ignoreCertificateErrors: true,
            proxy: this.config.proxy,
            deviceEmulationConfig: hasFullConfig ? deviceEmulation : undefined,
        };
        this.logger.info('Launching browser');
        this.stepExecutor = new execution_1.BrowserStepExecutor(testContext, browserConfig, this.config.apiClient, this.config.verbose ?? false);
        this.browser = await this.stepExecutor.initializeNewBrowser();
        if (!this.browser) {
            throw new Error('Failed to launch browser');
        }
        const executionContext = this.stepExecutor.getExecutionContext();
        this.page = executionContext.pageFrameContext.getCurrentPage();
        if (!this.page) {
            throw new Error('Failed to get page from execution context');
        }
        this.logger.info(`Navigating to ${this.config.url}`);
        try {
            await this.page.goto(this.config.url, {
                waitUntil: browserLauncher_1.LifecycleEvent.NetworkIdle,
                timeout: execution_1.NAVIGATE_TIMEOUT_MILLISECONDS,
            });
        }
        catch {
            this.logger.info('Page did not reach network idle within timeout, proceeding');
        }
    }
    async createGenerationContextWithRetry() {
        for (let attempt = 1; attempt <= MAX_CONTEXT_SETUP_ATTEMPTS; attempt++) {
            try {
                await this.createGenerationContext();
                return;
            }
            catch (error) {
                const isRetriable = error instanceof Error && (0, errorUtil_1.isRetriableError)(error);
                if (!isRetriable || attempt === MAX_CONTEXT_SETUP_ATTEMPTS) {
                    throw error;
                }
                this.logger.warn(`Generation context setup failed on attempt ${attempt}/${MAX_CONTEXT_SETUP_ATTEMPTS}, ` +
                    `retrying after page settles: ${error instanceof Error ? error.message : String(error)}`);
                await (0, utilities_1.sleep)(CONTEXT_RETRY_DELAY_MS);
            }
        }
    }
    async createGenerationContext() {
        this.logger.info('Creating generation context');
        if (!this.page || !this.stepExecutor) {
            throw new Error('Browser not initialized - call createBrowserAndNavigate first');
        }
        this.flowCache = new FlowCache_1.FlowCache({
            apiClient: this.config.apiClient,
            workspaceId: this.config.workspaceId,
            branchName: this.config.branchName ?? constants_1.DefaultBranchName,
        });
        this.state = this.providers.createState({
            testIntent: this.config.testIntent,
            url: this.config.url,
            workspaceId: this.config.workspaceId,
            applicationId: this.config.applicationId,
        });
        this.trainingContext = this.providers.createTrainingContext(this.page, this.browser, (awaitTabStep) => {
            void this.eventHandler?.dispatchSteps([awaitTabStep], {
                description: 'Wait for new tab',
            });
        });
        await this.trainingContext.initialize();
        const snippetCache = new SnippetCache_1.SnippetCache({
            apiClient: this.config.apiClient,
            workspaceId: this.config.workspaceId,
        });
        this.stateProvider = this.providers.createStateProvider(this.state, this.trainingContext, this.flowCache, this.accountFeatures, snippetCache);
        this.eventHandler = this.providers.createEventHandler({
            state: this.state,
            trainingContext: this.trainingContext,
            flowCache: this.flowCache,
            stepExecutor: this.stepExecutor,
            verbose: this.config.verbose,
        });
        const trainingSessionId = this.state.getTrainingSessionId();
        this.activeSession = {
            sessionId: (0, uuid_1.v4)(),
            trainingSessionId,
            stopCondition: this.config.stopCondition ?? 'generated-test',
            completedTaskCount: 0,
        };
        this.state.setActiveSession(this.activeSession);
        this.state.setStatus(generation_types_1.TestGenerationStatus.LearningSteps);
        const deps = {
            takeScreenshot: async () => this.trainingContext.takeScreenshot(),
            getPageSnapshot: async () => {
                const page = await this.trainingContext.getActivePage();
                return page?.ariaSnapshot();
            },
            trainingContext: this.trainingContext,
            getMablApiClient: () => this.config.apiClient,
            getWorkspaceId: () => this.config.workspaceId,
            getAppVersion: () => this.config.appVersion,
            validateTest: (options) => this.validateTest(options),
            isLocalTrainer: false,
            accessibilityChecksEnabled: this.accountFeatures?.hasAccessibilityChecksEnabled() ?? false,
        };
        this.orchestrator = new TestGenerationOrchestrator_1.TestGenerationOrchestrator(this.stateProvider, this.eventHandler, this.logger, deps);
        this.setupSessionState();
    }
    setupSessionState() {
        if (!this.state) {
            throw new Error('State not initialized');
        }
        this.logger.info(`Using agent session: ${this.config.agentSessionId}`);
        this.state.setAuthoringSessionId(this.config.agentSessionId);
        for (const artifact of this.config.initialArtifacts) {
            if (artifact.artifact_type === 'inline') {
                const inlineArtifact = artifact;
                if (inlineArtifact.name) {
                    this.logger.info(`Loaded artifact: ${inlineArtifact.name}`);
                    this.state.setArtifact(inlineArtifact.name, artifact);
                }
            }
        }
        if (this.config.existingFlows && this.config.existingFlows.length > 0) {
            const existingSteps = [];
            const importer = new MablscriptImporter_1.default({ steps: [], flowSelectors: this.state.getElementSelectors() }, this.state.getVariables(), this.state.getSnippetsByInvariantId());
            for (const flow of this.config.existingFlows) {
                importer.importFlow(flow, existingSteps);
            }
            if (existingSteps.length > 0) {
                this.state.addSteps(existingSteps);
                this.logger.info(`Pre-populated state with ${existingSteps.length} existing steps from ${this.config.existingFlows.length} flow(s)`);
            }
        }
        if (this.config.initialMessages.length > 0) {
            this.state.addAuthoringMessages(this.config.initialMessages);
            this.state.setLastSentMessageCount(this.config.initialMessages.length);
            this.logger.info(`Loaded ${this.config.initialMessages.length} existing messages from session`);
        }
        const hasTestInfo = this.config.initialArtifacts.some((a) => a.artifact_type === 'inline' &&
            a.name === 'test_information');
        const hasTestOutline = this.config.initialArtifacts.some((a) => a.artifact_type === 'inline' &&
            a.name === 'test_outline');
        if (hasTestInfo) {
            this.logger.info('Found test_information artifact');
        }
        if (hasTestOutline) {
            this.logger.info('Found test_outline artifact');
        }
    }
    async runGenerationLoop() {
        this.logger.info('Running generation loop');
        if (!this.orchestrator || !this.activeSession || !this.state) {
            throw new Error('Generation context not initialized - call createGenerationContext first');
        }
        this.eventHandler.addAgentLogEntry({
            type: generation_types_1.AgentLogEntryType.TestGenerationStarted,
            stopCondition: this.activeSession.stopCondition,
        });
        const loopRunResult = await this.orchestrator.runGenerationLoop(this.activeSession, this.config.shouldContinueGenerationCheck);
        loopRunResult.cleanup();
        return {
            status: this.state.getStatus(),
            steps: this.state.getSteps(),
            variables: this.state.getVariables(),
            trainingSessionId: this.state.getTrainingSessionId(),
            failureReason: loopRunResult.failureReason,
            earlyTermination: loopRunResult.earlyTermination,
            pendingAskForHelp: loopRunResult.pendingAskForHelp,
            pendingCompleteTask: loopRunResult.pendingCompleteTask,
            pendingFailedToolCall: loopRunResult.pendingFailedToolCall,
        };
    }
    buildResult(loopResult) {
        const humanizedSteps = loopResult.steps.map((step) => step.annotations?.description ||
            (0, humanize_1.humanizeStepDescription)(step, loopResult.variables));
        if (this.providers.processResult) {
            this.providers.processResult(loopResult);
        }
        if (loopResult.pendingAskForHelp) {
            return {
                success: false,
                steps: humanizedSteps,
                trainingSessionId: loopResult.trainingSessionId,
                status: loopResult.status,
                rawSteps: loopResult.steps,
                variables: loopResult.variables,
                error: loopResult.pendingAskForHelp.question,
            };
        }
        const success = loopResult.status === generation_types_1.TestGenerationStatus.Completed ||
            loopResult.status === generation_types_1.TestGenerationStatus.Paused ||
            loopResult.status === generation_types_1.TestGenerationStatus.Off;
        return {
            success,
            steps: humanizedSteps,
            trainingSessionId: loopResult.trainingSessionId,
            status: loopResult.status,
            rawSteps: loopResult.steps,
            variables: loopResult.variables,
            error: loopResult.status === generation_types_1.TestGenerationStatus.Failed
                ? loopResult.failureReason ?? 'Generation failed'
                : undefined,
        };
    }
    async handleError(error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        this.logger.error('Generation failed', error instanceof Error ? error : undefined);
        let saveResult;
        if (this.config.autoSave && this.config.testName) {
            saveResult = await this.saveErrorRecoveryTest();
        }
        await this.reportErrorToSession(errorMessage, saveResult?.testId);
        return {
            success: false,
            error: errorMessage,
            fatalError: error instanceof Error ? error : new Error(errorMessage),
            steps: [],
            trainingSessionId: this.state?.getTrainingSessionId() ?? '',
            status: generation_types_1.TestGenerationStatus.Failed,
            rawSteps: [],
            variables: this.state?.getVariables() ?? {},
            saveResult,
        };
    }
    async reportErrorToSession(errorMessage, testId) {
        const sessionId = this.config.agentSessionId;
        if (!sessionId) {
            return;
        }
        try {
            const message = (0, messageConversion_1.createErrorTextMessage)(`Generation failed: ${errorMessage}`);
            const artifacts = [];
            if (testId) {
                artifacts.push({
                    artifact_type: 'remote',
                    entity_kind: mablApi_1.ArtifactEntityKind.Test,
                    entity_id: testId,
                });
            }
            await this.config.apiClient.appendAgentSessionMessage(sessionId, message, artifacts.length > 0 ? artifacts : undefined);
            this.logger.info('Posted error message to agent session');
        }
        catch (reportError) {
            this.logger.error('Failed to post error message to agent session', reportError instanceof Error ? reportError : undefined);
        }
    }
    async saveErrorRecoveryTest() {
        try {
            const existingSteps = this.state?.getSteps() ?? [];
            const steps = existingSteps.length > 0
                ? existingSteps
                : [
                    new ViewportStep_1.default({
                        width: this.config.viewportWidth ?? ViewportStep_1.DEFAULT_VIEWPORT_SIZE.width,
                        height: this.config.viewportHeight ?? ViewportStep_1.DEFAULT_VIEWPORT_SIZE.height,
                    }),
                    new VisitUrlStep_1.default('', {
                        name: 'web.defaults.url',
                        humanizedName: 'app.url',
                        readOnly: true,
                        variableType: mablscriptFind_1.VariableType.INPUT,
                        flowStepIndex: [],
                    }),
                ];
            const variables = this.state?.getVariables();
            const stepDescriptions = steps.map((step) => step.annotations?.description ||
                (0, humanize_1.humanizeStepDescription)(step, variables ?? {}));
            const result = await (0, saveGeneratedTest_1.saveGeneratedTest)({
                apiClient: this.config.apiClient,
                workspaceId: this.config.workspaceId,
                applicationId: this.config.applicationId,
                url: this.config.url,
                testName: this.config.testName,
                testDescription: this.config.testDescription ?? this.config.testIntent,
                labels: this.config.labels,
                datatables: this.config.datatables,
                rawSteps: steps,
                stepDescriptions,
                credentialsId: this.config.credentialsId,
                trainingSessionId: this.state?.getTrainingSessionId(),
                agentSessionId: this.config.agentSessionId,
                branchId: this.config.branchId,
                planId: this.config.planId,
                devicePreset: this.config.devicePreset,
                deviceOrientation: this.config.deviceOrientation,
                scenarioId: this.config.scenarioId,
                existingTest: this.config.existingTest,
            });
            if (result.success) {
                this.logger.info(`Saved error recovery test: ${result.testId}`);
            }
            else {
                this.logger.error(`Failed to save error recovery test: ${result.error}`);
            }
            return result;
        }
        catch (saveError) {
            const message = saveError instanceof Error
                ? saveError.message
                : 'Unknown error saving error recovery test';
            this.logger.error('Failed to save error recovery test', saveError instanceof Error ? saveError : undefined);
            return { success: false, error: message };
        }
    }
    async cleanup() {
        if (this.browser) {
            try {
                await this.browser.close();
            }
            catch {
            }
        }
    }
    async validateTest(options) {
        if (!this.state) {
            return {
                success: false,
                stepResults: [],
                totalDurationMs: 0,
                error: 'No generation state — run generation first',
            };
        }
        const steps = this.state.getSteps();
        if (steps.length === 0) {
            return {
                success: false,
                stepResults: [],
                totalDurationMs: 0,
                error: 'No steps to validate',
            };
        }
        const startUrl = options?.url ?? this.config.url;
        const stopOnFirstFailure = options?.stop_on_first_failure ?? false;
        this.logger.info(`Validating ${steps.length} steps in a fresh browser at ${startUrl}`);
        await this.cleanup();
        try {
            await this.createBrowserAndNavigate();
            if (startUrl !== this.config.url && this.page) {
                this.logger.info(`Navigating to override URL: ${startUrl}`);
                await this.page.goto(startUrl, {
                    waitUntil: browserLauncher_1.LifecycleEvent.DomContentLoaded,
                });
            }
            await this.recreateTrainingContext();
            const snippets = this.state.getSnippetsByInvariantId();
            for (const snippet of Object.values(snippets)) {
                if (snippet.reusable) {
                    this.stepExecutor.setSnippet(snippet);
                }
            }
            const fileRefs = collectFileRefsFromSteps(steps);
            if (fileRefs.length > 0) {
                await this.stepExecutor.downloadFileUploadsAndSetReference(fileRefs);
            }
        }
        catch (error) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            this.logger.error('Failed to launch validation browser', error instanceof Error ? error : undefined);
            return {
                success: false,
                stepResults: [],
                totalDurationMs: 0,
                error: `Failed to launch validation browser: ${message}`,
            };
        }
        return (0, replayStepsForValidation_1.replayStepsForValidation)({
            steps,
            stepExecutor: this.stepExecutor,
            variables: this.state.getVariables(),
            logger: this.logger,
            stopOnFirstFailure,
            getImportedFlowSteps: (flowInvariantId) => this.stateProvider?.getImportedFlowSteps(flowInvariantId),
        });
    }
    async recreateTrainingContext() {
        if (!this.page || !this.browser || !this.stepExecutor || !this.state) {
            throw new Error('Browser and state must be initialized before recreating training context');
        }
        this.trainingContext = this.providers.createTrainingContext(this.page, this.browser, (awaitTabStep) => {
            void this.eventHandler?.dispatchSteps([awaitTabStep], {
                description: 'Wait for new tab',
            });
        });
        await this.trainingContext.initialize();
        if (!this.flowCache) {
            this.flowCache = new FlowCache_1.FlowCache({
                apiClient: this.config.apiClient,
                workspaceId: this.config.workspaceId,
                branchName: this.config.branchName ?? constants_1.DefaultBranchName,
            });
        }
        this.stateProvider = this.providers.createStateProvider(this.state, this.trainingContext, this.flowCache, this.accountFeatures);
        this.eventHandler = this.providers.createEventHandler({
            state: this.state,
            trainingContext: this.trainingContext,
            flowCache: this.flowCache,
            stepExecutor: this.stepExecutor,
            verbose: this.config.verbose,
        });
        if (this.orchestrator) {
            this.orchestrator.updateDependencies({
                takeScreenshot: async () => this.trainingContext.takeScreenshot(),
                getPageSnapshot: async () => {
                    const page = await this.trainingContext.getActivePage();
                    return page?.ariaSnapshot();
                },
                trainingContext: this.trainingContext,
            });
            this.orchestrator.updateProviders(this.stateProvider, this.eventHandler);
        }
    }
    async saveTest(testName, options) {
        if (!this.state) {
            return {
                success: false,
                error: 'No generation state - run generation first',
            };
        }
        const rawSteps = this.state.getSteps();
        if (rawSteps.length === 0) {
            return { success: false, error: 'No steps to save' };
        }
        this.logger.info(`Saving test: ${testName}`);
        const stepDescriptions = rawSteps.map((step) => step.annotations?.description ||
            (0, humanize_1.humanizeStepDescription)(step, this.state.getVariables()));
        const result = await (0, saveGeneratedTest_1.saveGeneratedTest)({
            apiClient: this.config.apiClient,
            workspaceId: this.config.workspaceId,
            applicationId: this.config.applicationId,
            url: this.config.url,
            testName,
            testDescription: options?.testDescription ??
                this.config.testDescription ??
                this.config.testIntent,
            labels: options?.labels ?? this.config.labels,
            datatables: this.config.datatables,
            rawSteps,
            stepDescriptions,
            credentialsId: this.config.credentialsId,
            trainingSessionId: this.state.getTrainingSessionId(),
            agentSessionId: this.config.agentSessionId,
            branchId: this.config.branchId,
            planId: this.config.planId,
            devicePreset: this.config.devicePreset,
            deviceOrientation: this.config.deviceOrientation,
            scenarioId: this.config.scenarioId,
            existingTest: this.config.existingTest,
        });
        if (result.success) {
            this.logger.info(`${this.config.existingTest ? 'Updated' : 'Created'} test: ${result.testId}`);
        }
        else {
            this.logger.error(`Failed to save test: ${result.error}`);
        }
        return result;
    }
    getBrowser() {
        return this.browser;
    }
    getStepExecutor() {
        return this.stepExecutor;
    }
    getFlowCache() {
        return this.flowCache;
    }
    getState() {
        return this.state;
    }
}
exports.AgenticGenerationRunner = AgenticGenerationRunner;
function collectFileRefsFromSteps(steps) {
    const seen = new Set();
    const refs = [];
    const visit = (step) => {
        if (step.stepType === mablscript_step_types_1.StepType.SET_FILES) {
            for (const file of step.files) {
                if (file.id && !seen.has(file.id)) {
                    seen.add(file.id);
                    refs.push({
                        id: file.id,
                        name: file.name,
                        size: file.size,
                        type: file.type,
                    });
                }
            }
        }
        if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
            for (const child of step.stepsInGroup) {
                visit(child);
            }
        }
    };
    for (const step of steps) {
        visit(step);
    }
    return refs;
}
