import React, { useEffect, useMemo, useState, useRef, useCallback } from 'react';
import Header from './components/Header/index';
import Layers from './components/Layers/index';
import TipCard from './components/TipCard/index';
import Content from './components/Content/index';
import { useHistory } from 'react-router-dom';
import styles from './index.less';
import { nodesDataTransform, getComponentOptions, treeDataConversion } from 'utils/nodes';
import { connect } from 'react-redux';
import { NodesProvider } from './Context/index';
import { getAssociation, resetNodeCardId } from 'utils/nodes';
import { NodeData } from 'models/node-types';
import { selectLayers } from 'models/create-app';
import { ConfigProvider } from '@easyv/react-gui';
import { ReactFlowInstance } from '@easyv/react-flow-renderer';
import { setScreenState } from 'models/screen-detail';
import { eventTracking } from '@/models/event-tracking';
import {
  setCurrentNode,
  clearCurrentNode,
  updateMenuState,
  MenuActionState,
  updateLayerNode,
  updateLayerEdge,
  updateLayerNodeEages,
} from 'models/node';
import lazyContainer from '../../lazyContainer';
import message from 'components/message';
import { setCustomEvents } from 'models/queue';
import { CustomEvent } from 'models/queue.type';
import { changeLayerStatus } from 'models/screen';

const ContextMenuModal = lazyContainer(
  () => import(/* webpackChunkName: "context-node-menu" */ './components/menu'),
);
export enum State {
  NORM,
  ERROR,
  ACTIVE,
  HIDDEN,
  DEFAULT_HIDDEN,
  INTERACT_HIDDEN, //交互隐藏，常用于隐藏不相关/只显示上游/只显示下游
}

