import React, { useMemo, useRef, useEffect, useContext, useState, useCallback } from 'react';
import styles from './index.less';
import { connect } from 'react-redux';
import { useHistory } from 'react-router-dom';
import ReactFlow, {
  MiniMap,
  useNodesState,
  useEdgesState,
  Background,
  useKeyPress,
  Node,
} from '@easyv/react-flow-renderer';
import { NodesContext } from '../../Context/index';
import { changeNodeScale, toggleLeftCollapsed, toggleRightCollapsed } from 'models/create-app';
import EasyvNodeCard from 'components/EasyvNodeCard';
import ToolBar from 'components/ToolBar';
import EasyvNodeGroup from 'components/EasyvNodeCard/Group';
import MouseTrap from 'mousetrap';
import _max from 'lodash/max';
import _find from 'lodash/find';
import _min from 'lodash/min';
import { State } from 'containers/nodes-edit/index';
import { updateScreentEditModel, screenEditModel } from 'models/screen-edit';
import EasyvSwitch from 'components/EasyvSwitch';
import { editPageModelLInk, isGroupNode } from 'utils/nodes';
import defaultEvent from '../../../../../images/default_event.svg';
import { NodeData } from 'models/node-types';
import { useAuth } from 'components/easyUse';
import { modules } from '@/constants/authority';

const nodeTypes = { EasyvNodeCard: EasyvNodeCard, EasyvNodeGroup: EasyvNodeGroup };

const zoomMax = 2,
  zoomMin = 0.2;

