import React, {
  useMemo,
  useState,
  useCallback,
  useEffect,
  useRef,
  useImperativeHandle,
} from 'react';
import classNames from 'classnames';
import ConfigProvider from 'antd/es/config-provider';
import locale from 'antd/es/locale/zh_CN';
import Input, { InputProps } from 'antd/es/input';
import 'antd/es/input/style/index';
import Select from 'antd/es/select';
import 'antd/es/select/style/index';
import Button from 'antd/es/button';
import 'antd/es/button/style/index';
import Cascader, { CascaderValueType, CascaderOptionType, CascaderProps } from 'antd/es/cascader';
import 'antd/es/cascader/style/index';
import DatePicker from 'antd/es/date-picker';
import { PickerProps, PickerDateProps, RangePickerProps } from 'antd/es/date-picker/generatePicker';
import 'antd/es/date-picker/style/index';
import { checkType } from '@jy-fe/utils';
import { XuiReactFilterIcon } from '@jy-fe/icons';
import {
  SearchHeadHandles,
  SearchHeadProps,
  NodeType,
  ValidatorMapType,
} from './xui-search-head.d';

const XuiSearchHead: React.ForwardRefRenderFunction<SearchHeadHandles, SearchHeadProps> = (
  { searchNode, defaultValues = {}, onSearch, hideResetButton = false },
  ref,
) => {
  const className = 'xui-ant__search-head';
  const searchheadEl = useRef(null);
  /** 渲染的数据 */
  const [state, setState] = useState({} as any);
  /** 真正筛选的数据 */
  const [searchState, setSearchState] = useState({} as any);
  /** 外部input焦点控制 */
  const [visible, setVisible] = useState(false);

  /** 获取校验集 */
  const validatorMap = useMemo(() => {
    const map: ValidatorMapType = {};
    searchNode.forEach((node: NodeType) => {
      const { key, validator } = node;
      if (validator) {
        map[key] = validator;
      }
    });
    return map;
  }, [searchNode, visible]);

  /** 解析keys，rangeInput keys */
  const analysis = useMemo(() => {
    const keys: string[] = [];
    const outerKeys: string[] = [];
    const innerKeys: string[] = [];
    const rangeKeys: string[] = [];
    searchNode.forEach((node: NodeType) => {
      const { key, hidden, type } = node;
      keys.push(key);
      if (hidden) {
        innerKeys.push(key);
      } else {
        outerKeys.push(key);
      }
      if (type === 'rangeInput') {
        rangeKeys.push(key);
      }
    });
    return {
      keys,
      outerKeys,
      innerKeys,
      rangeKeys,
    };
  }, [searchNode]);

  /** 改变渲染的数据 */
  const changeState = useCallback(
    (newState: any, callback?: () => void) => {
      setState(newState);
      if (callback) {
        callback();
      }
    },
    [searchNode, state, analysis, visible],
  );

  const changeVisible = useCallback(
    (newVisible: boolean) => {
      if (newVisible) {
        setState(searchState);
      }
      setVisible(newVisible);
    },
    [visible, searchState],
  );

  /** 查找 */
  const search = useCallback(
    (newState?: any) => {
      let flag = true;
      const currentState = newState || state;
      const validatorKeys = Object.keys(validatorMap);
      for (let i = 0; i < validatorKeys.length; i++) {
        const f = validatorMap[validatorKeys[i]];
        if (f) {
          const { rangeKeys } = analysis;
          const currentKey = validatorKeys[i];
          const callbackParam: { [key: string]: any } = {};
          if (rangeKeys.includes(currentKey)) {
            callbackParam[`${currentKey}Low`] = currentState[`${currentKey}Low`];
            callbackParam[`${currentKey}High`] = currentState[`${currentKey}High`];
          } else {
            callbackParam[currentKey] = currentState[currentKey];
          }
          const result = f(callbackParam);
          if (!result) {
            flag = false;
            break;
          }
        }
      }
      if (flag) {
        let params = searchState;
        const { keys } = analysis;
        Object.keys(currentState).forEach((itemKey: string) => {
          if (
            keys.includes(itemKey) ||
            (itemKey.includes('Low') && keys.includes(itemKey.replace('Low', ''))) ||
            (itemKey.includes('High') && keys.includes(itemKey.replace('High', '')))
          ) {
            const value = currentState[itemKey];
            if (value || value === 0) {
              params = {
                ...params,
                [itemKey]: currentState[itemKey],
              };
            } else {
              delete params[itemKey];
            }
          }
        });
        const resultParams = {};
        Object.keys(params).forEach(pKey => {
          const pValue = params[pKey];
          if (
            !(
              (!pValue && pValue !== 0) ||
              (checkType(pValue) === 'Array' && pValue.length === 0) ||
              (checkType(pValue) === 'Object' && Object.keys(pValue).length === 0)
            )
          ) {
            resultParams[pKey] = pValue;
          }
        });
        setSearchState(resultParams);
        onSearch(resultParams, 'search');
        changeVisible(false);
      }
    },
    [state, searchState, searchNode, visible, analysis],
  );

  const dropdownClassName = `${className}--dropdown`;
  const classNamePopoverContentCol = `${className}--col`;
  const classNamePopoverContentColTitle = `${className}--col--title`;
  const classNamePopoverContentColContent = `${className}--col--content`;

  /** 渲染节点 */
  const renderNode = useMemo(() => {
    const outerNode: (JSX.Element | null)[] = [];
    const innerNode: (JSX.Element | null)[] = [];
    searchNode.forEach((node: NodeType) => {
      let dom: any = null;
      const {
        hidden,
        type,
        label,
        width,
        options = [],
        cascaderOptionMap = [],
        key,
        placeholder,
        extraProps,
        placeholders,
        getPopupContainer = () => document.body,
      } = node;
      if (type === 'input') {
        const props: InputProps = {
          className: classNamePopoverContentColContent,
          value: state[key],
          allowClear: true,
          placeholder,
          onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
            const { value } = e.target;
            const newState = {
              ...state,
              [key]: value || undefined,
            };
            changeState(newState);
          },
          onPressEnter: () => search(),
          ...extraProps,
        };
        if (!visible && width) {
          props.style = {
            width,
          };
        }
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <Input {...props} />
          </div>
        );
      } else if (type === 'select' || type === 'multiple') {
        const props = {
          className: classNamePopoverContentColContent,
          dropdownClassName,
          showArrow: true,
          value: state[key],
          placeholder,
          getPopupContainer,
          allowClear: true,
          onChange: (value: string) => {
            const newState = {
              ...state,
              [key]: value,
            };
            changeState(newState);
          },
          ...extraProps,
        } as any;
        if (type === 'multiple') {
          props.mode = 'multiple';
        }
        if (!visible && width) {
          props.style = {
            width,
          };
        }
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <Select {...props}>{options}</Select>
          </div>
        );
      } else if (type === 'rangeInput') {
        const lowProps: InputProps = {
          style: { width: 88 },
          placeholder: placeholders ? placeholders[0] : '',
          value: state[`${key}Low`],
          allowClear: true,
          onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
            const { value } = e.target;
            const newState = {
              ...state,
              [`${key}Low`]: value,
            };
            changeState(newState);
          },
          onPressEnter: () => search(),
          ...extraProps,
        };
        const highProps: InputProps = {
          style: { width: 88 },
          placeholder: placeholders ? placeholders[1] : '',
          value: state[`${key}High`],
          allowClear: true,
          onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
            const { value } = e.target;
            const newState = {
              ...state,
              [`${key}High`]: value,
            };
            changeState(newState);
          },
          onPressEnter: () => search(),
          ...extraProps,
        };
        if (!visible && width) {
          lowProps.style = {
            width,
          };
          highProps.style = {
            width,
          };
        }
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <div className={classNamePopoverContentColContent}>
              <Input {...lowProps} />
              <div className={`${classNamePopoverContentColContent}--segmentation`} />
              <Input {...highProps} />
            </div>
          </div>
        );
      } else if (type === 'cascader') {
        const props: CascaderProps = {
          popupClassName: dropdownClassName,
          options: cascaderOptionMap,
          placeholder,
          getPopupContainer,
          value: state[key],
          style: { width: '100%' },
          onChange: (value: CascaderValueType) => {
            const newState = {
              ...state,
              [key]: value,
            };
            setState(newState);
          },
          showSearch: {
            matchInputWidth: false,
            filter: (inputValue, path: CascaderOptionType[]) =>
              path.some((option: CascaderOptionType) => {
                if (option && option.label && typeof option.label === 'string') {
                  return option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1;
                }
                return false;
              }),
          },
          ...extraProps,
        };
        if (!visible && width) {
          props.style = {
            width,
          };
        }
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <div className={classNamePopoverContentColContent}>
              <Cascader {...props} />
            </div>
          </div>
        );
      } else if (type === 'datePicker') {
        const props: PickerProps<moment.Moment> = {
          dropdownClassName,
          placeholder,
          getPopupContainer,
          value: state[key],
          style: { width: '100%' },
          onChange: value => {
            const newState = {
              ...state,
              [key]: value,
            };
            setState(newState);
          },
          ...extraProps,
        };
        if (!visible && width) {
          props.style = {
            width,
          };
        }
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <div className={classNamePopoverContentColContent}>
              <DatePicker {...props} />
            </div>
          </div>
        );
      } else if (type === 'monthPicker') {
        const props: PickerDateProps<moment.Moment> = {
          dropdownClassName,
          placeholder,
          getPopupContainer,
          value: state[key],
          style: { width: '100%' },
          onChange: value => {
            const newState = {
              ...state,
              [key]: value,
            };
            setState(newState);
          },
          ...extraProps,
        };
        if (!visible && width) {
          props.style = {
            width,
          };
        }
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <div className={classNamePopoverContentColContent}>
              <DatePicker.MonthPicker {...props} />
            </div>
          </div>
        );
      } else if (type === 'rangePicker') {
        const props: RangePickerProps<moment.Moment> = {
          dropdownClassName,
          placeholder: placeholders,
          getPopupContainer,
          value: state[key],
          style: { width: '100%' },
          onChange: value => {
            const newState = {
              ...state,
              [key]: value,
            };
            setState(newState);
          },
          suffixIcon: null,
          ...extraProps,
        };
        dom = (
          <div key={key} className={classNamePopoverContentCol}>
            <div className={classNamePopoverContentColTitle}>{label}</div>
            <div
              className={`${classNamePopoverContentColContent} ${classNamePopoverContentColContent}--rangePicker`}
            >
              <DatePicker.RangePicker {...props} />
            </div>
          </div>
        );
      }
      if (hidden) {
        innerNode.push(dom);
      } else {
        outerNode.push(dom);
      }
    });
    return {
      outerNode,
      innerNode,
    };
  }, [searchNode, state, visible]);

  /** 重置 */
  const reset = useCallback(() => {
    setState({});
    setSearchState({});
    onSearch({}, 'reset');
  }, [searchState, analysis]);

  /** 判断是否在组件内 */
  const contains = useCallback((e: MouseEvent) => {
    let ownFlag = false;
    let dropdownFloag = false;
    if (searchheadEl.current) {
      ownFlag = (searchheadEl.current as any).contains(e.target);
    }
    const dropdownDom = document.querySelectorAll(`.${dropdownClassName}`);
    dropdownDom.forEach((element: any) => {
      if (element.contains(e.target)) {
        dropdownFloag = true;
      }
    });
    if (!(ownFlag || dropdownFloag)) {
      setVisible(false);
    }
  }, []);

  useImperativeHandle(ref, () => ({
    clear: () => {
      setState({});
      setSearchState({});
      onSearch({}, 'reset');
    },
    resetState: (paramNameList: string[]) => {
      const paramObjNeedClear = Object.assign(
        {},
        ...paramNameList.map(item => ({ [item]: undefined })),
      );

      setState((pre: { [key: string]: any }) => ({
        ...pre,
        ...paramObjNeedClear,
      }));
      setSearchState((pre: { [key: string]: any }) => ({
        ...pre,
        ...paramObjNeedClear,
      }));
    },
  }));

  useEffect(() => {
    if (defaultValues) {
      setState(defaultValues);
      setSearchState(defaultValues);
    }
  }, [defaultValues]);

  useEffect(() => {
    window.addEventListener('click', contains);
    return () => {
      window.removeEventListener('click', contains);
    };
  }, []);

  return (
    <ConfigProvider locale={locale}>
      <div className={`${className}--position`} ref={searchheadEl}>
        <div className={className}>
          {renderNode.outerNode}
          {analysis.innerKeys.length > 0 && (
            <div
              className={classNames({
                [`${className}--iconfont`]: true,
              })}
              onClick={() => setVisible(!visible)}
            >
              <XuiReactFilterIcon />
            </div>
          )}
          <div className={`${className}--btn`}>
            <Button type='primary' onClick={() => search()}>
              查询
            </Button>
            {!hideResetButton && <Button onClick={reset}>重置</Button>}
          </div>
        </div>
        <div
          className={classNames({
            [className]: true,
            [`${className}--popup`]: true,
            [`${className}--show`]: visible,
          })}
        >
          {renderNode.outerNode}
          {renderNode.innerNode}
          <div className={`${className}--btn`}>
            <Button type='primary' onClick={() => search()}>
              查询
            </Button>
            {!hideResetButton && <Button onClick={reset}>重置</Button>}
          </div>
        </div>
      </div>
    </ConfigProvider>
  );
};

export default React.forwardRef(XuiSearchHead);
