"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.replayStepsForValidation = replayStepsForValidation;
const TestResult_1 = require("../../core/execution/TestResult");
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
const humanize_1 = require("../sharedUtils/mablscript/humanize");
const stepIndexUtils_1 = require("../sharedUtils/stepIndexUtils");
async function replayStepsForValidation(options) {
    const { steps, stepExecutor, variables, logger, stopOnFirstFailure = false, getImportedFlowSteps, } = options;
    if (steps.length === 0) {
        return {
            success: false,
            stepResults: [],
            totalDurationMs: 0,
            error: 'No steps to validate',
        };
    }
    const stepResults = [];
    const screenshotImages = [];
    const validationStart = Date.now();
    let stepCount = 0;
    const captureStepScreenshot = async () => {
        try {
            const imageData = await stepExecutor.captureScreenshot();
            if (!imageData) {
                return;
            }
            screenshotImages.push({
                type: 'image-data',
                image_data: imageData,
                media_type: 'image/png',
            });
        }
        catch {
        }
    };
    let conditionalSkipDepth = 0;
    const conditionalBranchTaken = [];
    const getChildSteps = (step) => {
        if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
            return step.stepsInGroup;
        }
        if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) &&
            step.flowInvariantId &&
            getImportedFlowSteps) {
            return getImportedFlowSteps(step.flowInvariantId);
        }
        return undefined;
    };
    const cursor = [0];
    while (cursor.length > 0) {
        const step = (0, stepIndexUtils_1.getStepAtPath)(steps, cursor, getChildSteps);
        if (!step) {
            cursor.pop();
            if (cursor.length > 0) {
                cursor[cursor.length - 1]++;
            }
            continue;
        }
        const children = getChildSteps(step);
        if (children && children.length > 0) {
            cursor.push(0);
            continue;
        }
        if (children !== undefined && children.length === 0) {
            cursor[cursor.length - 1]++;
            continue;
        }
        stepCount++;
        const stepStart = Date.now();
        const description = step.annotations?.description || (0, humanize_1.humanizeStepDescription)(step, variables);
        if (conditionalSkipDepth > 0) {
            if ((0, mablscript_step_types_1.isIfOrElseIfStep)(step)) {
                conditionalSkipDepth++;
            }
            else if ((0, mablscript_step_types_1.isEndStep)(step)) {
                conditionalSkipDepth--;
                if (conditionalSkipDepth === 0) {
                    conditionalBranchTaken.pop();
                }
            }
            else if ((0, mablscript_step_types_1.isElseStep)(step) &&
                conditionalSkipDepth === 1 &&
                !conditionalBranchTaken[conditionalBranchTaken.length - 1]) {
                conditionalSkipDepth = 0;
                conditionalBranchTaken[conditionalBranchTaken.length - 1] = true;
            }
            if (conditionalSkipDepth > 0 || (0, mablscript_step_types_1.isElseStep)(step) || (0, mablscript_step_types_1.isEndStep)(step)) {
                stepResults.push({
                    stepIndex: stepCount - 1,
                    stepDescription: description,
                    status: 'skipped',
                    durationMs: Date.now() - stepStart,
                });
                cursor[cursor.length - 1]++;
                continue;
            }
        }
        if ((0, mablscript_step_types_1.isElseStep)(step)) {
            conditionalSkipDepth = 1;
            stepResults.push({
                stepIndex: stepCount - 1,
                stepDescription: description,
                status: 'skipped',
                durationMs: Date.now() - stepStart,
            });
            cursor[cursor.length - 1]++;
            continue;
        }
        if ((0, mablscript_step_types_1.isEndStep)(step)) {
            conditionalBranchTaken.pop();
            stepResults.push({
                stepIndex: stepCount - 1,
                stepDescription: description,
                status: 'skipped',
                durationMs: Date.now() - stepStart,
            });
            cursor[cursor.length - 1]++;
            continue;
        }
        logger.info(`Validating step ${stepCount}: ${description}`);
        let stopAfterStep = false;
        try {
            const mablscript = step.generateMablscript();
            const result = await stepExecutor.executeSingleStepForReplay(mablscript);
            const durationMs = Date.now() - stepStart;
            const passed = result.status === TestResult_1.StepStatus.Passed;
            if ((0, mablscript_step_types_1.isIfOrElseIfStep)(step)) {
                if (passed) {
                    conditionalBranchTaken.push(true);
                    stepResults.push({
                        stepIndex: stepCount - 1,
                        stepDescription: description,
                        status: 'pass',
                        durationMs,
                    });
                }
                else {
                    conditionalBranchTaken.push(false);
                    conditionalSkipDepth = 1;
                    stepResults.push({
                        stepIndex: stepCount - 1,
                        stepDescription: description,
                        status: 'skipped',
                        errorMessage: `Conditional evaluated to false: ${result.logInfo ?? 'no details'}`,
                        durationMs,
                    });
                }
            }
            else {
                const validationStatus = classifyExecutorStatus(result.status);
                const failed = validationStatus === 'fail';
                const failureReason = result.assertFailure?.reason;
                const errorMessage = failed
                    ? result.logInfo ||
                        failureReason ||
                        `Step failed (${result.status}, ${durationMs}ms — no logInfo)`
                    : validationStatus === 'skipped'
                        ? `Skipped by executor (${result.status})`
                        : undefined;
                stepResults.push({
                    stepIndex: stepCount - 1,
                    stepDescription: description,
                    status: validationStatus,
                    errorMessage,
                    failureReason,
                    expected: result.assertFailure?.expected !== undefined
                        ? String(result.assertFailure.expected)
                        : undefined,
                    actual: result.assertFailure?.found !== undefined
                        ? String(result.assertFailure.found)
                        : undefined,
                    durationMs,
                });
                if (failed) {
                    logger.warn(`Step ${stepCount} failed: ${errorMessage}`);
                    if (stopOnFirstFailure) {
                        stopAfterStep = true;
                    }
                }
            }
        }
        catch (error) {
            const durationMs = Date.now() - stepStart;
            const errorMessage = error instanceof Error ? error.message : 'Unexpected error';
            if ((0, mablscript_step_types_1.isIfOrElseIfStep)(step)) {
                conditionalBranchTaken.push(false);
                conditionalSkipDepth = 1;
                stepResults.push({
                    stepIndex: stepCount - 1,
                    stepDescription: description,
                    status: 'skipped',
                    errorMessage: `Conditional threw during replay: ${errorMessage}`,
                    durationMs,
                });
            }
            else {
                stepResults.push({
                    stepIndex: stepCount - 1,
                    stepDescription: description,
                    status: 'fail',
                    errorMessage,
                    durationMs,
                });
                logger.error(`Step ${stepCount} threw an exception`, error instanceof Error ? error : undefined);
                if (stopOnFirstFailure) {
                    stopAfterStep = true;
                }
            }
        }
        await captureStepScreenshot();
        if (stopAfterStep) {
            break;
        }
        cursor[cursor.length - 1]++;
    }
    const totalDurationMs = Date.now() - validationStart;
    const failedCount = stepResults.filter((r) => r.status === 'fail').length;
    const skippedCount = stepResults.filter((r) => r.status === 'skipped').length;
    const allPassed = failedCount === 0;
    logger.info(allPassed
        ? `Validation passed: ${stepCount} step(s) executed in ${totalDurationMs}ms (${skippedCount} conditional skip(s))`
        : `Validation failed: ${failedCount} of ${stepCount} step(s) failed in ${totalDurationMs}ms`);
    return {
        success: allPassed,
        stepResults,
        totalDurationMs,
        screenshotImages: screenshotImages.length > 0 ? screenshotImages : undefined,
    };
}
function classifyExecutorStatus(status) {
    switch (status) {
        case TestResult_1.StepStatus.Passed:
            return 'pass';
        case TestResult_1.StepStatus.Skipped:
        case TestResult_1.StepStatus.NestedSkipped:
        case TestResult_1.StepStatus.Pending:
            return 'skipped';
        case TestResult_1.StepStatus.Failed:
        case TestResult_1.StepStatus.NotImplemented:
            return 'fail';
    }
}
