import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import ScrollArea from 'ui-externals/ScrollArea';

import './index.styl';

const Select = React.createClass({

    _name: 'lego-select',

    getInitialState() {
        return {
            value: this.props.val,
        };
    },

    _openEvent() {
        if (this._self && !this._self.contains(event.target)) {
            this.setState({ open: false });
        }
    },

    componentWillUnmount() {
        document.removeEventListener('click', this._openEvent);
    },

    _registerSelf(element) {
        if (!this._self) {
            document.addEventListener('click', this._openEvent);
        }
        this._self = element;
    },

    _registerValueHolder(element) {
        this._valueHolder = element;
    },

    _getOption(value) {
        return _.find(this.props.options, { val: value });
    },

    // не selectable элементы не имеют собственного значения val,
    // обычно это кастомные элементы с поведением, отличным от дефолтного
    _isOptionSelectable(option) {
        return option.val !== undefined && option.val !== null;
    },

    _handleValueClick() {
        this.setState({
            open: !this.state.open,
        });
    },

    _handleOptionClick(index) {
        const option = this.props.options[index];

        if (!option) {
            return;
        }

        // если элемент не selectable, к нему не применяются
        // стандартные обработчики

        if (this._isOptionSelectable(option) && option.val !== this.state.value) {
            this.setState({
                open: false,
                empty: !(option.content || option.text),
                value: option.val,
            });

            setTimeout(() => {
                if (this.props.onChange) {
                    this.props.onChange({ target: this._valueHolder });
                }
            }, 0);
        } else {
            this.setState({
                open: false,
            });
        }

        if (option.onSelect) {
            option.onSelect({ target: this._valueHolder });
        }
    },

    _renderOption(option, index) {
        const className = [
            `${this._name}__option`,
            option.val === this.state.value ? `${this._name}__option_selected` : null,
            this._isOptionSelectable(option) ? `${this._name}__option_regular` : null,
        ].filter(Boolean).join(' ');

        return (
            <div
                className={className}
                key={index}
                onClick={index === undefined ? null : this._handleOptionClick.bind(this, index)}
            >
                {option.content || option.text}
            </div>
        );
    },

    _renderOptions() {
        const items = this.props.options.map(this._renderOption);
        const components = {};

        if (items.length) {
            components.list = (
                <ScrollArea
                    className={`${this._name}__scroll-area`}
                    style={{ maxHeight: this.props.maxListHeight }}
                    minScrollSize={18}
                >
                    {items}
                </ScrollArea>
            );
        }

        if (this.props.after) {
            components.after = this._renderOption({ content: this.props.after });
        }

        if (!Object.keys(components).length) {
            return null;
        }

        return (
            <div className={`${this._name}__option-list`}>
                {components.list}
                {components.after}
            </div>
        );
    },

    _renderValues() {
        const items = this.props.options.map((option, index) => {
            let content = option.content || option.text;

            if (this.props.prerenderValue) {
                content = this.props.prerenderValue(content);
            }

            return (
                <div className={`${this._name}__value`} key={index}>
                    {content}
                </div>
            );
        });

        if (!items.length) {
            return null;
        }

        return (
            <div
                className={`${this._name}__value-list`}
                onClick={this._handleValueClick}
            >
                {items}
            </div>
        );
    },

    render() {
        const { width, placeholderLabel } = this.props;
        const component = {};

        const className = [
            this._name,
            this.state.open ? `${this._name}_open` : null,
            this.state.empty ? `${this._name}_empty` : null,
            placeholderLabel ? `${this._name}_labeled` : null,
            `${this._name}_size-${this.props.size}`,
            `${this._name}_border-${this.props.border}`,
            width === 'available' ? `${this._name}_full-width` : null,
        ].filter(Boolean).join(' ');

        if (placeholderLabel) {
            component.placeholderLabel = (
                <div className={`${this._name}__placeholder-label`}>
                    {placeholderLabel}
                </div>
            );
        }

        const selectedOption = this._getOption(this.state.value);
        let content = selectedOption ? selectedOption.content || selectedOption.text : '';

        // см. propTypes
        if (this.props.prerenderValue) {
            content = this.props.prerenderValue(content);
        }

        // скрытый список из this._renderValues() позволяет автоматически
        // отрегулировать ширину селекта по ширине самого длинного элемента

        return (
            <div className={className} ref={this._registerSelf}>
                {this._renderValues()}
                <div
                    className={`${this._name}__value ${this._name}__value_selected`}
                    onClick={this._handleValueClick}
                >
                    {content}
                </div>
                {this._renderOptions()}
                {component.placeholderLabel}
                <input
                    type="hidden"
                    name={this.props.name}
                    value={this.state.value}
                    ref={this._registerValueHolder}
                />
            </div>
        );
    },

});

// @if NODE_ENV='development'
Select.propTypes = {
    // props.options:
    // [{ val?, (content|text)?: primitive | ReactComponent, onSelect?: function(event) }]
    options: PropTypes.array,
    border: PropTypes.oneOf(['all', 'bottom']),
    maxListHeight: PropTypes.number,
    // props.prerenderValue позволяет вывести выбранное значение селекта
    // иначе, чем определено в его свойствах, например, с добавлением префиксного
    // текста или другого кастомного оформления
    prerenderValue: PropTypes.func,
};
// @endif

Select.defaultProps = {
    size: 'm',
    border: 'all',
    options: [],
};

export default Select;
