"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MAX_SELECTOR_ATTRIBUTE_LENGTH = void 0;
exports.chunkStepsByReusableFlows = chunkStepsByReusableFlows;
exports.chunkToMablscript = chunkToMablscript;
exports.isReusableFlowChunk = isReusableFlowChunk;
exports.isPrimaryFlowChunk = isPrimaryFlowChunk;
exports.sanitizeSelectors = sanitizeSelectors;
exports.extractOutputVariables = extractOutputVariables;
exports.extractInputVariables = extractInputVariables;
exports.extractInputVariablesSimple = extractInputVariablesSimple;
exports.normalizeAndValidate = normalizeAndValidate;
const mablscript_step_types_1 = require("../../sharedUtils/mablscript-step-types");
const mablscript_utils_1 = require("../../sharedUtils/mablscript/mablscript-utils");
const variable_names_util_1 = require("../../sharedUtils/variable-names-util");
const variable_utils_1 = require("../../sharedUtils/variable-utils");
const variable_constants_1 = require("../../sharedUtils/variable-constants");
function shallowCloneStep(step) {
    return Object.assign(Object.create(Object.getPrototypeOf(step)), step);
}
function chunkStepsByReusableFlows(steps) {
    const chunks = [];
    let currentSteps = [];
    for (const step of steps) {
        if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) && step.flowInvariantId) {
            if (currentSteps.length > 0) {
                chunks.push({ steps: currentSteps });
                currentSteps = [];
            }
            chunks.push({
                steps: [],
                reusableFlowId: step.flowInvariantId,
                reusableFlow: step.flow,
            });
        }
        else {
            currentSteps.push(step);
        }
    }
    if (currentSteps.length > 0) {
        chunks.push({ steps: currentSteps });
    }
    return chunks;
}
function chunkToMablscript(chunk) {
    return (0, mablscript_utils_1.translateToMablScript)(chunk.steps);
}
function isReusableFlowChunk(chunk) {
    return !!chunk.reusableFlowId;
}
function isPrimaryFlowChunk(chunk) {
    return !chunk.reusableFlowId && chunk.steps.length > 0;
}
exports.MAX_SELECTOR_ATTRIBUTE_LENGTH = 10000;
const FIND_DESCRIPTOR_PROPERTIES_IGNORED_FOR_SANITIZATION = [
    'css_query',
    'innerText',
    'other_attributes',
    'relative_xpath',
    'xpath',
];
function sanitizeSelectors(steps) {
    return steps.map(sanitizeStep);
}
function sanitizeStep(step) {
    let result = step;
    let cloned = false;
    if ((0, mablscript_step_types_1.isStepWithFind)(step) &&
        step.findDescriptor?.selector) {
        const sanitizedDescriptor = recursivelySanitizeSelectorsInDescriptor(step.findDescriptor);
        if (sanitizedDescriptor !== step.findDescriptor) {
            result = shallowCloneStep(step);
            cloned = true;
            result.findDescriptor = sanitizedDescriptor;
        }
    }
    if ((0, mablscript_step_types_1.isStepGroupStep)(result)) {
        const original = result.stepsInGroup;
        const sanitizedNested = original.map(sanitizeStep);
        const nestedChanged = sanitizedNested.some((sanitized, i) => sanitized !== original[i]);
        if (nestedChanged) {
            if (!cloned) {
                result = shallowCloneStep(result);
                cloned = true;
            }
            result.stepsInGroup = sanitizedNested;
        }
    }
    return result;
}
function recursivelySanitizeSelectorsInDescriptor(findDescriptor) {
    const sanitizedAuxiliary = findDescriptor.auxiliaryDescriptors?.map(recursivelySanitizeSelectorsInDescriptor);
    const auxChanged = !!sanitizedAuxiliary &&
        sanitizedAuxiliary.some((aux, i) => aux !== findDescriptor.auxiliaryDescriptors?.[i]);
    if (findDescriptor.selector) {
        const selector = { ...findDescriptor.selector };
        let selectorChanged = false;
        for (const [key, value] of Object.entries(selector)) {
            if (typeof value === 'string' &&
                !FIND_DESCRIPTOR_PROPERTIES_IGNORED_FOR_SANITIZATION.includes(key) &&
                value.length > exports.MAX_SELECTOR_ATTRIBUTE_LENGTH) {
                delete selector[key];
                selectorChanged = true;
            }
            if (key === 'app_state') {
                delete selector[key];
                selectorChanged = true;
            }
        }
        if (selectorChanged || auxChanged) {
            return {
                ...findDescriptor,
                auxiliaryDescriptors: sanitizedAuxiliary,
                selector,
            };
        }
    }
    else if (auxChanged) {
        return { ...findDescriptor, auxiliaryDescriptors: sanitizedAuxiliary };
    }
    return findDescriptor;
}
const VARIABLE_NAMESPACES = ['user', 'web', 'mail', 'api', 'flow', 'run'];
const TOP_LEVEL_VARIABLE_NAME_REGEX = new RegExp(`^((${VARIABLE_NAMESPACES.join('|')})\\.)?([a-zA-Z_][a-zA-Z0-9_]*)`);
function extractOutputVariables(steps) {
    const outputVariables = new Set();
    for (const step of steps) {
        if ((0, mablscript_step_types_1.isCreateVariableStep)(step) && step.variable.name) {
            outputVariables.add(step.variable.name);
        }
        else if ((0, mablscript_step_types_1.isMultiOutputVariablesStep)(step)) {
            step.variables?.forEach((v) => {
                if (v.name) {
                    outputVariables.add(v.name);
                }
            });
        }
    }
    return outputVariables;
}
function extractInputVariables(steps, variables, flow, flowConfiguration) {
    const inputVariables = new Map();
    setInputVariablesFromFlowParameters(inputVariables, steps, variables, flow, flowConfiguration);
    setInputVariablesUsedInSteps(inputVariables, steps, variables);
    setInputVariablesForUnknown(inputVariables, steps, variables);
    return inputVariables;
}
function extractInputVariablesSimple(steps) {
    const inputVariables = new Set();
    const createdVariables = new Set();
    for (const step of steps) {
        const usedVars = step.inputVariables?.() ?? [];
        for (const varName of usedVars) {
            if (!createdVariables.has(varName) && varName.startsWith('user.')) {
                inputVariables.add(varName);
            }
        }
        if ((0, mablscript_step_types_1.isCreateVariableStep)(step) && step.variable.name) {
            createdVariables.add(step.variable.name);
        }
        else if ((0, mablscript_step_types_1.isMultiOutputVariablesStep)(step)) {
            step.variables?.forEach((v) => {
                if (v.name) {
                    createdVariables.add(v.name);
                }
            });
        }
    }
    return inputVariables;
}
function normalizeAndValidate(potentialVariableName, allowedNamespaceIdentifiers = ['user']) {
    let variableName = getTopLevelVariableName(potentialVariableName);
    if (!variableName) {
        return undefined;
    }
    variableName = containsNamespacePrefix(variableName)
        ? variableName
        : 'user.' + variableName;
    if (containsNamespacePrefix(variableName, allowedNamespaceIdentifiers)) {
        return variableName;
    }
    return undefined;
}
function setInputVariablesFromFlowParameters(inputVariables, steps, variables, flow, flowConfiguration) {
    for (const step of steps) {
        const usedVariableNames = step.inputVariables?.() ?? [];
        for (const name of usedVariableNames) {
            const variableName = normalizeAndValidate(name, ['flow']);
            if (!variableName) {
                continue;
            }
            const parameterName = variableName.split('.')[1];
            const flowParameterVariable = flow?.parameters?.find((parameter) => parameter.name === parameterName);
            if (!flowParameterVariable) {
                continue;
            }
            setInputVariablesFromPattern(inputVariables, flowParameterVariable.default_value ?? '', variables);
            if (flowConfiguration?.parameters?.[parameterName]) {
                setInputVariablesFromPattern(inputVariables, flowConfiguration.parameters[parameterName], variables);
            }
        }
    }
}
function setInputVariablesUsedInSteps(inputVariables, steps, variables) {
    const createdVariableNames = new Set();
    for (const step of steps) {
        const usedVariableNames = step.inputVariables?.() ?? [];
        for (const name of usedVariableNames) {
            const variableName = normalizeAndValidate(name, ['user']);
            if (!variableName || createdVariableNames.has(variableName)) {
                continue;
            }
            setInputVariablesFromVariable(inputVariables, variableName, variables);
        }
        if ((0, mablscript_step_types_1.isCreateVariableStep)(step)) {
            createdVariableNames.add(step.variable.name);
        }
        else if ((0, mablscript_step_types_1.isMultiOutputVariablesStep)(step)) {
            step.variables?.forEach((variable) => {
                createdVariableNames.add(variable.name);
            });
        }
    }
}
function setInputVariablesForUnknown(inputVariables, steps, variables) {
    const hasUnknownInputVariables = steps.some((step) => step.inputVariables?.() === undefined);
    if (hasUnknownInputVariables) {
        const variablesList = (0, variable_utils_1.getInputVariablesAsList)(variables);
        for (const variable of variablesList) {
            if (!variable.readOnly) {
                const variableName = variable.name;
                const variableValue = variable.value || variable_constants_1.EMPTY_VARIABLE_VALUE_PLACEHOLDER;
                if (variableName && !inputVariables.has(variableName)) {
                    inputVariables.set(variableName, String(variableValue));
                }
            }
        }
    }
}
function setInputVariablesFromPattern(inputVariables, pattern, variables) {
    const variableNames = (0, variable_names_util_1.extractVariableNamesFromPattern)(pattern);
    for (const variableName of variableNames) {
        if (typeof variableName === 'string') {
            setInputVariablesFromVariable(inputVariables, variableName, variables);
        }
    }
}
function setInputVariablesFromVariable(inputVariables, potentialVariableName, variables) {
    const variableName = normalizeAndValidate(potentialVariableName);
    if (!variableName) {
        return;
    }
    const variable = variables[potentialVariableName];
    if (!variable) {
        inputVariables.set(variableName, variable_constants_1.EMPTY_VARIABLE_VALUE_PLACEHOLDER);
    }
    else if (isVariable(variable) && !variable.readOnly) {
        inputVariables.set(variableName, getDefaultValue(variable));
    }
}
function getTopLevelVariableName(variableName) {
    return variableName.match(TOP_LEVEL_VARIABLE_NAME_REGEX)?.[0];
}
function containsNamespacePrefix(variableName, namespaceIdentifiers = VARIABLE_NAMESPACES) {
    return namespaceIdentifiers.some((identifier) => variableName.startsWith(identifier + '.'));
}
function isVariable(value) {
    if (typeof value !== 'object' || !value) {
        return false;
    }
    return 'name' in value && 'humanizedName' in value;
}
function getDefaultValue(variable) {
    const unresolvedValue = variable.unresolvedValue;
    const value = variable.value;
    return (toValueString(unresolvedValue) ||
        toValueString(value) ||
        variable_constants_1.EMPTY_VARIABLE_VALUE_PLACEHOLDER);
}
function toValueString(value) {
    switch (typeof value) {
        case 'string':
            return value;
        case 'number':
        case 'boolean':
            return `${value}`;
        case 'object':
        default:
            return '';
    }
}
