import formToObj from 'form-to-obj';
import validate from 'validate.js';
import { TranslateService } from "@ema/core";
export class Form {
    constructor() {
        this.formClass = 'form-horizontal col-12';
        this.resettable = false;
        this.submitLabel = 'SUBMIT';
        this.translateService = TranslateService.instance;
    }
    componentDidLoad() {
        this.postRender();
    }
    componentDidUpdate() {
        this.postRender();
    }
    postRender() {
        this.form = this.el.getElementsByTagName('form')[0];
        if (this.entity) {
            this.entityForm = this.el.getElementsByTagName('ema-entity-form')[0];
            this.entityForm.setData(this.entity);
            if (this.definitions) {
                this.entityForm.setDefinitions(this.definitions);
            }
        }
        this.initValidations();
    }
    async handleSubmit(e) {
        let errors;
        if (this.constraints) {
            errors = validate(this.form, this.constraints);
            this.showErrors(this.form, errors || {});
        }
        if (errors)
            this.logErrors(errors);
        if (!this.action) {
            e.preventDefault();
        }
        if (this.action && errors) {
            e.preventDefault();
        }
        if (!errors) {
            let obj = formToObj(this.form);
            if (this.submitHandler) {
                obj = await this.submitHandler(obj, this.dataType);
            }
            if (obj) {
                this.formSubmitHandler();
            }
            else {
                console.error('form submit failure');
            }
        }
    }
    formSubmitHandler() {
        this.formSubmit.emit({ form: this.form, data: formToObj(this.form) });
    }
    async setData(data) {
        if (!data.data) {
            await this.setEntity(data);
        }
        else {
            await this.setEntity(data.data);
        }
    }
    async setEntity(entity) {
        if (entity.meta && entity.meta.definitions) {
            await this.setDefinitions(entity.meta.definitions);
        }
        this.entity = entity;
    }
    async setDefinitions(definitions) {
        typeof definitions === 'string' ? this.definitions = JSON.parse(definitions) : this.definitions = definitions;
        let constraints = {};
        Object.keys(this.definitions).forEach((k, i) => {
            let field = this.definitions[k];
            if (field.constraints) {
                constraints[k] = field.constraints;
            }
        });
        this.setConstraints(constraints);
    }
    async setConstraints(constraints) {
        typeof constraints === 'string' ? this.constraints = JSON.parse(constraints) : this.constraints = constraints;
    }
    async setSubmitInterceptor(submitFn) {
        this.submitHandler = submitFn;
    }
    async setTranslateService(instance) {
        this.translateService = instance;
        this.update();
    }
    async update() {
        this.render();
    }
    initValidations() {
        setTimeout((arg) => {
            const self = this;
            let inputs = this.el.querySelectorAll("input, textarea, select");
            for (let i = 0; i < inputs.length; ++i) {
                inputs.item(i).addEventListener("change", function (ev) {
                    let errors = validate(self.form, self.constraints) || {};
                    self.showErrorsForInput(this, errors[this['getAttribute']('name')]);
                });
            }
            validate.extend(validate.validators.datetime, {
                parse: function (value, options) {
                    return +moment.utc(value);
                },
                format: function (value, options) {
                    var format = options.dateOnly ? "YYYY-MM-DD" : "YYYY-MM-DD hh:mm:ss";
                    return moment.utc(value).format(format);
                }
            });
        }, 20);
    }
    showErrors(form, errors) {
        Array.from(form.querySelectorAll("input[name], select[name]")).forEach((input) => {
            this.showErrorsForInput(input, errors && errors[input['getAttribute']('name')]);
        });
    }
    showErrorsForInput(input, errors) {
        let formGroup = this.closestParent(input.parentNode, "form-group");
        if (formGroup) {
            let messages = formGroup.querySelector(".messages");
            this.resetFormGroup(formGroup);
            if (errors) {
                formGroup.classList.add("has-error");
                Object.keys(errors).forEach((k, i) => {
                    this.addError(messages, errors[k]);
                });
            }
            else {
                formGroup.classList.add("has-success");
            }
        }
        else {
            if (errors) {
                this.logErrors(errors, input['getAttribute']('name'));
            }
        }
    }
    logErrors(errors, fieldName = undefined) {
        Object.keys(errors).forEach((k, i) => {
            if (fieldName) {
                console.error(fieldName + ': ' + errors[k]);
            }
            else {
                console.error(k + ': ' + errors[k]);
            }
        });
    }
    closestParent(child, className) {
        if (!child || child == document) {
            return null;
        }
        if (child.classList.contains(className)) {
            return child;
        }
        else {
            return this.closestParent(child.parentNode, className);
        }
    }
    resetFormGroup(formGroup) {
        formGroup.classList.remove("has-error");
        formGroup.classList.remove("has-success");
        Array.from(formGroup.querySelectorAll(".help-block.error")).forEach((el) => {
            el.parentNode.removeChild(el);
        });
    }
    addError(messages, error) {
        let block = document.createElement("p");
        block.classList.add("help-block");
        block.classList.add("error");
        block.innerText = this.translate(error);
        messages.appendChild(block);
    }
    translate(str) {
        return this.translateService.get(str);
    }
    render() {
        if (this.dataJson) {
            this.setData(JSON.parse(this.dataJson));
        }
        if (this.entityJson) {
            this.setEntity(JSON.parse(this.entityJson));
        }
        if (this.definitionsJson) {
            this.setDefinitions(JSON.parse(this.definitionsJson));
        }
        if (this.language && this.language !== this.translateService.language) {
            this.translateService.use(this.language);
        }
        return (h("form", { onSubmit: (e) => this.handleSubmit(e), class: this.formClass, role: 'form', action: this.action || 'javascript:;', method: this.method || 'get' },
            this.entity &&
                h("div", null,
                    h("ema-entity-form", null)),
            h("slot", null),
            this.entity &&
                h("div", { class: "form-group row" },
                    h("div", { class: "col-md-8 offset-md-4" },
                        h("button", { type: "submit", class: "btn btn-primary mr-2" }, this.translate(this.submitLabel)),
                        this.resettable &&
                            h("button", { type: "reset", class: "btn btn-primary" }, this.translate('RESET')),
                        this.dataType &&
                            h("ema-permission", { can: '["delete"]', subject: this.dataType },
                                h("button", { type: "button", class: "btn btn-danger mr-2 delete-btn" }, this.translate('DELETE'))),
                        this.dataType &&
                            h("ema-permission", { can: '["create"]', subject: this.dataType },
                                h("button", { type: "button", class: "btn btn-secondary mr-2 delete-create" }, this.translate('CREATE')))),
                    this.submitError &&
                        h("div", { class: "col-md-8 offset-md-4 messages help-block error" },
                            this.translate(this.submitError),
                            "."))));
    }
    static get is() { return "ema-form"; }
    static get properties() { return {
        "action": {
            "type": String,
            "attr": "action"
        },
        "dataJson": {
            "type": String,
            "attr": "data-json"
        },
        "dataType": {
            "type": String,
            "attr": "data-type"
        },
        "definitionsJson": {
            "type": String,
            "attr": "definitions-json"
        },
        "el": {
            "elementRef": true
        },
        "entity": {
            "state": true
        },
        "entityJson": {
            "type": String,
            "attr": "entity-json"
        },
        "formClass": {
            "type": String,
            "attr": "form-class"
        },
        "language": {
            "type": String,
            "attr": "language"
        },
        "method": {
            "type": String,
            "attr": "method"
        },
        "resettable": {
            "type": Boolean,
            "attr": "resettable"
        },
        "setConstraints": {
            "method": true
        },
        "setData": {
            "method": true
        },
        "setDefinitions": {
            "method": true
        },
        "setEntity": {
            "method": true
        },
        "setSubmitInterceptor": {
            "method": true
        },
        "setTranslateService": {
            "method": true
        },
        "submitError": {
            "type": String,
            "attr": "submit-error"
        },
        "submitLabel": {
            "type": String,
            "attr": "submit-label"
        },
        "update": {
            "method": true
        }
    }; }
    static get events() { return [{
            "name": "formSubmit",
            "method": "formSubmit",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }]; }
    static get style() { return "/**style-placeholder:ema-form:**/"; }
}
