"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmptyNamespace = exports.isVariableDescriptor = void 0;
exports.isVariableOnImport = isVariableOnImport;
exports.flattenVariables = flattenVariables;
exports.aliasVariables = aliasVariables;
exports.humanizeVariablePattern = humanizeVariablePattern;
exports.canonicalizeVariablePattern = canonicalizeVariablePattern;
exports.humanizeVariablePatternsInObject = humanizeVariablePatternsInObject;
exports.canonicalizeVariablePatternsInObject = canonicalizeVariablePatternsInObject;
exports.canonicalizeVariablePatternsInObjectWithAliases = canonicalizeVariablePatternsInObjectWithAliases;
exports.safelyGeneratePatternPreview = safelyGeneratePatternPreview;
exports.getUsableVariablesAsList = getUsableVariablesAsList;
exports.getUsableVariablesAsMap = getUsableVariablesAsMap;
exports.getEditableVariablesAsList = getEditableVariablesAsList;
exports.getInputVariablesAsList = getInputVariablesAsList;
exports.proxify = proxify;
exports.unproxify = unproxify;
exports.wrapVariableRoot = wrapVariableRoot;
exports.printableVariableValue = printableVariableValue;
exports.normalizeVariable = normalizeVariable;
exports.extractSingleVariableName = extractSingleVariableName;
exports.getResolvedUserVariableValue = getResolvedUserVariableValue;
const dom_util_1 = require("@mablhq-internal/mablscript-find/dom-util");
const mablscriptFind_1 = require("@mablhq/test-model/mablscriptFind");
const largeJsDependencyUtil_1 = require("../../domUtil/largeJsDependencyUtil");
const type_utils_1 = require("./type-utils");
const mablscript_step_types_1 = require("./mablscript-step-types");
function getByPath(obj, path) {
    if ((0, type_utils_1.isNullOrUndefined)(obj) || !path) {
        return undefined;
    }
    return path.split('.').reduce((acc, key) => acc?.[key], obj);
}
function setByPath(obj, path, value) {
    if ((0, type_utils_1.isNullOrUndefined)(obj) || !path) {
        return;
    }
    const keys = path.split('.');
    let current = obj;
    for (let i = 0; i < keys.length - 1; i++) {
        const key = keys[i];
        if ((0, type_utils_1.isNullOrUndefined)(current[key])) {
            current[key] = {};
        }
        current = current[key];
    }
    current[keys[keys.length - 1]] = value;
}
function isVariableOnImport(value) {
    return value?.name && value.humanizedName;
}
const isVariableDescriptor = (variable) => (variable?.name && variable?.humanizedName) !== undefined;
exports.isVariableDescriptor = isVariableDescriptor;
const isEmptyNamespace = (value) => (value && typeof value === 'object' && Object.keys(value).length === 0) ??
    false;
