import { type CallbackArgs, type EventArgs, type EventCallback, LogicFlow } from '@logicflow/core';
import { register as __register } from '@logicflow/vue-node-registry';
import type { Dagre } from '@logicflow/layout';

import '@logicflow/core/lib/style/index.css';
import '@logicflow/extension/es/index.css';
import '@logicflow/extension/lib/style/index.css';

import VLogicFlow from './VLogicFlow.vue';
import { useEventListener } from './use';
import type { LogicFlowInstance } from './types';

export type * from './types';
export { VLogicFlow };

export type GraphData = LogicFlow.GraphConfigData;

const Threshold = 100;

export default function init(
  lf: LogicFlowInstance,
  config?: {
    register: { category: 'node' | 'edge'; type: string; view: any; model: any }[];
    graph?: GraphData;
  }
) {
  const register = (type: string, component: Component, model?: any) => __register({ type, component, model }, lf);
  const listener = <T extends keyof EventArgs>(event: T, callback: EventCallback<T>, predicate?: (event: CallbackArgs<T>) => boolean, once?: boolean) =>
    useEventListener(lf.graphModel.eventCenter, event, callback, predicate, once);
  const getDagre = () => lf.extension.dagre as Dagre;

  for (const { category, type, view, model } of config?.register ?? []) {
    if (category === 'node') register(type, view, model);
  }
  lf.renderRawData(config?.graph ?? {});

  let delayClick: ReturnType<typeof setTimeout>;
  listener('edge:dbclick', (event) => {
    lf.deleteEdge(event.data.id);
  });
  listener('node:dbclick', (event) => {
    clearTimeout(delayClick);
    lf.deleteNode(event.data.id);
  });
  listener('node:click', (event) => {
    delayClick = setTimeout(() => {
      lf.graphModel.eventCenter.emit('node:click:300', event);
    }, 300);
  });

  let insertNodeInPolylineTime = 0;
  let delayInsertNodeInPolyline: ReturnType<typeof setTimeout>;
  let cacheEdgePoint: string[] | void = void 0
  listener('edge:delete', () => {
    insertNodeInPolylineTime = Date.now();
    cacheEdgePoint = [];
    setTimeout(() => { cacheEdgePoint = void 0; }, Threshold * 2)
  });
  listener('edge:add', (event) => {
    clearTimeout(delayInsertNodeInPolyline);
    if (Array.isArray(cacheEdgePoint)) cacheEdgePoint.push(event.data.sourceNodeId, event.data.targetNodeId);
    if (Date.now() - insertNodeInPolylineTime < Threshold)
      delayInsertNodeInPolyline = setTimeout(() => {
        if (Array.isArray(cacheEdgePoint) && cacheEdgePoint.length === 4 && cacheEdgePoint[1] === cacheEdgePoint[2]) {
          const id = cacheEdgePoint[1];
          lf.graphModel.eventCenter.emit('node:insert', { data: lf.getNodeModelById(id)?.getData() });
        }
      }, Threshold);
  });
  /**
   * 拖拽节点插入边报错
   */
  let notConnectionId: string | void;
  listener('connection:not-allowed', (event) => {
    notConnectionId = event.data.id;
    setTimeout(() => { notConnectionId = void 0 }, Threshold);
    clearTimeout(delayInsertNodeInPolyline);
  });
  listener('node:dnd-add', (event) => {
    if (event.data.id === notConnectionId) setTimeout(() => lf.graphModel.eventCenter.emit('node:delete', event as any), Threshold)
  });

  /**
   * 增强功能
   * @description 渐进连线成功触发 [edge:proximity-connect] 事件
   */
  let dropAddEdgeTime = 0;
  listener('node:drop', () => {
    if (Date.now() - dropAddEdgeTime < 100) lf.graphModel.eventCenter.emit('edge:proximity-connect', {});
  });
  listener('edge:add', () => {
    dropAddEdgeTime = Date.now();
  });

  return { lf, register, getDagre, listener };
}

export type VLogicFlowInstance = ReturnType<typeof init>;
