import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import shortid from 'shortid';
import classNames from 'classnames';

import {validations, getValidator} from '../../helpers/helpers';
import findParentForm from '../../helpers/findParentForm';
import {isEqual} from "underscore";

function getSelectValues(select) {
    let result = [];
    let options = select && select.options;
    let opt;

    for (let i = 0; i < options.length; i++) {
        opt = options[i];

        if (opt.selected) {
            result.push(opt.value || opt.text);
        }
    }
    return result.length ? result : null;
}

@findParentForm
export default class CustomSelect extends Component {
    static displayName = 'FormElementSelect';

    static defaultProps = {
        search: true
    }

    get _formElement() {
        return true;
    }

    id = shortid();

    state = {
        focus: false,
        open: false,
        error: null,
        success: null,
        showSearch: null,
        value: this.props.value
    };

    get _value() {
        return this.state.value;
    }

    set(value) {
        if (this.props.mask) {
            value = conformToMask(
                value.toString(),
                this.props.mask,
                {
                    guide: false,
                    previousConformedValue: true
                }
            ).conformedValue;
        }
        this.input.value = value;
        if (this.props.onChange) {
            this.props.onChange(value);
        }
    }

    __handleClick = (e) => {
        let {open, focus} = this.state;
        if (
            e.target === this.select ||
            this.select.contains(e.target)
        ) {
            if (!open)
                this.setState({open: true, focus: true});
        } else {
            if (
                this.options &&
                e.target !== this.options &&
                !this.options.contains(e.target)
            ) {
                this.setState({open: false, focus: false});
            } else if (focus && !open) {
                this.setState({focus: false});
            }
        }
    };

    __onBlur(){
        if(this.props.onBlur){
            this.props.onBlur();
        }
        this.validate()
    }

    componentDidUpdate(prevProps, prevState) {
        // if (this.props.value !== prevProps.value) {
        //     this.input.value = this.props.value;
        // }
        if(this.state.search && !this.state.open && prevState.open){
            this.setState({search: null})
        }
        if(!this.state.focus && this.state.focus !== prevState.focus){
            this.__onBlur()
        }
        if(!isEqual(this.props.value, prevProps.value)){
            this.setState({
                value: this.props.value
            })
        }
    }

    componentDidMount() {
        if (typeof document !== "undefined") {
            document.body.addEventListener('click', this.__handleClick, false);
        }
    }


    componentWillUnmount() {
        if (typeof document !== "undefined") {
            document.body.removeEventListener('click', this.__handleClick, false);
        }
    }

    validate() {
        let {_value: value} = this;
        let {validate} = this.props;
        let promises = [];
        if (!validate) {
            this.setState({success: true, error: null});
            return Promise.resolve(value);
        } else if ({}.toString.call(validate) === '[object Function]') {
            promises.push(validate(value));
        } else {
            for (let key in validate) {
                let valid = null;
                let error = validate[key].error || 'ERROR';
                if (validate[key].custom) {
                    valid = validate[key].custom(value);
                } else {
                    let validator = getValidator(key);
                    valid = validations[validator.name](value, ...validator.args);
                }
                if (valid) {
                    promises.push(Promise.resolve(value))
                } else {
                    promises.push(Promise.reject(error));
                    break;
                }
            }
        }
        return Promise.all(promises).then(() => {
            this.setState({success: true, error: null});
            return Promise.resolve(value);
        }).catch((error) => {
            this.setState({success: null, error});
            return Promise.reject(error);
        })
    }

    __setValue(value) {
        let {mask} = this.props;
        if (mask) {
            value = conformToMask(
                value + "",
                mask,
            );
            this.input.value = value.conformedValue;
        } else {
            this.input.value = value;
        }
    }

    __setPositionForOptions = () => {
        let selectRect = this.select.getBoundingClientRect();
        return {
            position: 'absolute',
            left: selectRect.left + pageXOffset,
            top: selectRect.top + selectRect.height + pageYOffset,
            width: selectRect.width
        }
    };

