/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import * as React from 'react';
import { Input, Icon, Checkbox } from 'cn-next';

import './style/index.scss';
import { Obj, ICnColumn } from 'src/type';
import { useControlValue } from './hooks';
import { debounce, isDef, isFunction, isString } from './util';

interface Iitem extends Obj {
  label?: string;
  value?: string;
  [labelKey: string]: any;
  children: Iitem[];
}

export interface IListFilter {
  list: Iitem[];
  value: string[];
  defaultValue: string[];
  onChange: (value: string[]) => void;
  openKeys: string[];
  defaultOpenKeys: string[];
  onChangeOpenKeys: (value: string[]) => void;
  labelKey?: string;
  valueKey?: string;
  className?: string;
  searchClassName?: string;
  style?: React.CSSProperties;
  searchStyle?: React.CSSProperties;
  searchTransform?: (str: string) => string;
  column: ICnColumn;
}

export default function ListFilter(props: IListFilter) {
  const {
    list,
    labelKey,
    valueKey,
    className,
    style,
    searchStyle,
    searchTransform,
    column,
  } = props;
  const [search, setSearch] = React.useState('');
  const onChangeSearch = debounce(
    (val: string) => {
      setSearch(val);
    },
    500,
    false,
  );

  const allValue = React.useMemo(() => getAllValue(list, valueKey), [list]);

  const filterList = React.useMemo(
    () =>
      getFilterList(
        list,
        isFunction(searchTransform) ? searchTransform(search) : search,
        labelKey,
      ),
    [list, search, labelKey],
  );

  // console.log(allValue, 'allValue');
  const [value, setValue] = useControlValue(props as unknown as Obj, {
    defaultValuePropName: 'defaultValue',
    valuePropName: 'value',
    changePropName: 'onChange',
    defaultValue: [],
  });
  // console.log(value, 'value');

  const filterValue = React.useMemo(() => {
    return (
      filterList?.filter((item) => {
        return value.includes(item.id);
      }) || []
    );
  }, [filterList, value]);
  const [openKeys, setOpenKeys] = useControlValue(props as unknown as Obj, {
    defaultValuePropName: 'defaultOpenKeys',
    valuePropName: 'openKeys',
    changePropName: 'onChangeOpenKeys',
    defaultValue: [],
  });

  const valueMap = React.useMemo((): any => {
    return Array.isArray(value)
      ? value.reduce(
          (ret, next) => ({
            ...ret,
            [next]: true,
          }),
          {},
        )
      : {};
  }, [value]);
  const openKeysMap = React.useMemo((): any => {
    return Array.isArray(openKeys)
      ? openKeys.reduce(
          (ret, next) => ({
            ...ret,
            [next]: true,
          }),
          {},
        )
      : {};
  }, [openKeys]);

  const onChange = (changeValue: string, checked: boolean) => {
    const ret = Array.isArray(value) ? [...value] : [];
    if (checked && !valueMap[changeValue]) {
      ret.push(changeValue);
      setValue(ret);
    } else if (!checked && valueMap[changeValue]) {
      const valueSet = new Set(ret);
      valueSet.delete(changeValue);
      setValue(Array.from(valueSet));
    }
  };

  const onChangeOpenKeys = (changeOpenKey: string, isOpen: boolean) => {
    const ret = Array.isArray(openKeys) ? [...openKeys] : [];
    if (isOpen && !openKeysMap[changeOpenKey]) {
      ret.push(changeOpenKey);
      setOpenKeys(ret);
    } else if (!isOpen && openKeysMap[changeOpenKey]) {
      const valueSet = new Set(ret);
      valueSet.delete(changeOpenKey);
      setOpenKeys(Array.from(valueSet));
    }
  };

  const onCheckAll = (checked: boolean) => {
    setValue(checked ? [...allValue] : []);
  };
  const onCheckAllFilterValue = (checked: boolean) => {
    const filterValueList = filterList?.map((item) => item.id);
    if (checked) {
      setValue([...value, ...filterValueList]);
    } else {
      setValue(allValue.filter((item) => filterList.includes(item)));
    }
  };
  // @ts-ignore
  function renderList(list: Iitem[], level: number = 1) {
    return Array.isArray(list)
      ? list.map(
          (
            item: Iitem = {
              children: [],
            },
            index = 1,
          ) => (
            <>
              <div
                className={`cn-list-filter-item ${
                  valueMap[item?.[valueKey ?? 'value']]
                    ? 'cn-list-filter-selected'
                    : ''
                }`}
                key={item?.[valueKey ?? 'value']}
              >
                {item?.children?.length > 0 ? (
                  <CaretRightIcon
                    className={
                      openKeysMap[item?.[valueKey ?? 'value']] &&
                      'cn-list-filter-opened'
                    }
                    onClick={() => {
                      onChangeOpenKeys(
                        item?.[valueKey ?? 'value'],
                        !openKeysMap[item?.[valueKey ?? 'value']],
                      );
                    }}
                  />
                ) : null}
                <Checkbox
                  checked={valueMap[item?.[valueKey ?? 'value']] ?? false}
                  onChange={(checked) => {
                    onChange(item?.[valueKey ?? 'value'], checked);
                  }}
                  className='cn-list-filter-checkbox'
                />
                {column?.render instanceof Function
                  ? column.render(
                      item?.[column.code ?? column.key ?? labelKey ?? ''],
                      item,
                      level === 1 ? index : -1,
                    )
                  : isUndefOrNullString(item[labelKey ?? 'label'])
                  ? '空'
                  : item[labelKey ?? 'label']}
              </div>

              {openKeysMap[item?.[valueKey ?? 'value']] && (
                <div
                  style={{ marginLeft: level * 16 }}
                  className='cn-list-filter-list'
                >
                  {renderList(item?.children, level + 1)}
                </div>
              )}
            </>
          ),
        )
      : null;
  }
  return (
    <div
      style={style}
      className={`cn-list-filter${className ? ` ${className ?? ''}` : ''}`}
    >
      <Input
        innerBefore={<Icon type='search' style={{ margin: 4 }} />}
        placeholder='search'
        className='cn-list-filter-search'
        onChange={onChangeSearch}
        style={searchStyle}
      />
      <div className='cn-list-filter-list cn-list-filter-wrapper'>
        {!search ? (
          <div
            className={`cn-list-filter-item ${
              allValue.length === value?.length ? 'cn-list-filter-selected' : ''
            }`}
          >
            <Checkbox
              checked={allValue.length === value?.length}
              className='cn-list-filter-checkbox'
              onChange={onCheckAll}
            />
            全选({value?.length ?? 0}/{allValue?.length ?? 0})
          </div>
        ) : filterList?.length ? (
          <div
            className={`cn-list-filter-item ${
              allValue.length === filterList?.length
                ? 'cn-list-filter-selected'
                : ''
            }`}
          >
            <Checkbox
              checked={filterValue.length === filterList?.length}
              className='cn-list-filter-checkbox'
              onChange={onCheckAllFilterValue}
            />
            全选搜索项目({filterValue?.length ?? 0}/{filterList?.length ?? 0})
          </div>
        ) : null}
        {renderList(filterList, 1)}
      </div>
    </div>
  );
}

