/*
 * @Author: 赵伟
 * @Date: 2024-06-07 11:24:10
 * @Description: 模型演化
 */

import { useEffectWhen } from '@/hooks';
import { ResourceVersionData } from '@/pages/Dataset/config';
import { getModelAtlasReq } from '@/services/dataset/index.js';
import themes from '@/styles/theme.less';
import { to } from '@/utils/promise';
import G6, { G6GraphEvent, Graph, INode } from '@antv/g6';
// @ts-ignore
import { Flex, Select } from 'antd';
import { useEffect, useRef, useState } from 'react';
import GraphLegend from '../GraphLegend';
import NodeTooltips from '../NodeTooltips';
import styles from './index.less';
import type { ModelDepsData, ProjectDependency, TrainDataset } from './utils';
import {
  NodeType,
  getGraphData,
  nodeFontSize,
  nodeHeight,
  nodeWidth,
  normalizeTreeData,
  traverseHierarchically,
} from './utils';

type modeModelEvolutionProps = {
  resourceId: number;
  versionList: ResourceVersionData[];
  version?: string;
  isActive: boolean;
  onVersionChange: (version: string) => void;
};

let graph: Graph;
function ModelEvolution({
  resourceId,
  versionList,
  version,
  isActive,
  onVersionChange,
}: modeModelEvolutionProps) {
  const graphRef = useRef<HTMLDivElement>(null);
  const [showNodeTooltip, setShowNodeTooltip] = useState(false);
  const [enterTooltip, setEnterTooltip] = useState(false);
  const [nodeTooltipX, setNodeToolTipX] = useState(0);
  const [nodeTooltipY, setNodeToolTipY] = useState(0);
  const [hoverNodeData, setHoverNodeData] = useState<
    ModelDepsData | ProjectDependency | TrainDataset | undefined
  >(undefined);
  const apiData = useRef<ModelDepsData | undefined>(undefined); // 接口返回的树形结构
  const hierarchyNodes = useRef<ModelDepsData[]>([]); // 层级迭代树形结构，得到的节点列表

  useEffect(() => {
    initGraph();
    const changeSize = () => {
      if (!graph || graph.get('destroyed')) return;
      if (!graphRef.current) return;
      graph.changeSize(graphRef.current.clientWidth, graphRef.current.clientHeight);
      graph.fitView();
    };

    window.addEventListener('resize', changeSize);
    return () => {
      window.removeEventListener('resize', changeSize);
    };
  }, []);

  useEffectWhen(
    () => {
      if (version) {
        getModelAtlas();
      } else {
        clearGraphData();
      }
    },
    [resourceId, version],
    isActive,
  );

  // 初始化图
  const initGraph = () => {
    graph = new G6.Graph({
      container: graphRef.current!,
      width: graphRef.current!.clientWidth,
      height: graphRef.current!.clientHeight,
      fitView: true,
      fitViewPadding: 100,
      minZoom: 0.5,
      maxZoom: 5,
      defaultNode: {
        type: 'rect',
        size: [nodeWidth, nodeHeight],
        anchorPoints: [
          [0, 0.5],
          [1, 0.5],
          [0.5, 0],
          [0.5, 1],
        ],
        style: {
          fill: themes['primaryColor'],
          lineWidth: 0,
          radius: 6,
          cursor: 'pointer',
        },
        labelCfg: {
          position: 'center',
          style: {
            fill: '#ffffff',
            fontSize: nodeFontSize,
            textAlign: 'center',
            cursor: 'pointer',
          },
        },
      },
      defaultEdge: {
        type: 'cubic-horizontal',
        labelCfg: {
          autoRotate: true,
        },
        style: {
          stroke: '#DEE0E5',
          lineWidth: 1,
        },
      },
      modes: {
        default: ['drag-canvas', 'zoom-canvas'],
      },
    });

    bindEvents();
  };

  // 绑定事件
  const bindEvents = () => {
    graph.on('node:mouseenter', (e: G6GraphEvent) => {
      const nodeItem = e.item;
      graph.setItemState(nodeItem, 'hover', true);

      const model = nodeItem.getModel() as ModelDepsData;
      const { x, y } = model;
      const point = graph.getCanvasByPoint(x!, y!);
      const zoom = graph.getZoom();
      // 更加缩放，调整 tooltip 位置
      const offsetX = (nodeWidth * zoom) / 4;
      const offsetY = (nodeHeight * zoom) / 4;

      const canvasWidth = graphRef.current!.clientWidth;
      if (point.x + 300 > canvasWidth) {
        point.x = canvasWidth - 300;
      }

      setHoverNodeData(model);
      setNodeToolTipX(point.x + offsetX);
      setNodeToolTipY(graphRef.current!.clientHeight - point.y + offsetY);
      setShowNodeTooltip(true);
    });

    graph.on('node:mouseleave', (e: G6GraphEvent) => {
      const nodeItem = e.item;
      graph.setItemState(nodeItem, 'hover', false);
      setShowNodeTooltip(false);
    });

    graph.on('node:click', (e: G6GraphEvent) => {
      const nodeItem = e.item as INode;
      const model = nodeItem.getModel() as ModelDepsData | ProjectDependency | TrainDataset;
      const { model_type } = model;
      if (
        model_type === NodeType.Project ||
        model_type === NodeType.TrainDataset ||
        model_type === NodeType.TestDataset ||
        !apiData.current ||
        !hierarchyNodes.current
      ) {
        return;
      }

      setShowNodeTooltip(false);
      setEnterTooltip(false);
      toggleExpended(model.id);
      const graphData = getGraphData(apiData.current, hierarchyNodes.current);
      graph.data(graphData);
      graph.render();
      graph.fitView();
    });

    // 鼠标滚轮缩放时，隐藏 tooltip
    graph.on('wheelzoom', () => {
      setShowNodeTooltip(false);
      setEnterTooltip(false);
    });
  };

  // toggle 展开
  const toggleExpended = (id: string) => {
    const nodes = hierarchyNodes.current;
    for (const node of nodes) {
      if (node.id === id) {
        node.expanded = !node.expanded;
        break;
      }
    }
  };

  const handleTooltipsMouseEnter = () => {
    setEnterTooltip(true);
  };

  const handleTooltipsMouseLeave = () => {
    setEnterTooltip(false);
  };

  // 获取模型依赖
  const getModelAtlas = async () => {
    const params = {
      current_model_id: resourceId,
      version,
    };
    const [res] = await to(getModelAtlasReq(params));
    if (res && res.data) {
      const data = normalizeTreeData(res.data);
      apiData.current = data;
      hierarchyNodes.current = traverseHierarchically(data);
      const graphData = getGraphData(data, hierarchyNodes.current);

      graph.data(graphData);
      graph.render();
      graph.fitView();
      setShowNodeTooltip(false);
      setEnterTooltip(false);
    } else {
      clearGraphData();
    }
  };

  // 请求失败或者版本不存在时，清除图形
  function clearGraphData() {
    graph.data({
      nodes: [],
      edges: [],
    });
    graph.render();
    graph.fitView();
  }

  return (
    <div className={styles['model-evolution']}>
      <Flex align="center" className={styles['model-evolution__top']}>
        <span style={{ marginRight: '10px' }}>版本号：</span>
        <Select
          placeholder="请选择版本号"
          style={{ width: '160px', marginRight: '20px' }}
          value={version}
          allowClear
          onChange={onVersionChange}
          options={versionList}
        />
        <GraphLegend style={{ marginRight: 0, marginLeft: 'auto' }}></GraphLegend>
      </Flex>
      <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div>
      {(showNodeTooltip || enterTooltip) && (
        <NodeTooltips
          resourceId={resourceId}
          x={nodeTooltipX}
          y={nodeTooltipY}
          data={hoverNodeData!}
          onVersionChange={onVersionChange}
          onMouseEnter={handleTooltipsMouseEnter}
          onMouseLeave={handleTooltipsMouseLeave}
        />
      )}
    </div>
  );
}

export default ModelEvolution;
