import React, { useState, useCallback, useEffect } from 'react';
import { DragDropContext } from 'react-beautiful-dnd';
import Mousetrap from 'mousetrap';
import { generateId } from 'utils/utils';
import LayerList from './LayerList';
import DeleteModal from '../../../../components/DeleteConfirmModal/Modal';
import { flattenToObject, getGroupChildren } from '../../../../utils/component';

export default function SortableLayerList({ layers, layersSelected, constants, onSelect, onSort, onChange, onReset, onCopy, onDelete }) {
  const [draggableId, setDraggableId] = useState(null);
  const [visible, setVisible] = useState(false);
  const connectedLayers = connectLayersWithSelected(layers, layersSelected, constants);
  const layersById = flattenToObject(connectedLayers);
  useEffect(() => {
    // 置顶
    Mousetrap.bind(['command+shift+up', 'ctrl+shift+up', 'command+alt+ctrl+up'], (e) => {
      e.preventDefault && e.preventDefault();
      if (layersSelected.length) {
        onSort(upToTop(layers, layersSelected));
      }
    });
    // 置底
    Mousetrap.bind(['command+shift+down', 'ctrl+shift+down', 'command+alt+ctrl+down'], (e) => {
      e.preventDefault && e.preventDefault();
      if (layersSelected.length) {
        onSort(downToBottom(layers, layersSelected));
      }
    });
    // 上移
    Mousetrap.bind(['command+alt+up', 'ctrl+alt+up'], (e) => {
      e.preventDefault && e.preventDefault();
      if (layersSelected.length) {
        onSort(upOne(layers, layersSelected));
      }
    });
    // 下移
    Mousetrap.bind(['command+alt+down', 'ctrl+alt+down'], (e) => {
      e.preventDefault && e.preventDefault();
      if (layersSelected.length) {
        onSort(downOne(layers, layersSelected));
      }
    });
    // 成组
    Mousetrap.bind(['command+g', 'ctrl+g'], (e) => {
      e.preventDefault && e.preventDefault();
      if (layersSelected.length) {
        const [newLayers, newGroupId] = groupBy(layers, layersSelected);
        onSort(newLayers);
        onSelect([newGroupId]);
      }
    });
    // 取消成组
    Mousetrap.bind(['command+shift+g', 'ctrl+shift+g'], (e) => {
      e.preventDefault && e.preventDefault();
      const [newLayers, newSelected] = breakUp(layers, layersSelected);
      onSelect(newSelected);
      onSort(newLayers);
    });
    // 锁定
    Mousetrap.bind(['command+shift+l', 'ctrl+shift+l'], (e) => {
      e.preventDefault && e.preventDefault();
      const isLock = layersSelected.every((d) => layersById[d] && layersById[d].lock);
      const changes = layersSelected.map((d) => ({
        id: d,
        key: 'lock',
        value: !isLock
      }));
      onChange(changes);
    });
    // 隐藏
    Mousetrap.bind(['command+shift+h', 'ctrl+shift+h'], (e) => {
      e.preventDefault && e.preventDefault();
      const isShow = layersSelected.every((d) => layersById[d] && layersById[d].show);
      const changes = layersSelected.map((d) => ({
        id: d,
        key: 'show',
        value: !isShow
      }));
      onChange(changes);
    });

    return () => {
      Mousetrap.unbind(['command+shift+up', 'ctrl+shift+up', 'command+alt+ctrl+up']);
      Mousetrap.unbind(['command+shift+down', 'ctrl+shift+down', 'command+alt+ctrl+down']);
      Mousetrap.unbind(['command+alt+up', 'ctrl+alt+up']);
      Mousetrap.unbind(['command+alt+down', 'ctrl+alt+down']);
      Mousetrap.unbind(['command+g', 'ctrl+g']);
      Mousetrap.unbind(['command+shift+g', 'ctrl+shift+g']);
      Mousetrap.unbind(['command+shift+l', 'ctrl+shift+l']);
      Mousetrap.unbind(['command+shift+h', 'ctrl+shift+h']);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [layers, layersSelected]);

  useEffect(() => {
    // 复制
    Mousetrap.bind(['command+d', 'ctrl+d'], (e) => {
      if (layersSelected.length) {
        e.preventDefault && e.preventDefault();
        onCopy();
      }
    });
    return () => {
      Mousetrap.unbind(['command+d', 'ctrl+d']);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [layersSelected]);

  useEffect(() => {
    // 删除
    Mousetrap.bind(['del', 'backspace'], () => {
      if (layersSelected.length) {
        setVisible(true);
      }
    });
    return () => {
      Mousetrap.unbind(['del', 'backspace']);
    };
  }, [layersSelected]);

  const onDragStart = useCallback((draggableId) => {
    setDraggableId(draggableId);
  }, []);

  const onDragEnd = useCallback(
    ({ destination, source, draggableId }) => {
      setDraggableId(null);
      // 移动到图层外面
      if (!destination) {
        return;
      }

      // 拖动的是未选中的组件
      if (layersSelected.indexOf(draggableId) === -1) {
        // eslint-disable-next-line react-hooks/exhaustive-deps
        layersSelected = [draggableId];
        onSelect([draggableId]);
      }
      const direction = source.index > destination.index ? 'up' : 'down';
      const { droppableId, index } = destination;
      const destinationLayer = droppableId === 'top' ? layers[index] : layersById[droppableId].components[index];
      if (destinationLayer && layersSelected.indexOf(destinationLayer.id) === -1) {
        const [unselected, selected] = getSelectedAndUnelectedLayers(layers, layersSelected.concat());
        const resultLayers = getMovedLayers(unselected, selected, destinationLayer.id, direction);
        onSort(resultLayers);
      }
    },
    [layers, layersSelected]
  );

  const handleSelect = (e, id) => {
    if (e.metaKey || e.ctrlKey) {
      handleCtrlSelect(id);
    } else if (e.shiftKey) {
      handleShiftSelect(id);
    } else {
      handleSingleSelect(id);
    }
  };

  // 单选
  const handleSingleSelect = (id) => {
    if (!(layersSelected.length === 1 && layersSelected[0] === id)) {
      onSelect([id]);
    }
  };

  // ctrl + 选择
  const handleCtrlSelect = (id) => {
    if (layersSelected.indexOf(id) > -1) {
      onSelect(layersSelected.filter((d) => d !== id));
    } else {
      if (!isInSelected(id, layersById) && !isContainSelected(id, layersById)) {
        onSelect(layersSelected.concat(id));
      }
    }
  };

  // shift + 选择
  const handleShiftSelect = (id) => {
    if (layersSelected.length) {
      let lastSelectedId = layersSelected[layersSelected.length - 1];
      if (id === lastSelectedId) return;
      let lastSelected = layersById[lastSelectedId];
      let currentClick = layersById[id];
      let rangeIds = getRangeLayerIds(layers, lastSelected.path, currentClick.path);
      let all = Array.from(new Set(layersSelected.concat(rangeIds)));
      let allWithoutInGroup = all.filter((d: any) => !layersById[d].groupPath.some((v) => all.indexOf(v) > -1));
      onSelect(allWithoutInGroup);
    } else {
      let all = layers.map((d) => d.id);
      onSelect(all);
    }
  };

  const onHideModal = useCallback(() => {
    setVisible(false);
  }, []);

  const onCommitDelete = useCallback(() => {
    onDelete();
    setVisible(false);
  }, [onDelete]);

  return (
    <DragDropContext onDragEnd={onDragEnd} onDragStart={onDragStart}>
      <LayerList layers={connectedLayers} draggableId={draggableId} onSelect={handleSelect} resetLayers={onReset} onChange={onChange} />
      {layersSelected.length ? <DeleteModal visible={visible} onClose={onHideModal} onOk={onCommitDelete} /> : null}
    </DragDropContext>
  );
}

const connectLayersWithSelected = (layers, layersSelected, constants) => {
  return layers.map((d) => {
    let constant = constants.find((v) => v.moduleName === d.moduleName);
    let thumb = constant && constant.thumb;
    let newLayer = { ...d, selected: layersSelected.indexOf(d.id) > -1, thumb };
    if (d.components) {
      newLayer.components = connectLayersWithSelected(d.components, layersSelected, constants);
    }
    return newLayer;
  });
};

const isInSelected = (id, layersById) => {
  let groupPath = layersById[id].groupPath;
  return groupPath.some((d) => layersById[d].selected);
};

const isContainSelected = (id, layersById) => {
  let children = getGroupChildren(layersById[id]);
  return children.some((d) => d.selected);
};

const getRangeLayerIds = (layers, start, end) => {
  const shouldExchange = (start, end) => {
    if (start[0] > end[0]) {
      return true;
    } else if (start[0] === end[0]) {
      return shouldExchange(start.slice(1), end.slice(1));
    } else {
      return false;
    }
  };
  if (shouldExchange(start, end)) {
    let bridge = start;
    start = end;
    end = bridge;
  }

  let result: any = [];
  const getRangeLayerAndGroup = (layers, start, end?) => {
    let range = end ? layers.slice(start[0], end[0] + 1) : layers.slice(start[0]);
    if (range.length) {
      if (range.length === 1 && start.length > 1 && end.length > 1 && start[0] === end[0]) {
        //同级
        getRangeLayerAndGroup(range[0].components, start.slice(1), end.slice(1));
        return;
      }
      if (start.length > 1) {
        let first = range.splice(0, 1);
        getRangeLayerAndGroup(first[0].components, start.slice(1));
      }

      if (range.length && end && end.length > 1) {
        let last = range.splice(range.length - 1, 1);
        getRangeLayerAndGroup(last[0].components, [0], end.slice(1));
      }

      range.map((item) => result.push(item.id));
    }
  };
  getRangeLayerAndGroup(layers, start, end);
  return result;
};

const upToTop = (layers, selected) => {
  return layers.reduce((all, item, index) => {
    // 当被选中的、不是第一个、且往上还有未被选中的，则移动至第一个未被选中的
    if (selected.indexOf(item.id) > -1 && index > 0) {
      let firstUnselected = all.findIndex((item) => selected.indexOf(item.id) === -1);
      if (firstUnselected !== -1) {
        all.splice(firstUnselected, 0, item);
        return all;
      } else {
        return all.concat(item);
      }
    } else if (item.components) {
      return all.concat({ ...item, components: upToTop(item.components, selected) });
    } else {
      return all.concat(item);
    }
  }, []);
};

const downToBottom = (layers, selected) => {
  return layers
    .concat()
    .reverse()
    .reduce((all, item, index) => {
      // 当被选中的、不是最后一个、且往下还有未被选中的，则移动至最后一个未被选中的
      if (selected.indexOf(item.id) > -1 && index > 0) {
        let firstUnselected = all.findIndex((item) => selected.indexOf(item.id) === -1);
        if (firstUnselected !== -1) {
          all.splice(firstUnselected, 0, item);
          return all;
        } else {
          return all.concat(item);
        }
      } else if (item.components) {
        return all.concat({ ...item, components: downToBottom(item.components, selected) });
      } else {
        return all.concat(item);
      }
    }, [])
    .reverse();
};

const upOne = (layers, selected) => {
  return layers.reduce((all, item, index) => {
    // 当被选中的、不是当前组的第一个、当前项的前面一个未被选中，则向前移一项
    if (selected.indexOf(item.id) > -1 && index > 0 && selected.indexOf(all[index - 1].id) === -1) {
      let merged = all.concat(item);
      merged[index] = merged[index - 1];
      merged[index - 1] = item;
      return merged;
    } else if (item.components) {
      return all.concat({ ...item, components: upOne(item.components, selected) });
    } else {
      return all.concat(item);
    }
  }, []);
};

const downOne = (arr, selected) => {
  return arr
    .concat()
    .reverse()
    .reduce((all, item, index) => {
      // 当被选中的、不是当前组的第一个、当前项的前面一个未被选中，则向前移一项
      if (selected.indexOf(item.id) > -1 && index > 0 && selected.indexOf(all[index - 1].id) === -1) {
        let merged = all.concat(item);
        merged[index] = merged[index - 1];
        merged[index - 1] = item;
        return merged;
      } else if (item.components) {
        return all.concat({ ...item, components: downOne(item.components, selected) });
      } else {
        return all.concat(item);
      }
    }, [])
    .reverse();
};

const groupBy = (layers, selected) => {
  let groupByComponents: any = [];

  const handleGroupBy = (layers, selected) => {
    return layers.reduce((all, item) => {
      if (!selected.length) {
        return all.concat(item);
      }

      let i = selected.indexOf(item.id);
      if (i > -1) {
        selected.splice(i, 1);
        groupByComponents.push(item);
        if (selected.length) {
          return all;
        } else {
          groupByComponents = [
            {
              id: `group_${generateId()}`,
              name: '分组',
              show: true,
              lock: false,
              collapsed: true,
              components: groupByComponents
            }
          ];
          return all.concat(groupByComponents);
        }
      } else if (item.components) {
        let result = handleGroupBy(item.components, selected);
        return all.concat({ ...item, components: result });
      } else {
        return all.concat(item);
      }
    }, []);
  };
  const newLayers = handleGroupBy(layers, selected.concat());
  return [newLayers, groupByComponents[0].id];
};

const breakUp = (layers, selected) => {
  return layers.reduce(
    (all, item) => {
      if (item.components && selected.indexOf(item.id) > -1) {
        return [all[0].concat(item.components), all[1].concat(item.components.map((d) => d.id))];
      } else if (item.components) {
        let result = breakUp(item.components, selected);
        return [all[0].concat({ ...item, components: result[0] }), all[1].concat(result[1])];
      } else {
        return [all[0].concat(item), all[1]];
      }
    },
    [[], []]
  );
};

const getSelectedAndUnelectedLayers = (layers, selected) => {
  return layers.reduce(
    (all, item) => {
      if (!selected.length) {
        return [all[0].concat(item), all[1]];
      }

      let i = selected.indexOf(item.id);
      if (i > -1) {
        selected.splice(i, 1);
        let merged = all[1].concat(item);
        return [all[0], merged];
      } else if (item.components) {
        let result = getSelectedAndUnelectedLayers(item.components, selected);
        return [all[0].concat({ ...item, components: result[0] }), all[1].concat(result[1])];
      } else {
        return [all[0].concat(item), all[1]];
      }
    },
    [[], []]
  );
};

const getMovedLayers = (unselected, selected, destinationId, direction) => {
  return unselected.reduce((all, item) => {
    if (item.id === destinationId) {
      if (direction === 'up') {
        return all.concat(selected).concat(item);
      } else {
        return all.concat(item).concat(selected);
      }
    } else if (item.components) {
      return all.concat({
        ...item,
        components: getMovedLayers(item.components, selected, destinationId, direction)
      });
    } else {
      return all.concat(item);
    }
  }, []);
};
