import React from 'react';
import cn from 'classnames';
import { isUndefined } from "../../utils";
import { FORM } from "../../constants";
const { FORM_DISABLED_STATE } = FORM;
import Validator from './validator';
/*type FieldProps = {
  [key: string];
};*/
export const FormContext = React.createContext({});
const initState = {
    fields: {},
    hasError: false,
    errorMessage: '',
    disabled: FORM_DISABLED_STATE.ENABLED,
    isSuccess: false,
    validationAlerts: true,
};
const VALIDATION_DELAY = 1000;
class FormC extends React.Component {
    constructor(props) {
        super(props);
        this.id = setTimeout(() => { }, 0);
        this.$ref = React.createRef();
        this.formUnmount = false;
        this.resetForm = () => {
            const { fields } = this.state;
            Object.keys(fields).forEach((fieldName) => {
                fields[fieldName].value = fields[fieldName].initValue;
                fields[fieldName].validation.setError(false);
            });
            this.disableForm(FORM_DISABLED_STATE.ENABLED);
            this.setError();
        };
        this.deleteField = (fieldName) => {
            const { fields } = this.state;
            delete fields[fieldName];
            this.setState({
                fields,
            });
        };
        this.addField = (fieldProps) => {
            const { initialValues } = this.props;
            const { value: propValue = '' } = fieldProps.value !== null && fieldProps;
            this.setState(({ fields }) => {
                const value = initialValues && !isUndefined(initialValues[fieldProps.name])
                    ? initialValues[fieldProps.name]
                    : propValue;
                return {
                    fields: Object.assign(Object.assign({}, fields), { [fieldProps.name]: {
                            value,
                            initValue: value,
                            grabIfDirty: !!fieldProps.grabIfDirty,
                            validateOnSubmit: fieldProps.validateOnSubmit,
                            dynamicValidate: fieldProps.dynamicValidate,
                            validation: new Validator(fieldProps, this),
                        } }),
                };
            });
        };
        this.onFieldChange = (fieldName, value, callBack) => {
            const { isSuccess } = this.state, { onChange } = this.props;
            if (isSuccess) {
                this.setSuccess(false);
            }
            this.resetFieldValidation(fieldName);
            this.setState(({ fields }) => ({
                fields: Object.assign(Object.assign({}, fields), { [fieldName]: Object.assign(Object.assign({}, fields[fieldName]), { value }) }),
            }), () => {
                const { fields } = this.state;
                if (fields[fieldName].validateOnSubmit) {
                    if (callBack)
                        callBack();
                }
                else if (fields[fieldName].dynamicValidate) {
                    clearTimeout(this.id);
                    this.id = setTimeout(() => this.validateField(fieldName, value).then((res) => !res && callBack ? callBack() : null), VALIDATION_DELAY);
                }
                else {
                    this.checkErrors();
                }
                onChange && onChange(fields);
            });
        };
        this.onBlurChange = (fieldName, value) => {
            const { fields } = this.state, field = fields[fieldName];
            if (this.id) {
                clearTimeout(this.id);
            }
            if (field === null || field === void 0 ? void 0 : field.validateOnSubmit)
                return;
            if ((field === null || field === void 0 ? void 0 : field.initValue) !== (field === null || field === void 0 ? void 0 : field.value))
                this.validateField(fieldName, value);
        };
        this.validateField = (fieldName, value) => {
            const { fields } = this.state;
            return fields[fieldName].validation
                .validate(value)
                .then(() => {
                this.checkErrors();
                return false;
            })
                .catch((err) => {
                const { validationAlerts } = this.state;
                this.setError(err, validationAlerts);
                return true;
            });
        };
        this.resetFieldValidation = (fieldName) => {
            var _a, _b;
            const { fields } = this.state;
            (_b = (_a = fields[fieldName]) === null || _a === void 0 ? void 0 : _a.validation) === null || _b === void 0 ? void 0 : _b.setError(false);
        };
        // resetFieldsValidation = () => {
        //   Object.keys(this.fields).forEach(this.resetFieldValidation);
        // };
        this.checkErrors = () => {
            const { disabled, fields } = this.state;
            let haveError = false;
            Object.keys(fields).forEach((field) => {
                if (haveError) {
                    return;
                }
                if (fields[field].validation.isError) {
                    haveError = true;
                }
            });
            if (!haveError && disabled) {
                this.disableForm(FORM_DISABLED_STATE.ENABLED);
                this.setError(null);
            }
        };
        this.validateFields = () => {
            const { fields } = this.state;
            const fieldsValidation = Object.keys(fields).map((field) => fields[field].validation.validate(fields[field].value));
            return Promise.all(fieldsValidation);
        };
        this.setError = (err, validationAlerts) => {
            this.setState(() => {
                if (!err) {
                    return {
                        hasError: false,
                        errorMessage: '',
                    };
                }
                return {
                    hasError: true,
                    isSuccess: false,
                    errorMessage: validationAlerts ? err.message : '',
                    disabled: FORM_DISABLED_STATE.SUBMIT_BUTTON,
                };
            });
        };
        this.setSuccess = (isSuccess) => this.setState({ isSuccess });
        this._serializeFields = () => {
            const { fields } = this.state;
            return Object.keys(fields).reduce((acc, field) => {
                const fieldState = fields[field];
                if (fieldState.grabIfDirty) {
                    return fieldState.value === fieldState.initValue
                        ? acc
                        : Object.assign(Object.assign({}, acc), { [field]: fieldState.value });
                }
                return Object.assign(Object.assign({}, acc), { [field]: fieldState.value });
            }, {});
        };
        this.onSubmit = (e) => {
            if (e) {
                e.preventDefault();
            }
            const { disableOnSubmit = true } = this.props;
            if (disableOnSubmit) {
                this.disableForm(FORM_DISABLED_STATE.FORM);
            }
            const { props: { onSubmit, callback, handlerError, onSuccess, callbackValidation }, } = this;
            this.validateFields()
                .then(() => {
                if (onSubmit) {
                    onSubmit(this._serializeFields());
                }
            })
                .catch((error) => {
                this.disableForm(FORM_DISABLED_STATE.SUBMIT_BUTTON);
                this.setError(error);
                if (callbackValidation) {
                    callbackValidation(error);
                }
            })
                .then(() => {
                if (this.formUnmount) {
                    return;
                }
                this.disableForm(FORM_DISABLED_STATE.ENABLED);
                this.updateInitValue();
                if (callback) {
                    callback();
                }
                if (onSuccess) {
                    this.setSuccess(true);
                }
            })
                .catch((resp) => {
                const errorDetails = handlerError ? handlerError(resp) : resp;
                this.disableForm(FORM_DISABLED_STATE.SUBMIT_BUTTON);
                this.updateFieldsError(errorDetails);
            });
        };
        this.disableForm = (disabled) => this.setState({ disabled });
        this.updateFieldsError = ({ message, data }) => {
            if (data) {
                const { fields } = this.state;
                Object.keys(data).forEach((field) => {
                    if (fields[field]) {
                        const errorMessage = Array.isArray(data[field])
                            ? data[field].map(({ message: fieldError }) => fieldError).join(', ')
                            : data[field].message;
                        fields[field].validation.setError(true, errorMessage);
                    }
                });
            }
            this.setError(new Error(message), true);
        };
        this.updateInitValue = () => {
            const newFields = {};
            const { fields } = this.state;
            Object.keys(fields).forEach((field) => {
                newFields[field] = Object.assign(Object.assign({}, fields[field]), { initValue: fields[field].value });
            });
            this.setState({
                fields: newFields,
            });
        };
        this.onFocusChange = (event) => {
            //const { currentTarget } = event || {};
        };
        const { disabled, validationAlerts } = props;
        this.state = Object.assign({}, initState, disabled ? { disabled } : {}, typeof validationAlerts !== 'undefined' ? { validationAlerts } : {});
    }
    componentDidUpdate(prevProps) {
        const { disabled, initialValues } = this.props;
        const { fields } = this.state;
        if (disabled !== prevProps.disabled) {
            this.disableForm(disabled);
        }
        if (initialValues !== prevProps.initialValues) {
            const updateFields = {};
            Object.keys(fields).forEach((field) => {
                if (!isUndefined(initialValues[field])) {
                    updateFields[field] = Object.assign(Object.assign({}, fields[field]), { value: initialValues[field] });
                }
            });
            this.setState(({ fields }) => {
                return {
                    fields: Object.assign(Object.assign({}, fields), updateFields),
                };
            });
        }
    }
    componentWillUnmount() {
        this.formUnmount = true;
    }
    fields() {
        const { fields } = this.state;
        return fields;
    }
    render() {
        const { state: { fields, disabled, errorMessage, isSuccess }, props: { children, className, onSuccess, forRef }, addField, deleteField, onFieldChange, onBlurChange, onSubmit, disableForm, onFocusChange, } = this;
        const value = {
            isSuccess: isSuccess ? onSuccess : '',
            fields,
            addField,
            deleteField,
            onChange: onFieldChange,
            onBlurForm: onBlurChange,
            onFocusForm: onFocusChange,
            disabled,
            errorMessage,
            onSubmit,
            disableForm,
        };
        const classList = cn('appForm', className);
        return (React.createElement("form", { ref: this.$ref, onSubmit: this.onSubmit, className: classList, noValidate: true },
            React.createElement(FormContext.Provider, { value: value }, children)));
    }
}
export default FormC;
