import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import formSerialize from 'form-serialize';
import validator from 'validator';
import { FormTiming } from 'lib/rum';

import Item from './Item';
import Label from './Label';

import './index.styl';

const PROTECTED_FIELD_NAMES = [
    'login', 'password', 'password_confirmation', 'password_change_required',
];

function sanitizeValue(s) {
    return s ? validator.stripLow(String(s).trim()) : s;
}

function sanitizeValueSet(data) {
    _.forEach(data, (value, key) => {
        const iterable = value instanceof Array || value instanceof Object;

        data[key] = iterable ? sanitizeValueSet(value) : sanitizeValue(value);
    });

    return data;
}

function getSafeFieldName(name) {
    return `${name}_${Math.floor(1e8 * Math.random())}`;
}

const Form = React.createClass({

    _name: 'lego-form',

    getInitialState() {
        return { processing: false };
    },

    componentWillUnmount() {
        this._unmounted = true;
    },

    _initialize(element) {
        this._element = element;
        this._discardProtectedFieldValues();
    },

    submit() {
        return this._handleFormSubmit();
    },

    getData() {
        const data = formSerialize(this._element, {
            hash: true,
            empty: true,
        });

        // data['password_<random_number>'] > data['password']
        Object.keys(data).forEach(key => {
            const originalKey = String(key).replace(/_\d+$/, '');

            if (originalKey !== key && PROTECTED_FIELD_NAMES.indexOf(originalKey) !== -1) {
                data[originalKey] = data[key];
                delete data[key];
            }
        });

        return sanitizeValueSet(data);
    },

    _discardProtectedFieldValues() {
        if (!this._element) {
            return;
        }

        const protectedFields = Array.prototype.slice.call(
            this._element.querySelectorAll('input[type="password"], input[name^="login_"]')
        );

        protectedFields.forEach(input => {
            input.parentNode.style.opacity = '0';
        });

        setTimeout(() => {
            protectedFields.forEach(input => {
                if (input.type === 'password') {
                    input.setAttribute('autocomplete', 'new-password');
                }
                input.value = '';
                input.parentNode.style.opacity = '';
            });
        }, 250);
    },

    _handleFormSubmit(event) {
        const { onSubmit } = this.props;
        const data = this.getData(event);

        if (event) {
            event.preventDefault();
        }

        if (onSubmit) {
            const output = onSubmit(data);

            if (output && output.then) {
                const timing = new FormTiming(this._element);

                this._start();

                output
                    .then(() => {
                        this._stop();
                        timing.end('form.submit.success');
                    })
                    .catch(() => {
                        this._stop();
                        timing.end('form.submit.failure');
                    });
            }
        }
    },

    _start() {
        if (!this._unmounted) {
            this.setState({ processing: true });
        }
    },

    _stop() {
        if (!this._unmounted) {
            this.setState({ processing: false });
        }
    },

    render() {
        const formProps = _.omit(this.props, 'children');
        const className = [
            this._name,
            this.state.processing ? `${this._name}_processing` : null,
            this.props.className,
        ].filter(Boolean).join(' ');

        return (
            <form
                {...formProps}
                className={className}
                onSubmit={this._handleFormSubmit}
                ref={this._initialize}
            >
                {this.props.children}
            </form>
        );
    },

});

// @if NODE_ENV='development'
Form.propTypes = {
    onSubmit: PropTypes.func,
};
// @endif

_.extend(Form, { Item, Label, getSafeFieldName });

export default Form;
