import { pull } from 'lodash';
import { JinjaVariable } from '../context/JinjaVariable.js';
export class FormVariable extends JinjaVariable {
    constructor(config, parent) {
        super(config.name, 'default' in config ? config.default : undefined, parent);
        Object.defineProperty(this, "state", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "stateListeners", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: []
        });
        Object.defineProperty(this, "config", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.config = config;
        this.state = { disabled: config.disabled === true, touched: false, visible: config.shouldRender === undefined };
    }
    getState() {
        return this.state;
    }
    setState(state) {
        this.state = state;
        this.stateListeners.forEach(l => l(state));
        if (state.visible === false)
            this.set(this.config.default);
    }
    patchState(state) {
        this.state = { ...this.state, ...state };
        this.stateListeners.forEach(l => l(this.state));
        if (state.visible === false)
            this.set(this.config.default);
    }
    onChangeState(listener) {
        this.stateListeners.push(listener);
        return () => {
            pull(this.stateListeners, listener);
        };
    }
    async isValid(context) {
        if (!this.getState().visible || (!('validation' in this.config) && !('required' in this.config)))
            return true;
        if (this.config.required && (this.get() === undefined || this.get() === null || this.get() === '')) {
            this.patchState({ error: 'required', touched: true });
            return false;
        }
        try {
            const error = await this.config.validation?.(await context.getResolvedVariable(this));
            this.patchState({ error, touched: true });
            return !error;
        }
        catch (error) {
            this.patchState({ error: error.message || `${error}`, touched: true });
            return false;
        }
    }
}
export class FormObjectVariable extends FormVariable {
    constructor(config, children = {}, parent) {
        super(config, parent);
        Object.defineProperty(this, "children", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: {}
        });
        this.children = children;
        for (const key in children) {
            for (const child of children[key]) {
                // fixme: this can lead to a memory leak (reference cycle).
                // Ideally, this listener would be removed before tearing down this object.
                child.onChange(value => this.set({ ...this.get(), [child.name]: value }));
            }
        }
    }
}
export class FormRepeatableVariable extends FormVariable {
    constructor({ condensed, expanded, config, parent }) {
        super(config, parent);
        Object.defineProperty(this, "repeating", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "condensed", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "expanded", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "repeatListeners", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: []
        });
        this.condensed = condensed;
        this.expanded = expanded;
        this.repeating = !!config.startRepeating;
        // fixme: possibly a memory leak
        this.condensed.onChange(v => this.set(v));
        // fixme: possibly a memory leak
        for (const contextName in expanded) {
            for (const variableName in expanded[contextName].variables) {
                for (const variable of expanded[contextName].variables[variableName]) {
                    variable.onChange(v => this.set({ ...this.get(), [contextName]: v }));
                }
            }
        }
    }
    setRepeating(value) {
        this.repeating = value;
        /* we must erase this value so conditional fields are not triggered if we change to the repeating mode. We don't need the "else" here,
        because there's no official support for referring to an specific repeating value. Although it would work to use a variable like
        "myVar.dev.myProperty", where "dev" is the repeating part this is not supported by the CLI and we don't need to treat the repercussions
        of this. */
        if (value) {
            this.condensed.set(this.config.default);
        }
        this.repeatListeners.forEach(l => l(value));
    }
    isRepeating() {
        return this.repeating;
    }
    onChangeRepeat(listener) {
        this.repeatListeners.push(listener);
        return () => {
            pull(this.repeatListeners, listener);
        };
    }
}
//# sourceMappingURL=FormVariable.js.map