import { findLast, get, mapValues } from 'lodash';
import { ContextValue } from '../context/ContextValue.js';
import { JinjaExpressionVariable } from '../context/JinjaVariable.js';
import { FormObjectVariable, FormRepeatableVariable, FormVariable } from './FormVariable.js';
function buildVariable(variables, field, context, parent) {
    variables[field.name] ??= [];
    if (field.repeat?.length) {
        const expanded = {};
        for (const r of field.repeat) {
            expanded[r] = new FormContextValue(r, [{
                    ...field,
                    label: r,
                    repeat: undefined,
                    children: 'children' in field
                        ? field.children.map(c => ({
                            ...c,
                            default: field.default?.[r]?.[c.name] ?? c.default,
                            disabled: typeof field.disabled === 'boolean' ? c.disabled : field.disabled?.[r],
                        }))
                        : [],
                    default: field.default?.[r] ?? field.default,
                    disabled: typeof field.disabled === 'boolean' ? field.disabled : field.disabled?.[r],
                    shouldRender: undefined,
                }], undefined, context);
        }
        const condensedChildren = {};
        buildVariable(condensedChildren, { ...field, repeat: undefined, shouldRender: undefined });
        const condensed = condensedChildren[field.name][0];
        variables[field.name].push(new FormRepeatableVariable({
            config: field,
            condensed,
            expanded,
            parent,
        }));
    }
    else if ('children' in field && field.children.length) {
        const children = {};
        field.children.forEach(f => buildVariable(children, { ...f, default: f.default ?? field.default?.[f.name] }, context, parent ? `${parent}.${field.name}` : field.name));
        variables[field.name].push(new FormObjectVariable(field, children, parent));
    }
    else {
        variables[field.name].push(new FormVariable(field, parent));
    }
}
export class FormContextValue extends ContextValue {
    constructor(name, fields, computed, parent) {
        super(name);
        Object.defineProperty(this, "variables", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: {}
        });
        Object.defineProperty(this, "computedVariables", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: {}
        });
        Object.defineProperty(this, "parent", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.parent = parent;
        fields.forEach(field => buildVariable(this.variables, field, this));
        for (const key in computed) {
            this.computedVariables[key] = new JinjaExpressionVariable(key, computed[key]);
        }
    }
    computeValidations(variable, promises) {
        if (variable instanceof FormRepeatableVariable) {
            if (variable.isRepeating()) {
                for (const key in variable.expanded) {
                    promises.push(variable.expanded[key].isValid());
                }
            }
            else {
                this.computeValidations(variable.condensed, promises);
            }
        }
        else if (variable instanceof FormObjectVariable) {
            promises.push(variable.isValid(this));
            for (const key in variable.children) {
                for (const child of variable.children[key]) {
                    this.computeValidations(child, promises);
                }
            }
        }
        else {
            promises.push(variable.isValid(this));
        }
    }
    async isValid() {
        const promises = [];
        for (const key in this.variables) {
            for (const child of this.variables[key]) {
                this.computeValidations(child, promises);
            }
        }
        const results = await Promise.all(promises);
        return !results.some(r => !r);
    }
    async getResolvedVariable(variable) {
        if (variable instanceof FormVariable) {
            if (!variable.config.jinja)
                return variable.get();
            if (variable.config.jinja === true || variable.config.jinja.mode === 'python') {
                // todo: implement jinja support
            }
            else {
                const [, variablePath, text] = variable?.get()?.match(/^(?:\{\{\s*([^\s]+)\s*\}\}|(.+))$/) ?? [];
                if (variablePath) {
                    const referred = this.getVariable(variablePath);
                    if (!referred)
                        return;
                    const value = referred.get();
                    if (typeof value === 'string' && value.match(/^{{.+}}$/)) {
                        return this.getResolvedVariable(referred);
                    }
                    else {
                        const remainingPath = value ? variablePath.substring(referred.path.length).replace(/^\./, '') : '';
                        return remainingPath ? get(value, remainingPath) : value;
                    }
                }
                else {
                    return text;
                }
            }
        }
        else {
            return variable?.get();
        }
    }
    findByKey(key) {
        // find last because there might be fields with the same name, in this case, the last visible field should be used.
        return findLast(this.variables[key], ((variable) => variable.getState().visible)) ?? this.computedVariables[key];
    }
    findVariableByKey(key) {
        return this.findByKey(key);
    }
    getVariableInContext(key) {
        return this.findVariableByKey(this.createKey(key))?.get();
    }
    getAllInContext() {
        return mapValues(this.variables, (_, key) => this.findByKey(key)?.get());
    }
    addVariablesToContext(variables) {
        for (const variable of variables) {
            this.variables[variable.name] ??= [];
            this.variables[variable.name].push(variable);
        }
    }
    forEachVariable(iteratee) {
        for (const key in this.variables) {
            for (const variable of this.variables[key]) {
                iteratee(variable);
            }
        }
    }
}
//# sourceMappingURL=FormContextValue.js.map