import React, { useRef, useCallback, ChangeEvent } from 'react';
import Input from '../input';
import Checkbox from '../checkbox';
import useReducer from 'utils/useReducerHelper';
import useRotate from 'styles/animation/useRotate';
import { animated } from 'react-spring';
import { ClearFilter, CollapseTransition, SelectedItemTransition } from './helpers';
import * as S from './style';
import { ISelect, ISelectInput } from './types';
import { useClickOutSide } from 'utils/hooks';

const Down = animated(S.DownIcon);

export const Select = function({
    options,
    onChange,
    value,
    width,
    filter = false,
    checkbox = false,
    checkAllLabel = '全部帐号',
    multiple,
    disabled,
    placeholder,
    defaultValue,
    noFocus,
    ...rest
}: ISelect) {
    if (value && !Array.isArray(value) && multiple) {
        throw new Error('多选模式的Select指定的value必须是数组');
    }
    const [state, dispatch] = useReducer({
        isOpen: false,
        checkAll: false,
        setDefault: false,
        selected: '',
        multiSelected: [],
        selectedIndex: -1,
        filterInput: '',
    });
    let controlledValue = false;
    const onFilterFocusBlur = ClearFilter(filter, dispatch);
    if ('value' in arguments[0]) {
        controlledValue = true;
        if (!multiple) {
            const find = options.findIndex(o => o.value === value);
            if (~find && state.selected !== options[find].label) {
                dispatch({ filterInput: options[find].label, selected: options[find].label, selectedIndex: find });
            }
        } else if (state.multiSelected.join() !== (value as any[]).join()) {
            dispatch({ multiSelected: value });
        }
    }
    if (!state.setDefault && 'defaultValue' in arguments[0]) {
        if (!multiple) {
            const find = options.findIndex(o => o.value === defaultValue);
            if (~find && state.selected !== options[find].label) {
                dispatch({ selected: options[find].label, setDefault: true, selectedIndex: find });
            }
        } else {
            dispatch({ multiSelected: defaultValue, setDefault: true });
        }
    }

    const multiRef: any = useRef();
    const inputRef: any = useRef();

    const onClick = useCallback(() => {
        !disabled && dispatch({ isOpen: multiple || !state.isOpen });
        setTimeout(() => {
            if (multiple && multiRef.current && multiRef.current.input) {
                multiRef.current.input.focus();
            }
        }, 0);
    }, [state.isOpen, disabled, multiple]);
    const rotate = useRotate(state.isOpen);

    const ref = useRef() as any;
    let filteredOptions: any = filter ? options.filter(o => o.label.includes(state.filterInput)) : options;
    if (filteredOptions.length === 0 && filter) {
        filteredOptions = (
            <S.NoData width={width}>
                <span>无匹配结果</span>
            </S.NoData>
        );
    }
    useClickOutSide(ref, () => {
        dispatch({ isOpen: false, filterInput: '' });
    });
    const height =
        Array.isArray(filteredOptions) && filteredOptions.length
            ? (filteredOptions.length + (checkbox ? 1 : 0)) * 36 + 12
            : 'auto';
    const collapse = CollapseTransition(state.isOpen + '_' + height, height);
    const multiSelectedItems = SelectedItemTransition(state.multiSelected);
    return (
        <S.Wrapper
            ref={ref}
            disabled={disabled}
            onClick={onClick}
            multiple={multiple && state.multiSelected.length}
            width={width}
            {...rest}
            onKeyDown={(e: any) => {
                const { key } = e;
                if (key === 'Escape') {
                    dispatch({ isOpen: false });
                    if (inputRef.current && inputRef.current.input) {
                        inputRef.current.input.blur();
                    }
                    e.preventDefault();
                    e.stopPropagation();
                }
                if (multiple && key === 'Backspace' && !state.filterInput && state.multiSelected.length) {
                    const multiSelected = [...state.multiSelected];
                    multiSelected.pop();
                    dispatch({ multiSelected });
                    onChange && onChange(multiSelected);
                    setTimeout(() => {
                        if (inputRef.current && inputRef.current.input) {
                            inputRef.current.input.focus();
                        }
                    }, 0);
                }
            }}
        >
            {multiple && state.multiSelected.length ? (
                <>
                    {multiSelectedItems.map(({ item: value, props }: any, index) => (
                        <S.MultiItem
                            style={props}
                            key={value}
                            onClick={e => {
                                e.preventDefault();
                            }}
                        >
                            {(options.find((o: any) => o.value === value) as any).label}
                            <S.Close
                                onClick={() => {
                                    const multiSelected = JSON.parse(JSON.stringify(state.multiSelected));
                                    multiSelected.splice(index, 1);
                                    dispatch({ multiSelected, filterInput: '' });
                                }}
                            />
                        </S.MultiItem>
                    ))}
                    <S.MultiInput
                        ref={multiRef}
                        width={1}
                        noFocus={noFocus}
                        value={state.filterInput}
                        onChange={({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
                            dispatch({ filterInput: value });
                        }}
                        onFocus={onFilterFocusBlur}
                    />
                    <S.Clear
                        onClick={(e: any) => {
                            dispatch({ isOpen: false, multiSelected: [], checkAll: false });
                            onChange && onChange([]);
                            e.stopPropagation();
                        }}
                    />
                </>
            ) : (
                <S.Input
                    noFocus={noFocus}
                    ref={inputRef}
                    value={filter ? state.filterInput : state.selected}
                    readOnly={!filter}
                    width={width}
                    disabled={disabled}
                    placeholder={placeholder}
                    onChange={({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
                        dispatch({ filterInput: value });
                    }}
                    onFocus={onFilterFocusBlur}
                />
            )}
            {!(multiple && state.multiSelected.length) && (
                <Down fillColor="#868DA1" fillColorRule="&&& path:first-child" style={rotate} className="down-icon" />
            )}
            {collapse.map(({ item, key, props }) => {
                const show = JSON.parse(item.split('_')[0]);
                return (
                    show && (
                        <S.Menu style={props} key={key}>
                            {checkbox && (
                                <S.MenuItem tabIndex={0} width={width}>
                                    <Checkbox
                                        checked={state.checkAll}
                                        style={{ transform: 'translateY(2px)' }}
                                        indeterminate={
                                            state.multiSelected.length > 0 &&
                                            state.multiSelected.length < filteredOptions.length
                                        }
                                        onChange={(e: boolean) => {
                                            if (e && Array.isArray(filteredOptions)) {
                                                dispatch({
                                                    multiSelected: filteredOptions.map(e => e.value),
                                                    isOpen: false,
                                                    checkAll: true,
                                                });
                                                onChange && onChange(filteredOptions.map(e => e.value));
                                            } else {
                                                dispatch({ multiSelected: [], checkAll: false });
                                                onChange && onChange([]);
                                            }
                                        }}
                                    >
                                        {checkAllLabel}
                                    </Checkbox>
                                </S.MenuItem>
                            )}
                            {Array.isArray(filteredOptions)
                                ? filteredOptions.map((o, index) => (
                                      <S.MenuItem
                                          tabIndex={0}
                                          width={width}
                                          key={index}
                                          selected={
                                              checkbox
                                                  ? false
                                                  : multiple
                                                  ? state.multiSelected.includes(o.value)
                                                  : state.selectedIndex === index
                                          }
                                          onClick={() => {
                                              const payload: any = {
                                                  multiSelected: JSON.parse(JSON.stringify(state.multiSelected)),
                                              };
                                              if (multiple) {
                                                  const mIndex = state.multiSelected.indexOf(o.value);
                                                  if (~mIndex) {
                                                      payload.multiSelected.splice(mIndex, 1);
                                                  } else {
                                                      payload.multiSelected.push(o.value);
                                                  }
                                                  payload.filterInput = '';
                                                  onChange && onChange(payload.multiSelected);
                                              } else {
                                                  payload.isOpen = false;
                                                  payload.selected = o.label;
                                                  payload.selectedIndex = index;
                                                  onChange && onChange(o.value);
                                              }
                                              if (!controlledValue) {
                                                  dispatch(payload);
                                              }
                                              !controlledValue &&
                                                  filter &&
                                                  !multiple &&
                                                  dispatch({ filterInput: o.label });
                                          }}
                                      >
                                          {multiple && !checkbox && state.multiSelected.includes(o.value) && (
                                              <S.Check />
                                          )}
                                          {checkbox ? (
                                              <Checkbox
                                                  checked={state.multiSelected.includes(o.value)}
                                                  style={{ transform: 'translateY(2px)', pointerEvents: 'none' }}
                                              >
                                                  {o.label}
                                              </Checkbox>
                                          ) : (
                                              o.label
                                          )}
                                      </S.MenuItem>
                                  ))
                                : filteredOptions}
                        </S.Menu>
                    )
                );
            })}
        </S.Wrapper>
    );
};
Select.defaultProps = {
    width: 158,
};
Select.SelectInput = SelectInput;
export default Select;

export function SelectInput({ input, select }: ISelectInput) {
    return (
        <S.SelectInputWrap>
            <Select {...select} />
            <Input {...input} />
        </S.SelectInputWrap>
    );
}
