"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DOT_NOTATION_PATTERN = void 0;
exports.parseStepIndex = parseStepIndex;
exports.pathToStepIndex = pathToStepIndex;
exports.getStepAtPath = getStepAtPath;
exports.collectStepsInRange = collectStepsInRange;
exports.flattenStepsWithPaths = flattenStepsWithPaths;
exports.findStepById = findStepById;
exports.getStepGroupNestedSteps = getStepGroupNestedSteps;
exports.resolveStepPathFromIdOrIndex = resolveStepPathFromIdOrIndex;
exports.isCursorInsideFlow = isCursorInsideFlow;
exports.getStepsAtCurrentLevel = getStepsAtCurrentLevel;
exports.createNestedStepResolver = createNestedStepResolver;
const mablscript_step_types_1 = require("./mablscript-step-types");
const GET_TEST_CONTEXT_TOOL_NAME = 'get_test_context';
exports.DOT_NOTATION_PATTERN = /^\d+(\.\d+)*$/;
function parseStepIndex(stepIndex) {
    const segments = stepIndex.split('.');
    return segments.map((segment) => {
        const parsed = parseInt(segment, 10);
        if (Number.isNaN(parsed) || parsed < 1) {
            throw new Error(`Invalid step index segment "${segment}" in "${stepIndex}": must be a positive integer`);
        }
        return parsed - 1;
    });
}
function pathToStepIndex(path) {
    return path.map((n) => n + 1).join('.');
}
function getStepAtPath(rootSteps, path, getNestedSteps) {
    if (path.length === 0) {
        return undefined;
    }
    let currentSteps = rootSteps;
    for (let depth = 0; depth < path.length; depth++) {
        const index = path[depth];
        if (index < 0 || index >= currentSteps.length) {
            return undefined;
        }
        const step = currentSteps[index];
        if (depth === path.length - 1) {
            return step;
        }
        const nested = getNestedSteps(step);
        if (!nested) {
            return undefined;
        }
        currentSteps = nested;
    }
    return undefined;
}
function collectStepsInRange(rootSteps, startPath, endPath, getNestedSteps) {
    const startIndex = pathToStepIndex(startPath);
    const endIndex = pathToStepIndex(endPath);
    const startStep = getStepAtPath(rootSteps, startPath, getNestedSteps);
    if (!startStep) {
        throw new Error(`Start step index "${startIndex}" is out of bounds: no step exists at that position`);
    }
    const endStep = getStepAtPath(rootSteps, endPath, getNestedSteps);
    if (!endStep) {
        throw new Error(`End step index "${endIndex}" is out of bounds: no step exists at that position`);
    }
    if (!(0, mablscript_step_types_1.indexLessThanOrEqualTo)(startPath, endPath)) {
        throw new Error(`Start step index "${startIndex}" must come before or equal end step index "${endIndex}"`);
    }
    const flat = flattenStepsWithPaths(rootSteps, getNestedSteps);
    const startFlatIdx = flat.findIndex((entry) => entry.path.length === startPath.length &&
        entry.path.every((v, i) => v === startPath[i]));
    const endFlatIdx = flat.findIndex((entry) => entry.path.length === endPath.length &&
        entry.path.every((v, i) => v === endPath[i]));
    return flat.slice(startFlatIdx, endFlatIdx + 1).map(({ step, index }) => ({
        step,
        index,
    }));
}
function flattenStepsWithPaths(rootSteps, getNestedSteps) {
    const results = [];
    function walk(steps, parentPath) {
        for (let i = 0; i < steps.length; i++) {
            const step = steps[i];
            const currentPath = [...parentPath, i];
            results.push({
                step,
                path: currentPath,
                index: pathToStepIndex(currentPath),
            });
            const nested = getNestedSteps(step);
            if (nested) {
                walk(nested, currentPath);
            }
        }
    }
    walk(rootSteps, []);
    return results;
}
function findStepById(rootSteps, stepId, getNestedSteps) {
    let firstMatch;
    function walk(steps, parentPath) {
        for (let i = 0; i < steps.length; i++) {
            const step = steps[i];
            const currentPath = [...parentPath, i];
            if (step.id === stepId) {
                if (firstMatch) {
                    return true;
                }
                firstMatch = { step, path: currentPath };
            }
            const nested = getNestedSteps(step);
            if (nested && walk(nested, currentPath)) {
                return true;
            }
        }
        return false;
    }
    const ambiguous = walk(rootSteps, []);
    if (ambiguous) {
        return { ambiguous: true };
    }
    return firstMatch;
}
function getStepGroupNestedSteps(step) {
    if ((0, mablscript_step_types_1.isStepGroupStep)(step) && step.stepsInGroup) {
        return step.stepsInGroup;
    }
    return undefined;
}
function resolveStepPathFromIdOrIndex(rootSteps, getNestedSteps, stepId, indexString, fieldNames) {
    if (stepId !== undefined) {
        const resolved = findStepById(rootSteps, stepId, getNestedSteps);
        if (!resolved) {
            return {
                error: `No step found with id "${stepId}"; the test may have changed since the last ${GET_TEST_CONTEXT_TOOL_NAME}. Refetch and retry.`,
            };
        }
        if ('ambiguous' in resolved) {
            return {
                error: `Step id "${stepId}" matches multiple steps; please use ${fieldNames.indexField} instead.`,
            };
        }
        return { path: resolved.path };
    }
    if (indexString === undefined) {
        return {
            error: `Either ${fieldNames.indexField} or ${fieldNames.idField} must be provided.`,
        };
    }
    try {
        return { path: parseStepIndex(indexString) };
    }
    catch (e) {
        return {
            error: `Invalid ${fieldNames.indexField} "${indexString}": ${e.message}`,
        };
    }
}
function isCursorInsideFlow(allSteps, cursorIndex) {
    let steps = allSteps;
    for (let i = 0; i < cursorIndex.length - 1; i++) {
        const step = steps[cursorIndex[i]];
        if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step)) {
            return true;
        }
        if ((0, mablscript_step_types_1.isStepGroupStep)(step) && step.stepsInGroup) {
            steps = step.stepsInGroup;
        }
        else {
            return false;
        }
    }
    return false;
}
function getStepsAtCurrentLevel(allSteps, cursorIndex) {
    let steps = allSteps;
    for (let i = 0; i < cursorIndex.length - 1; i++) {
        const step = steps[cursorIndex[i]];
        if ((0, mablscript_step_types_1.isStepGroupStep)(step) && step.stepsInGroup) {
            steps = step.stepsInGroup;
        }
        else {
            return [];
        }
    }
    return steps;
}
function createNestedStepResolver(getImportedFlowSteps) {
    return (step) => {
        if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
            return step.stepsInGroup;
        }
        if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step)) {
            const flowId = step.flowInvariantId ?? step.instanceId;
            return getImportedFlowSteps(flowId);
        }
        return undefined;
    };
}
