import React from 'react';
import { block } from 'bem-cn';
import toObjectKey from 'lib2/toObjectKey';
import SearchInput from 'components2/SearchInput';
import SearchItem from 'components2/SearchItem';
import './index.css';

const b = block('search-box');

function isValueTheSame(prevProps, nextProps) {
    return prevProps.values.length === nextProps.values.length &&
        prevProps.values.every((val, i) =>
            val.object.id === nextProps.values[i].object.id && val.type === nextProps.values[i].type);
}

export default class SearchBox extends React.PureComponent {
    constructor(props) {
        super(props);

        this.state = {
            values: new Map(),
        };

        this._onClick = this._onClick.bind(this);
        this._setInput = this._setInput.bind(this);
        this._onSelect = this._onSelect.bind(this);
        this._onRemove = this._onRemove.bind(this);
        this._onInputFocus = this._onInputFocus.bind(this);
        this._onInputBlur = this._onInputBlur.bind(this);
    }

    componentDidMount() {
        const { values } = this.props;

        if (values) {
            this._update(values);
        }
    }

    componentDidUpdate(prevProps) {
        if (isValueTheSame(prevProps, this.props)) {
            return;
        }

        const { values } = this.props;

        if (values) {
            this._update(values);
        }
    }

    _update(values) {
        const prevValues = this.state.values;
        const nextValues = new Map();

        values.forEach(item => {
            nextValues.set(toObjectKey(item), item);
        });

        this._onValuesChange(prevValues, nextValues);
    }

    _setInput(element) {
        this._searchInput = element;
    }

    _onSelect(item) {
        const prevValues = this.state.values;
        const nextValues = new Map(prevValues);

        if (prevValues.has(toObjectKey(item))) {
            return;
        }

        nextValues.set(toObjectKey(item), item);

        this._onValuesChange(prevValues, nextValues);
    }

    _onRemove(object, type) {
        const prevValues = this.state.values;
        const nextValues = new Map(prevValues);

        nextValues.delete(toObjectKey(object, type));

        this._onValuesChange(prevValues, nextValues);
    }

    _onValuesChange(prevValues, nextValues) {
        this.setState({
            values: nextValues,
        }, () => {
            this._focusInput();

            this.props.onChange(Array.from(nextValues.values()));
        });
    }

    _onInputFocus() {
        this.setState({ focused: true });
    }

    _onInputBlur() {
        this.setState({ focused: false });
    }

    _onClick() {
        this._focusInput();
    }

    _focusInput() {
        if (this._searchInput) {
            this._searchInput.focus();
        }
    }

    render() {
        const { focused } = this.state;
        const {
            cls,
            id,
            searchType,
            placeholder,
            showSearchIcon,
            limit = 1,
            values,
        } = this.props;

        return (
            <div
                className={b({ focused }).mix(cls)}
                onClick={this._onClick}
            >
                <div className={b('search-items')}>
                    {values.map(({ type, object }) => (
                        <SearchItem
                            cls={b('item')}
                            key={toObjectKey(object, type)}
                            type={type}
                            object={object}
                            onRemove={this._onRemove}
                        />
                    ))}
                    <div className={b('search-input', { visible: limit > values.length })}>
                        <SearchInput
                            id={id}
                            size="m"
                            onSelect={this._onSelect}
                            placeholder={placeholder}
                            showSearchIcon={showSearchIcon}
                            searchType={searchType}
                            ref={this._setInput}
                            onFocus={this._onInputFocus}
                            onBlur={this._onInputBlur}
                        />
                    </div>
                </div>
                <div className={b('border')} />
            </div>
        );
    }
}
