"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createJsonArtifact = createJsonArtifact;
exports.buildTestExecutionStateArtifact = buildTestExecutionStateArtifact;
exports.buildToolContextArtifact = buildToolContextArtifact;
exports.buildCurrentApplicationStateArtifact = buildCurrentApplicationStateArtifact;
exports.buildTestInfoArtifact = buildTestInfoArtifact;
exports.buildAllArtifacts = buildAllArtifacts;
const variable_utils_1 = require("../sharedUtils/variable-utils");
const humanize_1 = require("../sharedUtils/mablscript/humanize");
const type_utils_1 = require("../sharedUtils/type-utils");
const contextBuilder_1 = require("../sharedUtils/contextBuilder");
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
const stepIndexUtils_1 = require("../sharedUtils/stepIndexUtils");
const uuid_1 = require("uuid");
function createJsonArtifact(id, name, description, value) {
    return {
        artifact_type: 'inline',
        id,
        name,
        description,
        artifact_data: {
            type: 'json',
            value,
        },
    };
}
const MAX_FLOW_EXPANSION_DEPTH = 10;
function expandNestedSteps(steps, stepList, variables, state, depth) {
    for (const step of steps) {
        if (depth >= MAX_FLOW_EXPANSION_DEPTH) {
            stepList.push(safeHumanize(step, variables));
        }
        else if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) &&
            step.flow &&
            !(0, type_utils_1.isAutoLoginFlow)(step.flow)) {
            const name = step.flow.description ?? 'Unknown flow';
            const id = step.flowInvariantId ?? step.instanceId;
            const nestedSteps = state.getImportedFlowSteps(id);
            stepList.push(`Start execute flow: ${name}`);
            expandNestedSteps(nestedSteps, stepList, variables, state, depth + 1);
            stepList.push(`End execute flow: ${name}`);
        }
        else if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
            const groupDescription = step.description ?? 'Unnamed Step Group';
            stepList.push(`Start task: ${groupDescription}`);
            expandNestedSteps(step.stepsInGroup, stepList, variables, state, depth + 1);
            stepList.push(`End task: ${groupDescription}`);
        }
        else {
            stepList.push(safeHumanize(step, variables));
        }
    }
}
function safeHumanize(step, variables) {
    try {
        return (0, humanize_1.humanizeStepDescription)(step, variables);
    }
    catch {
        return step.annotations?.description ?? '[step description unavailable]';
    }
}
function processStepsWithDescriptions(steps, variables, cursorIndex, state, depth = 0) {
    const stepList = [];
    const tasks = [];
    let currentTaskIndex = -1;
    let cursorFound = false;
    const cursorAtThisDepth = cursorIndex[depth];
    const cursorIsAtThisDepth = cursorIndex.length === depth + 1;
    steps.forEach((step, index) => {
        const isAtCursor = cursorIsAtThisDepth && cursorAtThisDepth === index;
        if (isAtCursor && !cursorFound) {
            cursorFound = true;
        }
        if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
            const groupDescription = step.description ?? 'Unnamed Step Group';
            stepList.push(`Start task: ${groupDescription}`);
            tasks.push(groupDescription);
            const taskIsBeforeCursor = cursorAtThisDepth > index;
            if (taskIsBeforeCursor) {
                currentTaskIndex++;
            }
            const cursorIsInsideThisGroup = cursorIndex.length > depth + 1 && cursorAtThisDepth === index;
            if (cursorIsInsideThisGroup) {
                const nestedResult = processStepsWithDescriptions(step.stepsInGroup, variables, cursorIndex, state, depth + 1);
                stepList.push(...nestedResult.stepList);
                if (nestedResult.cursorFound) {
                    cursorFound = true;
                }
            }
            else {
                expandNestedSteps(step.stepsInGroup, stepList, variables, state, 1);
            }
            stepList.push(`End task: ${groupDescription}`);
        }
        else if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) &&
            step.flow &&
            !(0, type_utils_1.isAutoLoginFlow)(step.flow)) {
            const flowName = step.flow.description ?? 'Unknown flow';
            const flowId = step.flowInvariantId ?? step.instanceId;
            const flowSteps = state.getImportedFlowSteps(flowId);
            stepList.push(`Start execute flow: ${flowName}`);
            expandNestedSteps(flowSteps, stepList, variables, state, 1);
            stepList.push(`End execute flow: ${flowName}`);
        }
        else {
            stepList.push(safeHumanize(step, variables));
        }
    });
    if (!cursorFound &&
        cursorIsAtThisDepth &&
        cursorAtThisDepth === steps.length) {
        cursorFound = true;
        stepList.push(contextBuilder_1.CURSOR_MARKER);
    }
    return { stepList, tasks, currentTaskIndex, cursorFound };
}
function buildFormattedSteps(steps, variables, state) {
    const result = [];
    function walk(currentSteps, parentPath) {
        for (let i = 0; i < currentSteps.length; i++) {
            const step = currentSteps[i];
            const currentPath = [...parentPath, i];
            const pathLabel = (0, stepIndexUtils_1.pathToStepIndex)(currentPath);
            if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
                const groupName = step.description ?? 'Unnamed Step Group';
                result.push({
                    path: pathLabel,
                    step_id: step.id,
                    text: groupName,
                    kind: 'task-start',
                });
                walk(step.stepsInGroup, currentPath);
                result.push({
                    path: '',
                    text: groupName,
                    kind: 'task-end',
                });
            }
            else if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) &&
                step.flow &&
                !(0, type_utils_1.isAutoLoginFlow)(step.flow)) {
                const flowName = step.flow.description ?? 'Unknown flow';
                const flowId = step.flowInvariantId ?? step.instanceId;
                const flowSteps = state.getImportedFlowSteps(flowId) ?? [];
                result.push({
                    path: pathLabel,
                    step_id: step.id,
                    text: flowName,
                    kind: 'flow-start',
                });
                walk(flowSteps, currentPath);
                result.push({
                    path: '',
                    text: flowName,
                    kind: 'flow-end',
                });
            }
            else {
                result.push({
                    path: pathLabel,
                    step_id: step.id,
                    text: safeHumanize(step, variables),
                    kind: 'step',
                });
            }
        }
    }
    walk(steps, []);
    return result;
}
function computeCursorPath(rootSteps, cursorIndex, state) {
    if (cursorIndex.length === 0) {
        return '';
    }
    let parentSteps = rootSteps;
    for (let depth = 0; depth < cursorIndex.length - 1; depth++) {
        const parentIdx = cursorIndex[depth];
        const parentStep = parentSteps[parentIdx];
        if (!parentStep) {
            return '';
        }
        if ((0, mablscript_step_types_1.isStepGroupStep)(parentStep)) {
            parentSteps = parentStep.stepsInGroup;
        }
        else if ((0, mablscript_step_types_1.isStepWithReusableFlow)(parentStep) && parentStep.flow) {
            const flowId = parentStep.flowInvariantId ?? parentStep.instanceId;
            parentSteps = state.getImportedFlowSteps(flowId) ?? [];
        }
        else {
            return '';
        }
    }
    const lastSegment = cursorIndex[cursorIndex.length - 1];
    if (lastSegment >= parentSteps.length) {
        if (cursorIndex.length === 1) {
            return '';
        }
        return (0, stepIndexUtils_1.pathToStepIndex)(cursorIndex);
    }
    return (0, stepIndexUtils_1.pathToStepIndex)(cursorIndex);
}
function buildVariablesMap(variables, cursorIndex) {
    const variablesList = (0, variable_utils_1.getUsableVariablesAsList)(variables, cursorIndex);
    const variablesMap = {};
    variablesList.forEach((variable) => {
        if (variable?.value === undefined) {
            return;
        }
        variablesMap[variable.humanizedName] = JSON.stringify(variable.value);
    });
    return variablesMap;
}
function buildTestExecutionStateArtifact(options) {
    const { state } = options;
    const cursorIndex = state.getNextStepIndex();
    const steps = state.getSteps();
    const variables = state.getVariables();
    const variablesMap = buildVariablesMap(variables, cursorIndex);
    const { tasks, currentTaskIndex } = processStepsWithDescriptions(steps, variables, cursorIndex, state);
    const lastToolsExecuted = state.getLastToolsExecuted();
    const lastTaskStatus = lastToolsExecuted?.map((tool) => ({
        success: tool.toolResult?.success ?? false,
        steps_created: tool.toolResult?.createdStepsInfo?.map((info) => info.humanizedStep.step) ?? [],
        action: tool.toolCall?.toolName,
    })) ?? [];
    const formattedSteps = buildFormattedSteps(steps, variables, state);
    const cursorPath = computeCursorPath(steps, cursorIndex, state);
    const testExecutionState = {
        fullStepList: [],
        cursorPosition: 0,
        currentTaskIndex: tasks.length === 0 ? 0 : currentTaskIndex,
        variables: variablesMap,
        lastTaskStatus,
        formatted_steps: formattedSteps,
        cursor_path: cursorPath,
    };
    return createJsonArtifact((0, uuid_1.v4)(), 'test_execution_state', 'Test execution state with steps and cursor', testExecutionState);
}
function buildToolContextArtifact(options) {
    const toolContext = {
        userSupportedTools: (0, contextBuilder_1.getSupportedTools)(options),
    };
    return createJsonArtifact((0, uuid_1.v4)(), 'tool_context', 'Tool context with user supported tools', toolContext);
}
function buildCurrentApplicationStateArtifact(options) {
    const { state, screenshot, scrollableElements, tabs, pageSnapshot } = options;
    const scrollableElementsMapped = scrollableElements?.elements?.map((element) => ({
        elementBoundingBox: element.elementBoundingBox,
        elementType: element.type,
        scrollDimensions: {
            up: element.scrollability.scrollUp,
            down: element.scrollability.scrollDown,
            left: element.scrollability.scrollLeft,
            right: element.scrollability.scrollRight,
        },
    })) ?? [];
    const currentApplicationState = {
        currentScreenshot: screenshot,
        currentUrl: state.getLastVisitUrl(),
        scrollableElements: scrollableElementsMapped,
        tabs,
        pageSnapshot,
    };
    return createJsonArtifact((0, uuid_1.v4)(), 'current_application_state', 'Current application state', currentApplicationState);
}
function buildTestInfoArtifact(testName, testDescription, urlOverride, applicationId) {
    const testInfo = {
        name: testName,
        description: testDescription,
        url_override: urlOverride,
        test_case: testDescription,
    };
    if (applicationId) {
        testInfo.application_id = applicationId;
    }
    return createJsonArtifact((0, uuid_1.v4)(), 'test_information', 'Test information with name, description, and URL', testInfo);
}
function buildAllArtifacts(options) {
    const artifacts = [];
    artifacts.push(buildTestExecutionStateArtifact(options));
    artifacts.push(buildToolContextArtifact({
        isLocalTrainer: options.isLocalTrainer,
        accessibilityChecksEnabled: options.accessibilityChecksEnabled,
    }));
    artifacts.push(buildCurrentApplicationStateArtifact(options));
    const testOutline = options.state.getArtifact('test_outline');
    if (testOutline) {
        artifacts.push(testOutline);
    }
    const testInfo = options.state.getArtifact('test_information');
    if (testInfo) {
        artifacts.push(testInfo);
    }
    const workspaceContext = options.state.getArtifact('workspace_context');
    if (workspaceContext) {
        artifacts.push(workspaceContext);
    }
    return artifacts;
}
