"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "runCalculations", {
    enumerable: true,
    get: function() {
        return runCalculations;
    }
});
const _mathjs = require("mathjs");
// set up math context
const math = (0, _mathjs.create)(_mathjs.all);
function getConfigObject(componentId, config) {
    if (!config) return {};
    try {
        return JSON.parse(config);
    } catch (e) {
        // eslint-disable-next-line no-console
        console.warn(`Invalid config in survey screen component ${componentId}`);
        return {};
    }
}
const isBuiltInFunction = (name)=>{
    try {
        // see https://mathjs.org/docs/reference/functions/help.html
        math.help(name);
        return true;
    } catch (e) {
        return false;
    }
};
const extractVariables = (calculation)=>{
    const parsed = math.parse(calculation);
    return parsed.filter((node)=>node.isSymbolNode && !isBuiltInFunction(node)).map(({ name })=>name);
};
function runCalculations(components, values) {
    const inputValues = {};
    // calculation expression use "code"
    for (const c of components){
        inputValues[c.dataElement.code] = values[c.dataElement.id] ?? '';
    }
    const calculatedValues = {};
    for (const c of components){
        if (c.calculation) {
            try {
                const variables = extractVariables(c.calculation);
                const variableInputs = {};
                for (const variable of variables){
                    variableInputs[variable] = inputValues[variable] ?? '';
                }
                const isInputsEmpty = Object.values(variableInputs).every((value)=>value === '');
                if (variables.length && isInputsEmpty) {
                    // Skip calculation if every input is empty
                    calculatedValues[c.dataElement.id] = null;
                    continue;
                }
                let value = math.evaluate(c.calculation, inputValues);
                if (Number.isNaN(value)) {
                    throw new Error('Value is NaN');
                }
                const config = getConfigObject(c.id, c.config);
                if (config.rounding) {
                    value = parseFloat(parseFloat(value).toFixed(config.rounding));
                }
                inputValues[c.dataElement.code] = value;
                calculatedValues[c.dataElement.id] = value;
            } catch (e) {
                calculatedValues[c.dataElement.id] = null;
            }
        }
    }
    return calculatedValues;
}

//# sourceMappingURL=calculations.js.map