const templateUrl = require('~components/form/form.partial.html');

function atFormLink (scope, el, attrs, controllers) {
    const formController = controllers[0];
    const form = el[0];

    scope.ns = 'form';
    scope[scope.ns] = { modal: {} };

    formController.init(scope, form);
}

function AtFormController (eventService, strings) {
    const vm = this || {};

    let scope;
    let modal;
    let form;

    vm.components = [];
    vm.state = {
        isValid: false,
        disabled: false,
        value: {},
    };

    vm.init = (_scope_, _form_) => {
        scope = _scope_;
        form = _form_;
        ({ modal } = scope[scope.ns]);

        vm.state.disabled = scope.state.disabled;

        vm.setListeners();
    };

    vm.register = (category, component) => {
        component.category = category;
        component.form = vm.state;

        if (category === 'input') {
            scope.state[component.state.id] = component.state;
        }

        vm.components.push(component);
    };

    vm.setListeners = () => {
        const listeners = eventService.addListeners([
            [form, 'keypress', vm.submitOnEnter]
        ]);

        scope.$on('$destroy', () => eventService.remove(listeners));
    };

    vm.submitOnEnter = event => {
        if (event.key !== 'Enter' || event.srcElement.type === 'textarea') {
            return;
        }

        event.preventDefault();
        scope.$apply(vm.submit);
    };

    vm.submit = () => {
        if (!vm.state.isValid) {
            return;
        }

        vm.state.disabled = true;

        const data = vm.components
            .filter(component => component.category === 'input')
            .reduce((values, component) => {
                if (component.state._value === undefined) {
                    return values;
                }

                if (component.state._format === 'selectFromOptions') {
                    values[component.state.id] = component.state._value[0];
                } else if (component.state._key && typeof component.state._value === 'object') {
                    values[component.state.id] = component.state._value[component.state._key];
                } else if (component.state._group) {
                    values[component.state._key] = values[component.state._key] || {};
                    values[component.state._key][component.state.id] = component.state._value;
                } else {
                    values[component.state.id] = component.state._value;
                }

                return values;
            }, {});

        scope.state.save(data)
            .then(scope.state.onSaveSuccess)
            .catch(err => vm.onSaveError(err))
            .finally(() => { vm.state.disabled = false; });
    };

    vm.onSaveError = err => {
        let handled;

        if (err.status === 400) {
            handled = vm.handleValidationError(err.data);
        }

        if (err.status === 500) {
            handled = vm.handleUnexpectedError(err);
        }

        if (!handled) {
            let message;
            const title = strings.get('form.SUBMISSION_ERROR_TITLE');
            const preface = strings.get('form.SUBMISSION_ERROR_PREFACE');

            if (typeof err.data === 'object') {
                message = JSON.stringify(err.data);
            } if (_.has(err, 'data.__all__')) {
                if (typeof err.data.__all__ === 'object' && Array.isArray(err.data.__all__)) {
                    message = JSON.stringify(err.data.__all__[0]);
                } else {
                    message = JSON.stringify(err.data.__all__);
                }
            } else {
                message = err.data;
            }

            modal.show(title, `${preface}: ${message}`);
        }
    };

    vm.handleUnexpectedError = () => {
        const title = strings.get('form.SUBMISSION_ERROR_TITLE');
        const message = strings.get('form.SUBMISSION_ERROR_MESSAGE');

        modal.show(title, message);

        return true;
    };

    vm.handleValidationError = errors => {
        const errorMessageSet = vm.setValidationMessages(errors);

        if (errorMessageSet) {
            vm.check();
        }

        return errorMessageSet;
    };

    vm.setValidationMessages = (errors, errorSet) => {
        let errorMessageSet = errorSet || false;

        Object.keys(errors).forEach(id => {
            if (!Array.isArray(errors[id]) && typeof errors[id] === 'object') {
                errorMessageSet = vm.setValidationMessages(errors[id], errorMessageSet);

                return;
            }

            vm.components
                .filter(component => component.category === 'input')
                .filter(component => errors[component.state.id])
                .forEach(component => {
                    errorMessageSet = true;

                    component.state._rejected = true;
                    component.state._message = errors[component.state.id].join(' ');
                });
        });

        return errorMessageSet;
    };

    vm.validate = () => {
        let isValid = true;

        for (let i = 0; i < vm.components.length; i++) {
            if (vm.components[i].category !== 'input') {
                continue;
            }

            if (!vm.components[i].state._isValid) {
                isValid = false;
                break;
            }
        }

        return isValid;
    };

    vm.check = () => {
        const isValid = vm.validate();

        if (isValid !== vm.state.isValid) {
            vm.state.isValid = isValid;
        }
    };

    vm.deregisterInputGroup = components => {
        for (let i = 0; i < components.length; i++) {
            for (let j = 0; j < vm.components.length; j++) {
                if (components[i] === vm.components[j].state) {
                    vm.components.splice(j, 1);
                    delete scope.state[components[i].id];
                    break;
                }
            }
        }
    };
}

AtFormController.$inject = ['EventService', 'ComponentsStrings'];

function atForm () {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        require: ['atForm'],
        templateUrl,
        controller: AtFormController,
        controllerAs: 'vm',
        link: atFormLink,
        scope: {
            state: '=',
            autocomplete: '@'
        }
    };
}

export default atForm;
