"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmptyNamespace = exports.isVariableDescriptor = void 0;
exports.flattenVariables = flattenVariables;
exports.aliasVariables = aliasVariables;
exports.humanizeVariablePattern = humanizeVariablePattern;
exports.canonicalizeVariablePattern = canonicalizeVariablePattern;
exports.humanizeVariablePatternsInObject = humanizeVariablePatternsInObject;
exports.canonicalizeVariablePatternsInObject = canonicalizeVariablePatternsInObject;
exports.canonicalizeVariablePatternsInObjectWithAliases = canonicalizeVariablePatternsInObjectWithAliases;
const mablscriptFind_1 = require("../mablscriptFind");
function isNullOrUndefined(arg) {
    return arg === null || arg === undefined;
}
function getByPath(obj, path) {
    if (isNullOrUndefined(obj) || !path) {
        return undefined;
    }
    return path.split('.').reduce((acc, key) => acc?.[key], obj);
}
function setByPath(obj, path, value) {
    if (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 (isNullOrUndefined(current[key])) {
            current[key] = {};
        }
        current = current[key];
    }
    current[keys[keys.length - 1]] = value;
}
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 (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 (isNullOrUndefined(object)) {
        return object;
    }
    const aliasedVariables = aliasVariables(variables);
    return canonicalizeVariablePatternsInObjectWithAliases(object, aliasedVariables);
}
function canonicalizeVariablePatternsInObjectWithAliases(object, aliasedVariables) {
    if (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;
}
