import React from 'react';
import type { FC } from 'react';
import { forwardRef, useImperativeHandle } from 'react';
import { useMemo, useCallback } from 'react';
import { useRef, useEffect } from 'react';
import type { Graph, G6GraphEvent, GraphOptions } from '@antv/g6';
import G6 from '@antv/g6';
import type { FGDProps, GuidData, FGDComponentProps } from './typeDefinition';
import { mergeConfig } from '../utils';
import { useAutoHeight } from '../hooks';

const useGraphCfg = (graphCfg?: GraphOptions) => {
  const newGraphCfg = useMemo(() => {
    let defaultConfig = {
      container: '',
      modes: {
        default: ['drag-canvas', 'zoom-canvas'],
      },
      layout: {
        type: 'force',
        linkDistance: (item: any) => {
          if (item.linkHeight) {
            return item.linkHeight;
          }
          return 100;
        },
        // nodeStrength: 0,
        // workerEnabled: true,
        // gpuEnabled:true
      },
      defaultNode: {
        shape: 'node',
        labelCfg: {
          position: 'top',
          style: {
            fill: '#fff',
            fontSize: 10,
            cursor: 'pointer',
          },
        },
        style: {
          stroke: '#72CC4A',
          width: 150,
          cursor: 'pointer',
        },
      },
      nodeStateStyles: {
        selected: {
          fill: 'steelblue',
          stroke: '#000',
          lineWidth: 1,
        },
        hover: {
          fill: 'red',
          stroke: '#000',
          lineWidth: 1,
        },
      },
      defaultEdge: {
        labelCfg: {
          style: {
            fill: '#000',
            fontSize: 10,
          },
          autoRotate: true,
        },
        style: {
          stroke: '#000',
          lineWidth: 1,
          endArrow: {
            path: G6.Arrow.triangle(3, 6, 3),
            d: 3,
          },
        },
      },
    };
    return mergeConfig(graphCfg, defaultConfig);
  }, [graphCfg]);

  return [newGraphCfg];
};

const FGD = forwardRef<FGDComponentProps, FC<FGDProps>>(
  (props: FGDProps, fgRef) => {
    const {
      graphData,
      onNodeClick,
      onNodeDblClick,
      graphCfg,
      style,
      request,
      params,
      postData,
    } = props;
    const ref = useRef<HTMLDivElement>(null);
    const graph = useRef<Graph>();
    const [newGraphCfg] = useGraphCfg(graphCfg);
    const [height] = useAutoHeight(ref);

    // 添加事件
    const addEvent = useCallback(
      (example: Graph, data?: GuidData): void => {
        if (data && data?.nodes.length <= 200) {
          console.log('绑定了=====》');
          const refreshDragedNodePosition = (e: any): void => {
            const model = e.item.get('model');
            model.fx = e.x;
            model.fy = e.y;
          };

          example.on('node:dragstart', (e: G6GraphEvent) => {
            example.layout();
            refreshDragedNodePosition(e);
          });
          example.on('node:drag', (e: G6GraphEvent) => {
            refreshDragedNodePosition(e);
          });
          example.on('node:dragend', (e: G6GraphEvent) => {
            e.item.get('model').fx = null;
            e.item.get('model').fy = null;
          });
        }

        let timeId: number;
        if (onNodeClick) {
          example.on('node:click', (e: G6GraphEvent) => {
            clearTimeout(timeId);
            timeId = window.setTimeout(() => {
              console.log('click', e);
              onNodeClick(e, example);
            }, 300);
          });
        }
        if (onNodeDblClick) {
          example.on('node:dblclick', (e: G6GraphEvent) => {
            clearTimeout(timeId);
            console.log('dblclick');
            onNodeDblClick(e, example);
          });
        }
      },
      [onNodeClick, onNodeDblClick],
    );

    const GraphData = useMemo(async () => {
      if (!request) return graphData;
      const { data } = await request(params, graph.current);
      if (postData) return postData(data);

      return data;
    }, [graphData, params, postData, request]);

    useEffect(() => {
      if (!graph.current) {
        GraphData.then((res) => {
          let modes = JSON.parse(JSON.stringify(newGraphCfg.modes));
          if (res && res?.nodes.length > 200) modes.default.push('drag-node');

          graph.current = new G6.Graph({
            ...newGraphCfg,
            container: ref.current,
            modes,
          });

          graph.current.data(res);
          graph.current.get('canvas').set('localRefresh', false);
          graph.current.render();
          addEvent(graph.current, res);
        });

        if (typeof window !== 'undefined')
          window.onresize = () => {
            if (!graph.current || graph.current.get('destroyed')) return;
            if (
              !ref ||
              !ref?.current?.scrollWidth ||
              !ref?.current?.scrollHeight
            )
              return;
            graph.current.changeSize(
              ref?.current?.scrollWidth,
              ref?.current?.scrollHeight,
            );
          };
      } else {
        GraphData.then((res) => {
          graph?.current?.changeData(res);
        });
      }
    }, [newGraphCfg, GraphData, addEvent]);

    useImperativeHandle(
      fgRef,
      () => ({ getInstance: () => graph?.current }),
      [],
    );

    return <div style={{ height, ...style }} className="graph-box" ref={ref} />;
  },
);

export default FGD;