exports.isEmptyNamespace = isEmptyNamespace;
function flattenVariables(variables, emptyNamespaceCallback, parentKey) {
    if (variables === undefined) {
        return {};
    }
    if ((0, exports.isVariableDescriptor)(variables)) {
        return { [variables.name]: variables };
    }
    return Object.entries(variables || {}).reduce((result, entry) => {
        const [key, value] = entry;
        const fullyQualifiedKey = parentKey ? `${parentKey}.${key}` : key;
        if ((0, exports.isVariableDescriptor)(value)) {
            result[key] = value;
        }
        else if (value && !(0, exports.isEmptyNamespace)(value)) {
            const subResult = flattenVariables(value, emptyNamespaceCallback, fullyQualifiedKey);
            Object.keys(subResult).forEach((k) => (result[key + '.' + k] = subResult[k]));
        }
        else if (emptyNamespaceCallback && (0, exports.isEmptyNamespace)(value)) {
            emptyNamespaceCallback(fullyQualifiedKey);
        }
        return result;
    }, {});
}
function aliasVariables(variables) {
    if (!variables) {
        return {};
    }
    const dict = {};
    const flattenedVariables = flattenVariables(variables);
    Object.values(flattenedVariables)
        .filter((variable) => (0, exports.isVariableDescriptor)(variable) &&
        variable?.humanizedName !== variable.name)
        .forEach((variable) => setByPath(dict, variable.humanizedName, variable));
    return dict;
}
function replacerFactory(variables, fieldKey, variableNotFoundFallback) {
    return (match, variableName) => {
        if (!(0, mablscriptFind_1.isValidVariableName)(variableName)) {
            return variableName || '';
        }
        const variableReference = getByPath(variables, variableName);
        let replacementVariableName;
        if ((0, exports.isVariableDescriptor)(variableReference)) {
            replacementVariableName = variableReference[fieldKey];
        }
        else {
            replacementVariableName = variableNotFoundFallback
                ? variableNotFoundFallback(variableName)
                : variableName;
        }
        if (variableName === replacementVariableName) {
            return match;
        }
        return match.replace(variableName, replacementVariableName);
    };
}
function humanizeVariablePattern(variables, pattern) {
    return ((0, mablscriptFind_1.replaceVariables)(pattern, replacerFactory(variables, 'humanizedName', (variableName) => variableName?.startsWith('user.')
        ? variableName.replace(/^user\./, '')
        : variableName)) ?? '');
}
function canonicalizeVariablePatternUsingAliases(aliasedVariables, pattern) {
    return ((0, mablscriptFind_1.replaceVariables)(pattern, replacerFactory(aliasedVariables, 'name', (variableName) => variableName?.includes('.') ? variableName : `user.${variableName}`)) ?? undefined);
}
function canonicalizeVariablePattern(variables, pattern) {
    return canonicalizeVariablePatternUsingAliases(aliasVariables(variables), pattern);
}
function humanizeVariablePatternsInObject(variables, object) {
    if ((0, type_utils_1.isNullOrUndefined)(object)) {
        return object;
    }
    if (typeof object === 'string') {
        return humanizeVariablePattern(variables, object);
    }
    else if (Array.isArray(object)) {
        return object.map((prop) => humanizeVariablePatternsInObject(variables, prop));
    }
    else if (typeof object === 'object') {
        const clone = { ...object };
        for (const property in clone) {
            if (clone.hasOwnProperty(property)) {
                const value = clone[property];
                clone[property] = humanizeVariablePatternsInObject(variables, value);
            }
        }
        return clone;
    }
    return object;
}
function canonicalizeVariablePatternsInObject(variables, object) {
    if ((0, type_utils_1.isNullOrUndefined)(object)) {
        return object;
    }
    const aliasedVariables = aliasVariables(variables);
    return canonicalizeVariablePatternsInObjectWithAliases(object, aliasedVariables);
}
function canonicalizeVariablePatternsInObjectWithAliases(object, aliasedVariables) {
    if ((0, type_utils_1.isNullOrUndefined)(object)) {
        return object;
    }
    if (typeof object === 'string') {
        return canonicalizeVariablePatternUsingAliases(aliasedVariables, object);
    }
    else if (Array.isArray(object)) {
        return object.map((prop) => canonicalizeVariablePatternsInObjectWithAliases(prop, aliasedVariables));
    }
    else if (typeof object === 'object') {
        const clone = { ...object };
        for (const property in clone) {
            if (clone.hasOwnProperty(property)) {
                const value = clone[property];
                clone[property] = canonicalizeVariablePatternsInObjectWithAliases(value, aliasedVariables);
            }
        }
        return clone;
    }
    return object;
}
function safelyGeneratePatternPreview(pattern = '', variables = undefined, undefinedCallback = () => '{{undefined}}', skipCanonicalization = false) {
    const value = skipCanonicalization
        ? pattern
        : canonicalizeVariablePattern(variables, pattern);
    return (0, largeJsDependencyUtil_1.generatePatternPreview)(value, variables, undefinedCallback) ?? '';
}
const variableCompareReadonlyLast = (a, b) => !!a.readOnly === !!b.readOnly
    ? a.name.localeCompare(b.name)
    : Number(!!a.readOnly) - Number(!!b.readOnly);
const variableCompareReadonlyFirst = (a, b) => !!a.readOnly === !!b.readOnly
    ? a.name.localeCompare(b.name)
    : Number(!!b.readOnly) - Number(!!a.readOnly);
const isDefined = (variable, flowStepIndex) => flowStepIndex === undefined ||
    (variable.flowStepIndex !== undefined &&
        (0, mablscript_step_types_1.indexLessThan)(variable.flowStepIndex, flowStepIndex));