    __selectItem(optionValue) {
        let {multiple} = this.props;
        let {value, open} = this.state;

        if (multiple) {
            if (!Array.isArray(value)) {
                value = value ? [value] : [];
            }
            let valueIndex = value.findIndex(f => f === optionValue);
            if (valueIndex !== -1) {
                value.splice(valueIndex, 1);
            } else {
                value.push(optionValue)
            }
        } else {
            value = optionValue
        }
        this.setState({value, open: multiple ? open : false}, () => {
            if(this.props.onChange){
                this.props.onChange(value);
            }
            this.validate();
        });
    }

    renderOptions() {
        let {value, search} = this.state;

        let {
            options,
            multiple,
            customSearch
        } = this.props;
        let emptyOptions = 0;

        let optionsList = options.map((option, index) => {
            if (search) {
                let text = (option.value + option.text).toUpperCase();
                if(
                    (customSearch && !customSearch(option)) ||
                    text.indexOf(search.toUpperCase()) === -1
                ){
                    emptyOptions++;
                    return null;
                }
            }
            let isSelected = null;
            if (multiple && value) {
                isSelected = !!value.find(f => f === option.value);
            } else {
                isSelected = option.value === value;
            }
            return <li
                key={index}
                onClick={(e)=>{
                    this.__selectItem(option.value);
                }}
                className={classNames({
                    'sbxFormField__selectCustomOptionsListItem': true,
                    'sbxFormField__selectCustomOptionsListItem_selected': isSelected,
                })}
            >
                {option.label || option.text || option.value}
            </li>
        });

        if (options.length === emptyOptions) {
            return <li className="sbxFormField__selectCustomOptionsEmpty">
                No data
            </li>
        }
        return optionsList;
    }

    render() {
        let {error, success, focus, value, open} = this.state;
        let {
            label,
            options,
            multiple,
            showSearch
        } = this.props;

        let fieldText = "No select";
        if (multiple) {
            if (value && value.length) {
                fieldText = value.map(value => {
                    let item = options.find(f => f.value === value);
                    return item.text || value;
                }).join(', ')
            }

        } else {
            fieldText = options.find(f => f.value === value);
            if (fieldText) {
                fieldText = fieldText.text;
            } else {
                fieldText = value;
            }
        }

        let props = {...this.props};

        delete props.validate;
        // delete props.children;
        delete props.value;
        delete props.onFocus;
        delete props.onBlur;
        delete props.options;


        return <div className="sbxFormField">
            {label && <label
                htmlFor={this.id}
                className="sbxFormField__label"
                onClick={e => {
                    this.setState({focus: true, open: true})
                }}
            >
                {label}
            </label>}
            <div className="sbxFormField__control-wrapper">
                {props.htmlbefore ? props.htmlbefore : null}
                <div
                    {...props}
                    id={this.id}
                    ref={e => this.select = e}
                    className={classNames({
                        'sbxFormField__selectCustom': true,
                        'sbxFormField__selectCustom_focus': focus,
                        'sbxFormField__selectCustom_valid': success,
                        'sbxFormField__selectCustom_invalid': error,
                        'sbxFormField__selectCustom_multiple': multiple,
                        'sbxFormField__selectCustom_open': open,
                        [props.className]: !!props.className
                    })}
                >
                    {fieldText}

                    <svg className="sbxFormField__selectCustom-chevron" width="24px" height="24px" viewBox="-20 -20 169 169" xmlns="http://www.w3.org/2000/svg">
                        <g>
                            <path d="m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z"/>
                        </g>
                    </svg>
                </div>
                {props.htmlafter ? props.htmlafter : null}
            </div>
            {open && ReactDOM.createPortal(<div
                className="sbxFormField__selectCustomOptions"
                ref={e => this.options = e}
                style={this.__setPositionForOptions()}
            >
                {showSearch && <input
                    autoFocus
                    type="text"
                    onChange={e => {
                        this.setState({search: e.target.value})
                    }}
                    placeholder="Search..."
                    className="sbxFormField__selectCustomOptionsSearch"
                />}
                <ul className="sbxFormField__selectCustomOptionsList">
                    {this.renderOptions()}
                </ul>
            </div>, document.body)}

            {error && <div
                className='sbxFormField__invalidFeedback'
            >
                {error}
            </div>}
        </div>
    }
}