function Content({
  node,
  selectNodes = [],
  selectEdges = [],
  nodeCurrents: _nodeCurrents = [],
  leftCollapsed,
  rightCollapsed,
  nodeScale,
  editModel,
  changeNodeScale,
  updateScreentEditModel,
  toggleLeftCollapsed,
  toggleRightCollapsed,
}: {
  editModel: screenEditModel;
  node: NodeData;
  nodeCurrents: string[];
  selectNodes: string[];
  selectEdges: string[];
  leftCollapsed: boolean;
  rightCollapsed: boolean;
  nodeScale: number;
  changeNodeScale: (scale: number) => void;
  updateScreentEditModel: (state) => void;
  toggleLeftCollapsed: () => void;
  toggleRightCollapsed: () => void;
}) {
  const {
    setCanvas,
    canvas,
    eventNodes,
    setActivatedState,
    setCurrentNode,
    clearCurrentNode,
    updateLayerNode,
    updateLayerEdge,
    onContextMenu,
  } = useContext(NodesContext);
  const [isCommand, setCommand] = useState(false);
  const contentDom = useRef<HTMLDivElement>(null);
  const nodeCurrents = useMemo(() => {
    return _nodeCurrents.concat(eventNodes);
  }, [_nodeCurrents, eventNodes]);
  const { checkAuth } = useAuth();

  useEffect(() => {
    function getNodesSize(nodes, code = 'x') {
      const sizes: number[] = nodes.map((d) => d.positionAbsolute?.[code]);
      const max = _max(sizes);
      const min = _min(sizes);
      const lastNode =
        _find(nodes, (o: Node) => {
          return o.positionAbsolute?.[code] === max;
        }) || {};
      const { width = 0, height = 0 } = lastNode;
      if (max && min) {
        return max - min + (code === 'x' ? width : height);
      }
      return 0;
    }
    function getNodesPosttion(nodes, code = 'x') {
      const sizes: number[] = nodes.map((d) => d.positionAbsolute?.[code]);
      const min = _min(sizes);
      return min || 0;
    }
    if (eventNodes.length) {
      const nodes = nodeCurrents.map((d) => canvas?.getNode(d)).filter((d) => d);
      const width = getNodesSize(nodes, 'x');
      const height = getNodesSize(nodes, 'y');
      const x = getNodesPosttion(nodes, 'x');
      const y = getNodesPosttion(nodes, 'y');
      canvas?.fitBounds(
        {
          x,
          y,
          width,
          height,
        },
        {
          padding: 1,
          duration: 300,
        },
      );
    }
  }, [eventNodes, canvas]);

  const _selectNodes = selectNodes.length
    ? Array.from(new Set(selectNodes.concat(nodeCurrents)))
    : selectNodes;

  const ialElements: any = useMemo(() => {
    const nodes = node.nodes.map((d) => {
      const id = d.id + '';
      const active = nodeCurrents.concat(eventNodes).includes(id);
      const localState = _selectNodes.length
        ? _selectNodes.includes(id)
          ? State.NORM
          : State.INTERACT_HIDDEN
        : d.state;

      return {
        id: d.id,
        data: {
          ...d,
          active,
          state: localState,
        },
        selected: active,
        extent: d.group && 'parent',
        type: 'EasyvNodeCard',
        parentNode: d.group,
        zIndex: 1,
        position: { x: d.left, y: d.top },
      };
    });

    const groups = node.groups.map((d) => {
      return {
        id: d.id,
        type: 'EasyvNodeGroup',
        extent: d.group && 'parent',
        parentNode: d.group,
        data: d,
        style: { width: d.width, height: d.height },
        position: { x: d.left, y: d.top },
      };
    });

    const edges = node.edges
      .map((d) => {
        if (d) {
          const selected = selectEdges.includes(d.id);
          return {
            data: d,
            id: d.id,
            selected,
            source: d.sourceNode,
            target: d.targetNode || d.target,
            sourceHandle: d.source,
            targetHandle: d.target,
            style: { stroke: '#3D404D' },
            animated: selected,
          };
        }
      })
      .filter((d) => d);

    return {
      nodes,
      edges,
      groups,
    };
  }, [node, selectEdges, selectNodes, nodeCurrents]);

  const onInit = (reactFlowInstance) => {
    setCanvas(reactFlowInstance);
    if (Array.isArray(nodeCurrents) && nodeCurrents.length === 1) {
      const node = reactFlowInstance?.getNode(nodeCurrents[0]);
      if (node) {
        const { positionAbsolute, width, height } = node;
        const { x, y } = positionAbsolute;
        reactFlowInstance?.fitBounds(
          {
            x,
            y,
            width,
            height,
          },
          {
            padding: 1,
            duration: 300,
          },
        );
      }
    }
  };

  const onChange = (value) => {
    updateScreentEditModel(value);
    editPageModelLInk(value, history);
  };

  useEffect(() => {
    setNodes(ialElements.groups.concat(ialElements.nodes));
    setEdges(ialElements.edges);
  }, [ialElements]);

  const [nodes, setNodes, onNodesChange] = useNodesState([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);

  const history = useHistory();
  const spacePressed = useKeyPress('Space');

  const toggleLeft = () => {
    toggleLeftCollapsed();
  };

  const toggleRight = () => {
    toggleRightCollapsed();
  };

  const onZoomIn = () => {
    if (nodeScale < zoomMax) {
      changeNodeScale(nodeScale + 0.1);
      canvas?.zoomIn({ duration: nodeScale + 0.1 });
    }
  };

  const onZoomOut = () => {
    if (nodeScale > zoomMin) {
      changeNodeScale(nodeScale - 0.1);
      canvas?.zoomOut({ duration: nodeScale - 0.1 });
    }
  };

  const handleResize = () => {
    changeNodeScale(1);
    canvas?.setViewport({ x: 0, y: 0, zoom: 1 }, { duration: 300 }); // 拉到头部
  };

  useEffect(() => {
    MouseTrap.bind(['alt+command+1', 'alt+ctrl+1'], toggleLeft);
    MouseTrap.bind(['alt+command+2', 'alt+ctrl+2'], toggleRight);
    MouseTrap.bind(['command+=', 'ctrl+='], onZoomIn);
    MouseTrap.bind(['command+-', 'ctrl+-'], onZoomOut);
    MouseTrap.bind(['command+2', 'ctrl+2'], handleResize); // 重置
    MouseTrap.bind(
      ['mod'],
      () => {
        setCommand(false);
      },
      'keyup',
    );
    MouseTrap.bind(
      ['mod'],
      () => {
        setCommand(true);
      },
      'keydown',
    );
    MouseTrap.bind(['mod+f'], () => {
      // 为了抵消搜索快捷键冲突
      setCommand(false);
    });

    return () => {
      MouseTrap.unbind(['mod']);
      MouseTrap.unbind(['mod+f']);
      MouseTrap.unbind(['command+=', 'ctrl+=']);
      MouseTrap.unbind(['command+-', 'ctrl+-']);
      MouseTrap.unbind(['command+2', 'ctrl+2']);
      MouseTrap.unbind(['command+1', 'ctrl+1']);
      MouseTrap.unbind(['alt+command+1', 'alt+ctrl+1']);
      MouseTrap.unbind(['alt+command+2', 'alt+ctrl+2']);
    };
  }, [canvas]);

  const onNodeClick = useCallback(
    (event, node) => {
      // 点击节点
      if (!isGroupNode(node.data.type)) {
        const id = node.id;
        if (isCommand) {
          if (nodeCurrents.includes(id)) {
            // 取消
            setCurrentNode(nodeCurrents.filter((v) => v !== id));
          } else {
            // 新增
            setCurrentNode(nodeCurrents.concat(id));
          }
        } else {
          setCurrentNode(id);
        }
        updateLayerNode([]);
        updateLayerEdge([]);
        setActivatedState('');
      }
    },
    [nodeCurrents],
  );

  const onPaneDoubleClick = () => {
    // 双击画布空白处
    updateLayerNode([]);
    updateLayerEdge([]);
    clearCurrentNode();
    setActivatedState('');
  };

  const onNodeContextMenu = (event, node) => {
    onContextMenu?.({
      event,
      id: node.id,
    });
  };

  return (
    <div className={styles.content} ref={contentDom}>
      <div style={{ height: '100%', position: 'relative' }}>
        {!nodes.length && (
          <div className={styles.defaultEvent}>
            <img src={defaultEvent}></img>
            <span style={{ fontSize: '14px' }}>暂未配置自定义事件</span>
          </div>
        )}
        <ReactFlow
          nodes={nodes}
          edges={edges}
          minZoom={zoomMin}
          selectNodesOnDrag={false}
          maxZoom={zoomMax}
          onlyRenderVisibleElements={true} // 按需加载
          onEdgeClick={(event, edge) => {
            // 点击线
            updateLayerEdge([edge.id]);
          }}
          onNodeClick={onNodeClick}
          onPaneDoubleClick={onPaneDoubleClick}
          onNodeContextMenu={onNodeContextMenu}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          panOnDrag={spacePressed}
          selectionKeyCode="" // 多选快捷键
          deleteKeyCode="" //删除快捷键
          zoomOnDoubleClick={false}
          onInit={onInit}
          nodesDraggable={false} // 节点不能拖动
          fitView={false}
          nodeTypes={nodeTypes}
          key="nodes-edit">
          <MiniMap
            nodeColor={(node) => {
              if (nodeCurrents.includes(node.id)) {
                return '#3A89FE';
              }
              // 组
              if (isGroupNode(node.data.type)) {
                return '#232630';
              }
              return '#3D404C';
            }}
            nodeStrokeWidth={0}
            maskColor="rgba(0,0,0,.6)"
            style={{
              border: 'none',
              backgroundColor: '#12131B',
              borderRadius: '2px',
            }}
          />
          <Background gap={12} size={1} color="#292C37" style={{ backgroundColor: '#181A24' }} />
        </ReactFlow>
      </div>
      <div className={styles.toolBar}>
        <ToolBar leftShow={!leftCollapsed} rightShow={!rightCollapsed} />
      </div>
      {checkAuth(modules.eventNodes) && (
        <div className={styles.switch}>
          <EasyvSwitch value={editModel} onChange={onChange} />
        </div>
      )}
    </div>
  );
}

const mapStateToProps = ({ createApp }) => {
  const { leftCollapsed, nodeScale, rightCollapsed } = createApp;
  return {
    nodeScale,
    leftCollapsed,
    rightCollapsed,
  };
};
const mapDispatchToProps = {
  changeNodeScale,
  toggleLeftCollapsed,
  toggleRightCollapsed,
  updateScreentEditModel,
};

export default connect(mapStateToProps, mapDispatchToProps)(Content);
