module.exports = form;

var React = require('react');
var ReactDOM = require('react-dom');
var util = require('./util');
var t = require('tcomb-form');
var Form = t.form.Form;
var extend = require('extend');
var $ = require('jquery');

function scrollIntoViewIfNeeded(el) {
    if (el.scrollIntoViewIfNeeded) {
        el.scrollIntoViewIfNeeded();

        return true;
    }

    if (el.scrollIntoView) {
        el.scrollIntoView();

        return true;
    }

    return false;
}

function form(formFactory, config, values, initialState) {
    return React.createClass({
        getInitialState: function() {
            return extend(true, {
                options: {},
                values: values || {},
                errors: {},
                successMsg: null,
                errorMsg: null
            }, initialState || {});
        },
        componentDidUpdate: function() {
            if (!this.state.errors) {
                return;
            }

            if (this.state.successMsg) {
                $(ReactDOM.findDOMNode(this)).each(function() {
                    scrollIntoViewIfNeeded(this);
                });
            } else {
                var el = $(ReactDOM.findDOMNode(this.refs.form));

                Object.keys(this.state.errors).every(function(value) {
                    el.find('[name="' + value + '"]:input, [name$="[' + value + ']"]:input').first().each(function() {
                        if (!scrollIntoViewIfNeeded(this)) {
                            this.focus();
                        }
                    });
                    return false;
                }.bind(this));
            }
        },
        onSubmit: function(evt) {
            if (!config.url) {
                return;
            }

            evt.preventDefault();

            var result = this.refs.form.validate();

            if (!result.isValid()) {
                // Autofocus first error'd input (https://github.com/gcanti/tcomb-form/issues/175#issuecomment-129896577)
                var component = this.refs.form.getComponent(result.errors[0].path);
                var formGroupElement = ReactDOM.findDOMNode(component);
                $(formGroupElement).each(function() {
                    if (!scrollIntoViewIfNeeded(this)) {
                        $(this).find(':input').focus();
                    }
                });

                return;
            }

            var values = result.value;

            var submits = $('[type=submit]', ReactDOM.findDOMNode(this)).trigger('hdeng-button-spinner-start');

            var dataCallback = config.dataCallback || function(values) {
                return util.flattenStruct(values);
            }.bind(this);

            var successCallback = config.successCallback || function(data) {
                $.when((config.onSuccess || $.noop).call(this, data))
                    .always(function() {
                        if (config.successUri) {
                            location.href = config.successUri;
                            return;
                        }

                        submits.trigger('hdeng-button-spinner-stop');

                        this.setState({
                            values: values,
                            successMsg: config.successMsg,
                            errors: {},
                            errorMsg: null
                        });
                    }.bind(this))
                ;
            }.bind(this);

            var errorCallback = config.errorCallback || function(xhr, data) {
                $.when((config.onError || $.noop).call(this, xhr, data))
                    .always(function() {
                        submits.trigger('hdeng-button-spinner-stop');

                        if (!xhr.responseJSON) {
                            return;
                        }

                        var state = {
                            values: values,
                            successMsg: null,
                            errors: {},
                            errorMsg: null
                        };

                        var errors = util.extractErrors(xhr.responseJSON);

                        if (errors.fields) {
                            state.errors = errors.fields;
                        } else if (errors.message) {
                            state.errorMsg = errors.message;
                        }

                        this.setState(state);
                    }.bind(this))
                ;
            }.bind(this);

            var defaultSubmitCallback = function(data, successCallback, errorCallback) {
                $.ajax({
                    url: config.url,
                    dataType: 'json',
                    type: config.method || 'POST',
                    data: data,
                    success: function() {
                        successCallback.call(this, data);

                        if (!config.successUri) {
                            submits.trigger('hdeng-button-spinner-stop');
                        }
                    }.bind(this),
                    error: function(xhr) {
                        errorCallback.call(this, xhr, data);
                        submits.trigger('hdeng-button-spinner-stop');
                    }.bind(this)
                });
            }.bind(this);

            var submitCallback = config.submitCallback || defaultSubmitCallback;

            submitCallback.call(
                this,
                dataCallback.call(this, values),
                successCallback,
                errorCallback,
                defaultSubmitCallback
            );
        },
        onChange: function(value) {
            config.changeCallback && config.changeCallback.call(this, value);
        },
        render: function() {
            if (this.state.successMsg) {
                return <div className="alert alert--success" role="alert" dangerouslySetInnerHTML={{__html: this.state.successMsg}}></div>;
            }

            var msg;

            if (this.state.errorMsg) {
                msg = <div className="alert alert--danger" role="alert">{this.state.errorMsg}</div>;
            } else if (config.infoMsg) {
                msg = <div className="alert alert--info" role="alert" dangerouslySetInnerHTML={{__html: config.infoMsg}}></div>;
            }

            var form = formFactory(this.state);

            var options = extend(
                true,
                {},
                this.state.options,
                form.options
            );

            var value = extend(
                true,
                {},
                values,
                this.state.values,
                form.values || {}
            );

            return (
                <form onSubmit={this.onSubmit} role="form" noValidate>
                    {msg}
                    <Form ref="form" onChange={this.onChange} type={form.type} options={options} value={value}/>
                    <div className="form-buttons">
                        {config.buttons.map(function(button, i) {
                            var className = 'button ladda-button';
                            if (button.className) {
                                className += ' ' + button.className
                            }

                            if (button.url) {
                                return <a key={'button' + i} href={button.url} className={className}>{button.label}</a>;
                            }

                            return <button key={'button' + i} type="submit" className={className} data-style="expand-right" data-spinner-size="25">{button.label}</button>;
                        })}
                    </div>
                </form>
            );
        }
    });
}

form.util = util;