function isStrictPrefix(prefix, full) {
    if (prefix.length >= full.length) {
        return false;
    }
    for (let i = 0; i < prefix.length; i++) {
        if (prefix[i] !== full[i]) {
            return false;
        }
    }
    return true;
}
function isFlowParameterInScope(variable, flowStepIndex) {
    if (variable.source !== mablscriptFind_1.LegacyVariableSource.FLOW_PARAMETER) {
        return true;
    }
    if (flowStepIndex === undefined) {
        return true;
    }
    const paramOwnerIndex = variable.flowStepIndex;
    if (!paramOwnerIndex || paramOwnerIndex.length === 0) {
        return true;
    }
    return isStrictPrefix(paramOwnerIndex, flowStepIndex);
}
function getUsableVariablesAsList(variables, flowStepIndex = undefined) {
    const flattenedVariables = flattenVariables(variables);
    return Object.values(flattenedVariables)
        .filter(exports.isVariableDescriptor)
        .filter((variable) => !variable.hidden &&
        isDefined(variable, flowStepIndex) &&
        isFlowParameterInScope(variable, flowStepIndex))
        .sort(variableCompareReadonlyLast);
}
function getUsableVariablesAsMap(variables, flowStepIndex = undefined, keyByName = true, keyByHumanizedName = false) {
    const dict = {};
    const flattenedVariables = flattenVariables(variables);
    Object.keys(flattenedVariables)
        .filter((name) => (0, exports.isVariableDescriptor)(flattenedVariables[name]))
        .filter((name) => !flattenedVariables[name].hidden &&
        isDefined(flattenedVariables[name], flowStepIndex) &&
        isFlowParameterInScope(flattenedVariables[name], flowStepIndex))
        .forEach((name) => {
        const variable = flattenedVariables[name];
        if (keyByHumanizedName && variable.humanizedName) {
            dict[variable.humanizedName] = variable;
        }
        if (keyByName && variable.name) {
            dict[variable.name] = variable;
        }
    });
    return dict;
}
function getEditableVariablesAsList(variables, flowStepIndex) {
    const flattenedVariables = flattenVariables(variables);
    return Object.values(flattenedVariables)
        .filter(exports.isVariableDescriptor)
        .filter((variable) => !variable.hidden &&
        !variable.readOnly &&
        variable.variableType !== 'input' &&
        isDefined(variable, flowStepIndex))
        .sort(variableCompareReadonlyLast);
}
function getInputVariablesAsList(variables) {
    const flattenedVariables = flattenVariables(variables);
    return Object.values(flattenedVariables)
        .filter(exports.isVariableDescriptor)
        .filter((variable) => !variable.hidden && variable.variableType === 'input')
        .sort(variableCompareReadonlyFirst);
}
const identity = (variable) => variable;
function proxify(variables, getValue = identity) {
    const proxy = (0, mablscriptFind_1.namespace)();
    const emptyNamespaceCallback = (key) => {
        proxy[key] = (0, mablscriptFind_1.namespace)();
    };
    const arr = Array.isArray(variables)
        ? variables
        : Object.values(flattenVariables(variables, emptyNamespaceCallback));
    arr.forEach((variable) => (proxy[variable.name] = getValue(variable)));
    return proxy;
}
function unflattenVariables(variables, getValue = identity) {
    const arr = Array.isArray(variables) ? variables : Object.values(variables);
    return JSON.parse(JSON.stringify(proxify(arr, getValue)));
}
function unproxify(variables, getValue = identity) {
    return unflattenVariables(flattenVariables(variables), getValue);
}
function wrapVariableRoot(variableRoot) {
    return new Proxy(variableRoot, {
        set(target, property, value) {
            if ((0, exports.isVariableDescriptor)(value)) {
                value.name = value.name || property;
                value.readOnly = value.readOnly === undefined ? false : value.readOnly;
                value.maskDisplay =
                    value.maskDisplay === undefined ? false : value.maskDisplay;
            }
            target[property] = value;
            return true;
        },
    });
}
function printableVariableValue(value) {
    return (0, type_utils_1.isString)(value) ? value : JSON.stringify(value);
}
function normalizeVariable(variableValue) {
    switch (typeof variableValue) {
        case 'number':
        case 'boolean':
            return variableValue.toString();
        case 'string':
            return (0, dom_util_1.compactWhitespace)(variableValue);
        case 'object':
            return JSON.stringify(variableValue);
        case 'undefined':
            return 'undefined';
        default:
            return `${variableValue}`;
    }
}
function extractSingleVariableName(value) {
    const variables = (0, mablscriptFind_1.findVariables)(value);
    return !!variables &&
        variables.length === 1 &&
        value === `{{@${variables[0]}}}`
        ? variables[0]
        : undefined;
}
function getResolvedUserVariableValue(variables, variableName, defaultValue) {
    const flattenedVariables = flattenVariables(variables);
    const fullVariableName = `user.${variableName}`;
    const variable = flattenedVariables[fullVariableName];
    if (variable?.value !== undefined) {
        return String(variable.value);
    }
    return defaultValue;
}