function dfs(list: Iitem[], handlerCallback: (item: Iitem) => void) {
  Array.isArray(list) &&
    list.forEach((item: Iitem) => {
      handlerCallback instanceof Function && handlerCallback(item);
      if (item?.children?.length > 0) {
        dfs(item?.children, handlerCallback);
      }
    });
}

// 是否为叶子结点
function isLeafNode(item: Iitem) {
  return !(item?.children?.length > 0);
}

function dfsHouxu(
  list: Iitem[],
  handlerCallback: (item: Iitem, level: number) => void,
  level = 1,
) {
  Array.isArray(list) &&
    list.forEach((item: Iitem) => {
      if (item?.children?.length > 0) {
        dfsHouxu(item?.children, handlerCallback, level + 1);
      }
      handlerCallback instanceof Function && handlerCallback(item, level);
    });
}
function getAllValue(list: Iitem[], valueKey: string) {
  const allValue: string[] = [];

  dfs(list, (item: Iitem) => {
    if ((valueKey ?? 'value') in item) {
      allValue.push(item?.[valueKey ?? 'value']);
    }
  });

  return allValue;
}

function getNewNode(labelKey: string): Iitem {
  return {
    [labelKey ?? 'label']: '',
    value: '',
    children: [],
  };
}

function getFilterList(
  list: Iitem[],
  search: string,
  labelKey: string = 'label',
): Iitem[] {
  if (search === '') {
    return list;
  }

  let nowNodes: Iitem | null = null;
  let nowNodeLevel = -1;

  let nowTree: Iitem | null = null;
  let nowTreeLevel = -1;

  dfsHouxu(list, (item: Iitem, level) => {
    if (nowNodeLevel === -1) {
      // 初始化
      nowNodeLevel = level;
      nowNodes = getNewNode(labelKey);
    }

    if (level < nowNodeLevel) {
      // 增加了一层，就做一层统计
      if (nowNodes && nowNodes?.children?.length > 0) {
        // 当前有节点, 则新建
        const newTempNode = {
          ...item,
          children: nowNodes?.children,
        };
        nowNodes = {
          ...getNewNode(labelKey),
          children: [newTempNode],
        };
      }
      if (level === nowTreeLevel && nowTree && nowNodes) {
        // 重新返回
        nowTree.children = nowTree.children.concat(nowNodes.children);
        nowNodes = nowTree;
      }
    } else if (level > nowNodeLevel) {
      // 开始深入下去
      // 记录当前start
      nowTreeLevel = nowNodeLevel;
      nowTree = nowNodes;
      // 记录当前end
      nowNodeLevel = level;
      nowNodes = getNewNode(labelKey);
      if (
        item &&
        isString(item[labelKey ?? 'label']) &&
        item[labelKey ?? 'label'].includes(search)
      ) {
        nowNodes?.children?.push?.(item);
      }
    } else if (level === nowNodeLevel) {
      // 平层，开始compare
      // @ts-ignore
      if (
        item &&
        isString(item[labelKey ?? 'label']) &&
        item[labelKey ?? 'label'].includes(search)
      ) {
        nowNodes?.children?.push?.(item);
      }
    }

    // 同步层次
    nowNodeLevel = level;
  });
  // @ts-ignore
  return nowNodes && nowNodes.children ? nowNodes.children : list;
}

interface IconProps extends React.SVGProps<SVGElement> {
  height?: number;
  preserveAspectRatio?: string;
  title?: string;
  viewBox?: string;
  width?: number;
  xmlns?: string;
  ref?: any;
}

function CaretRightIcon(props: IconProps) {
  return (
    <svg
      focusable='false'
      preserveAspectRatio='xMidYMid meet'
      fill='currentColor'
      width='16'
      height='16'
      viewBox='0 0 32 32'
      {...props}
    >
      <path d='M12 8L22 16 12 24z' />
    </svg>
  );
}

function isUndefOrNullString(val: any) {
  return !isDef(val) || (isString(val) && val.replace(/\s/g, '') === '');
}
