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

import Item from './Item';
import Label from './Label';
import Error from './Error';
import Buttons from './Buttons';
import Separator from './Separator';
import Title from './Title';
import Description from './Description';

import './index.css';
import './buttons.css';

import './column-section-header.css';
import './card-section.css';
import './card-section-header.css';

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

function sanitizeValue(s) {
    return s ? validator.stripLow(_.trim(s)) : 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())}`;
}

function getOriginalFieldName(name) {
    return String(name).replace(/__\d+$/, '');
}

const Form = React.createClass({

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

    componentDidMount() {
        this._discardProtectedFieldValues();
    },

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

    getData() {
        const form = this._getElement();

        const data = formSerialize(form, {
            hash: true,
            empty: true,
        });

        // data['password__<random_number>'] > data['password']
        Object.keys(data).forEach(key => {
            const originalKey = getOriginalFieldName(key);

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

        return sanitizeValueSet(data);
    },

    getInput() {
        const elements = this._getElement().querySelectorAll('[name]');
        const state = {};

        for (let i = 0; i < elements.length; i++) {
            state[elements[i].name] = elements[i].value;
        }

        return state;
    },

    setInput(state) {
        if (!state) {
            return;
        }

        const elements = this._getElement().querySelectorAll('[name]');

        for (let i = 0; i < elements.length; i++) {
            if (state[elements[i].name] !== undefined) {
                elements[i].value = state[elements[i].name];
            }
        }
    },

    _getElement() {
        return ReactDOM.findDOMNode(this.refs.form);
    },

    // DIR-4382
    _discardProtectedFieldValues() {
        const form = this._getElement();
        let protectedTextFields = [];

        PROTECTED_FIELD_NAMES.forEach(name => {
            protectedTextFields = protectedTextFields.concat(
                Array.prototype.slice.call(form.querySelectorAll(`input[name^="${name}__"]`))
                    .filter(({ type }) => type !== 'checkbox' && type !== 'radio')
            );
        });

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

        setTimeout(() => {
            protectedTextFields.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._getElement());

                this._start();

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

    _start() {
        if (this._getElement()) {
            this.setState({ inProgress: true });
        }
    },

    _stop() {
        if (this._getElement()) {
            this.setState({ inProgress: false });
        }
    },

    render() {
        const formProps = _.omit(this.props, ['children', 'title']);
        let title;

        if (this.props.title) {
            title = <h2 className="form__title">{this.props.title}</h2>;
        }

        return (
            <form
                ref="form"
                {...formProps}
                onSubmit={this._handleFormSubmit}
            >

                {title}
                {this.props.children}
                <Loader visible={this.state.inProgress} />

            </form>
        );
    },

});

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

Form.defaultProps = {
    className: 'form',
};

_.extend(Form, {
    Item,
    Label,
    Error,
    Buttons,
    Separator,
    Title,
    Description,
    getSafeFieldName,
});

export default Form;