export interface Layer {
  name: string;
  id: number | string;
  layers?: Layer[];
  moduleName?: string;
  editionId:string;
  type: string;
  show: boolean;
  color: string;
  selectNodes: string[];
  selectEdges: string[];
  changeLayerStatus: (x: string) => void;
  clearCurrentNode: () => void;
  setCustomEvents: (data: CustomEvent[]) => void;
  setCurrentNode: (id: string) => void;
  updateLayerNode: (ids: string[]) => void;
  updateLayerEdge: (ids: string[]) => void;
}
function NodesEdit({
  componentsById,
  editModel,
  id,
  editionId,
  selectNodes = [],
  selectEdges = [],
  nodeCurrents,
  screensById,
  panelsById,
  activatedState,
  constants,
  tenantConstants,
  setCurrentNode,
  updateLayerNode,
  updateLayerEdge,
  setScreenState,
  clearCurrentNode,
  updateLayerNodeEages,
  changeLayerStatus,
  updateMenuState,
  selectLayers,
  eventTracking,
  setCustomEvents,
}) {
  const screen = screensById[id];
  const [canvas, setCanvas] = useState<ReactFlowInstance>();
  const [nodeData, setNodeData] = useState<NodeData>({ nodes: [], groups: [], edges: [] });
  const [layers, setLayer] = useState([]);
  const layerRef = useRef<HTMLDivElement>(null);
  const [visible, setVisible] = useState(false);
  const [eventNodes, setEventNode] = useState<string[]>([]);
  const [nodeUp, setNodeUp] = useState<string[]>([]);
  const [nodeDown, setNodeDown] = useState<string[]>([]);
  const [edges, setEdges] = useState<{
    upEdges: string[];
    downEdges: string[];
  }>();
  const [location, setLocation] = useState({
    top: 0,
    left: 0,
  });
  const history = useHistory();

  useEffect(() => {
    if (screen) {
      // 进入节点管理的次数
      eventTracking({
        id: `${id}-editEventNodes`,
          type: 'editEventNodes',
          screenName: screen.name,
          screenId: id,
          num: 1,
      });
    }
  }, [id, screen]);

  useEffect(() => {
    const now = Date.now();
    return () => {
      const interval = Date.now() - now;
      screen && eventTracking({
        id: `${id}-nodesPageTime`,
        type: 'nodesPageTime',
        screenName: screen.name,
        screenId: id,
        timeInterval: interval});
    };
  }, [id]);

  useEffect(() => {
    const handleClick = (e) => {
      const nodeHideDom = document.querySelector('#node-asjkndajnbasd-hide');
      if (!layerRef.current?.contains(e.target) && !nodeHideDom?.contains(e.target)) {
        const appDOM = document.getElementById('easyv-app');
        if (appDOM && appDOM.contains(e.target)) {
          setVisible(false);
        }
      }
    };

    document.addEventListener('click', handleClick, false);
    return () => {
      document.removeEventListener('click', handleClick);
    };
  }, [layerRef]);

  useEffect(() => {
    const nodesLayerData = nodesDataTransform({
      componentsById,
      id,
      panelsById,
      screensById,
      constants,
      tenantConstants,
    });

    const dataConversion = new treeDataConversion(
      nodesLayerData,
      componentsById,
      screensById,
      panelsById,
    );
    dataConversion.getData().then(({ nodes, groups, edges }) => {
      const nodeLayers = getComponentOptions({
        componentsById,
        panelsById,
        screensById,
        constants,
        screenId: id,
        nodes: nodes.map((d) => d.parentId || d.id),
      });
      const edgeIds = Array.from(new Set(edges.map((d) => d.id)));

      setLayer(nodeLayers);
      setNodeData({
        nodes,
        groups,
        edges: edgeIds.map((id) => edges.find((d) => d.id === id)).filter((d) => d),
      });
    });
  }, [id, componentsById, screensById, panelsById, constants, tenantConstants]);

  const onContextMenu = useCallback(
    ({ event, id, visible }) => {
      if (visible === false) {
        setVisible(false);
        return;
      }
      if (event && (event.pageX || event.pageY)) {
        event?.preventDefault?.();
        const { pageX, pageY } = event;
        setLocation({
          top: pageY,
          left: pageX,
        });
        const { nodes: upNodes, edges: upEdges } = getAssociation({
          id,
          nodeData,
          type: 'up',
        });
        const { nodes: downNodes, edges: downEdges } = getAssociation({
          id,
          nodeData,
          type: 'down',
        });
        setNodeUp(upNodes);
        setNodeDown(downNodes);
        setCurrentNode(id + '');
        setVisible(true);
        setEdges({
          upEdges,
          downEdges,
        });
      }
    },
    [nodeData],
  );

  const onSelectChange = (code) => {
    if (MenuActionState.NODE_UP === code) {
      updateLayerNodeEages({
        nodes: nodeUp,
        edges: edges?.upEdges,
      });
    } else if (MenuActionState.NODE_DOWN === code) {
      updateLayerNodeEages({
        nodes: nodeDown,
        edges: edges?.downEdges,
      });
    } else if (MenuActionState.CANVAS_CHANGE === code) {
      // 子组件 - 普通组件 - 动态面板 - 组 - 动态面板状态
      onRoutingSwitch(nodeCurrents?.[0]);
    }
    updateMenuState(code);
    setVisible(false);
  };

  const onRoutingSwitch = useCallback(
    async (nodeId) => {
      const current = nodeData.groups.concat(nodeData.nodes).find((d) => d.id === nodeId);
      if (current) {
        onClick({
          data: current,
          fromScreen: current.screenId,
        });
      } else {
        message.error('该节点不存在');
      }
    },
    [nodeData, history, id],
  );

  const onClick = ({ data, fromScreen }) => {
    // 新增
    // 当前屏
    const dataId = data.parentId || data.id;
    const rootScreen = id;
    if (dataId) {
      const layerId = resetNodeCardId(dataId);
      selectLayers(layerId);

      sessionStorage.setItem('pre_select_layer', layerId);
    }
    if (fromScreen == rootScreen) {
      history.push(`/create/${rootScreen}`);
    } else if (fromScreen) {
      // 转到其他屏
      const panelId = screensById[fromScreen]?.panelId;
      rootScreen == fromScreen
        ? history.push(`/create/${rootScreen}`)
        : history.push(`/create/${rootScreen}/panel_${panelId}/${fromScreen}`);
    } else {
      message.warning('数据异常，无法路由切换，请手动操作');
    }
  };

  const nodesContextValue = useMemo(() => {
    return {
      canvas,
      eventNodes,
      setCanvas: (canvas) => {
        setCanvas(canvas);
      },
      setEventNode,
      onRoutingSwitch,
      activatedState,
      setActivatedState: setScreenState,
      onContextMenu,
      setCustomEvents,
      changeLayerStatus,
      setCurrentNode,
      clearCurrentNode,
      updateLayerNode,
      updateLayerEdge,
    };
  }, [activatedState, canvas, eventNodes]);

  if (!screen) {
    return null;
  }

  return (
    <ConfigProvider>
      <NodesProvider value={nodesContextValue}>
        <div className={styles.nodesEdit}>
          <Header node={nodeData} screen={screen} editionId={editionId} />
          <div className={styles.box}>
            <Layers layers={layers} id={id} />
            <Content
              node={nodeData}
              editModel={editModel}
              nodeCurrents={nodeCurrents}
              selectNodes={selectNodes}
              selectEdges={selectEdges}
            />

            <TipCard
              nodeCurrents={nodeCurrents}
              node={nodeData}
              screenId={id}
              screenName={screen.name}
            />
          </div>
          <span ref={layerRef}>
            <ContextMenuModal
              nodeDown={nodeDown}
              nodeUp={nodeUp}
              nodeCurrents={nodeCurrents}
              onSelectChange={onSelectChange}
              visible={visible}
              top={location.top}
              left={location.left}
            />
          </span>
        </div>
      </NodesProvider>
    </ConfigProvider>
  );
}

function mapStateToProps({
  createApp,
  node,
  screenEdit,
  screenDetail,
  user,
  screensById,
  tenantConstants,
  constants,
  panelsById,
  componentsById,
}) {
  const { userInfo } = user;
  const {  edition = {} } = userInfo;
  const { id } = edition;
  const { selectNodes, selectEdges, currents } = node;
  return {
    componentsById,
    id: createApp.id,
    screensById,
    panelsById,
    editModel: screenEdit.editModel,
    activatedState: screenDetail.activatedState,
    selectNodes,
    editionId:id,
    selectEdges,
    nodeCurrents: currents,
    constants: constants.list,
    tenantConstants: tenantConstants.list,
  };
}

export default connect(mapStateToProps, {
  setCurrentNode,
  clearCurrentNode,
  updateLayerNode,
  updateLayerEdge,
  updateLayerNodeEages,
  updateMenuState,
  selectLayers,
  eventTracking,
  setScreenState,
  setCustomEvents,
  changeLayerStatus,
})(NodesEdit);
