/**
 * useG6Graph — G6 图谱初始化 / 渲染 / 销毁 composable
 *
 * 将 GraphExplorer 与 DocDetailView 中重复的 G6 逻辑抽取为可复用组合式函数。
 * 内部延迟加载 @antv/g6 以减小首屏体积。
 *
 * 支持 WebGL 渲染、Web Worker 布局计算、LOD 优化（可选启用）。
 */
import { shallowRef } from 'vue'
import { getNodeColor, getRelTypeZh } from '@/utils/graphLabels'
import type { GraphNode, GraphEdge, G6NodeData, G6EdgeData } from '@/types/graph'

// ---------------------------------------------------------------------------
// 公共数据转换函数
// ---------------------------------------------------------------------------

/** 获取节点主标签（第一个 Neo4j label） */
function getPrimaryLabel(node: GraphNode): string {
  return (node.labels || [])[0] || 'Unknown'
}

/** 获取节点显示文本 */
function getNodeDisplayLabel(node: GraphNode): string {
  const p = node.properties || {}
  return p.name || p.title || p.doc_number || node.id.slice(0, 12)
}

/**
 * 将原始 GraphNode 数组转换为 G6 渲染用节点数据。
 * 可选传入 visibleTypes 以过滤不需要展示的节点类型。
 */
export function transformNodesForG6(
  nodes: GraphNode[],
  visibleTypes?: string[],
): G6NodeData[] {
  const filtered = visibleTypes
    ? nodes.filter((n) => visibleTypes.includes(getPrimaryLabel(n)))
    : nodes
  return filtered.map((n) => ({
    ...n,
    _type: getPrimaryLabel(n),
    _label: getNodeDisplayLabel(n),
  }))
}

/**
 * 将原始 GraphEdge 数组转换为 G6 渲染用边数据。
 * 自动过滤掉 source/target 不在 nodeIds 中的无效边。
 */
export function transformEdgesForG6(
  edges: GraphEdge[],
  nodeIds: Set<string>,
): G6EdgeData[] {
  return edges
    .filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target))
    .map((e, i) => ({
      id: `e_${i}`,
      source: e.source,
      target: e.target,
      relType: e.type, // G6 v5 保留 'type' 字段，改用 relType
    }))
}

// ---------------------------------------------------------------------------
// 默认样式配置
// ---------------------------------------------------------------------------

/**
 * 当前渲染的节点总数（用于自适应节点大小）。
 * 由 renderGraph / addGraphData 在渲染前更新。
 */
let _currentNodeCount = 0

/** 根据节点总数自适应元素大小 */
function _adaptiveSize(isDoc: boolean): number {
  if (_currentNodeCount <= 30) return isDoc ? 36 : 28
  if (_currentNodeCount <= 80) return isDoc ? 30 : 22
  return isDoc ? 24 : 18
}
function _adaptiveFontSize(): number {
  if (_currentNodeCount <= 30) return 12
  if (_currentNodeCount <= 80) return 11
  return 10
}

/** 默认节点样式回调 */
function defaultNodeStyle(d: G6NodeData) {
  const isDoc = d._type === 'Document'
  const style: Record<string, unknown> = {
    fill: getNodeColor(d._type || ''),
    stroke: '#fff',
    lineWidth: 2,
    size: _adaptiveSize(isDoc),
    labelText: d._label || '',
    labelFill: '#333',
    labelFontSize: _adaptiveFontSize(),
    labelPlacement: 'bottom' as const,
    labelMaxWidth: _currentNodeCount <= 50 ? 120 : 100,
  }
  // 后端预计算坐标：直接注入到 style 中
  if (typeof d.x === 'number' && typeof d.y === 'number') {
    style.x = d.x
    style.y = d.y
  }
  return style
}

/** 默认边样式回调 */
function defaultEdgeStyle(d: G6EdgeData) {
  return {
    stroke: '#adb5bd',
    lineWidth: 1.5,
    endArrow: true,
    labelFill: '#666',
    labelFontSize: 10,
    labelText: getRelTypeZh(d.relType || ''),
  }
}

// ---------------------------------------------------------------------------
// Composable 主体
// ---------------------------------------------------------------------------

/** G6 插件配置（tooltip 等） */
export interface G6PluginConfig {
  type: string
  getContent?: (event: unknown, items: G6NodeData[]) => string
  [key: string]: unknown
}

/** 布局类型 */
export type G6LayoutType = 'preset' | 'dagre' | 'force' | 'd3-force'

export interface UseG6GraphOptions {
  /** 布局类型：'preset'（后端预计算坐标）| 'dagre' | 'force' | 'd3-force' */
  layoutType?: G6LayoutType
  /** 覆盖布局参数 */
  layoutOverrides?: Record<string, unknown>
  /** G6 插件列表（如 tooltip） */
  plugins?: G6PluginConfig[]
  /** 节点点击回调 */
  onNodeClick?: (nodeData: GraphNode) => void
  /** 渲染模式：'canvas'（默认）或 'webgl'（大图推荐） */
  rendererMode?: 'canvas' | 'webgl'
  /** 是否启用 Web Worker 计算布局（大图推荐） */
  enableWorkerLayout?: boolean
  /** 是否启用 LOD 优化（缩放时自动隐藏标签） */
  enableLOD?: boolean
}

/**
 * G6 图谱 composable。
 * 提供 initGraph / renderGraph / addGraphData / fitView / destroy 核心方法，
 * 以及 graphInstance 引用供外部访问底层 Graph 实例。
 */
/** G6 Graph 实例的最小接口（G6 v5 官方类型不完整，使用自定义接口桥接） */
interface G6GraphInstance {
  setData(data: { nodes: G6NodeData[]; edges: G6EdgeData[] }): Promise<void>
  addData(data: { nodes?: G6NodeData[]; edges?: G6EdgeData[] }): Promise<void>
  render(): Promise<void>
  fitView(): Promise<void>
  resize(width: number, height: number): void
  destroy(): void
  on(event: string, handler: (e: G6NodeClickEvent) => void): void
  getData(): { nodes: G6NodeData[]; edges: G6EdgeData[] }
}

/** G6 节点点击事件（v5 结构） */
interface G6NodeClickEvent {
  target: { id?: string };
  targetType: string;
  itemData?: GraphNode;
}

export function useG6Graph(options?: UseG6GraphOptions) {
  const graphInstance = shallowRef<G6GraphInstance | null>(null)
  const rendererMode = options?.rendererMode ?? 'canvas'
  const enableWorkerLayout = options?.enableWorkerLayout ?? true
  const enableLOD = options?.enableLOD ?? false
  const layoutType = options?.layoutType ?? 'preset'

  /** 根据 layoutType 构建布局配置 */
  function buildLayoutConfig(type: G6LayoutType): Record<string, unknown> {
    switch (type) {
      case 'preset':
        // 后端已预计算 x/y 坐标，直接放置，零计算
        return { type: 'preset', ...options?.layoutOverrides }
      case 'dagre':
        return {
          type: 'antv-dagre',
          rankdir: 'TB',
          nodesep: 40,
          ranksep: 60,
          workerEnabled: enableWorkerLayout,
          ...options?.layoutOverrides,
        }
      case 'force':
        return {
          type: 'd3-force',
          manyBody: { strength: -300 },
          link: { distance: 150 },
          collide: { radius: 35 },
          alphaDecay: 0.08,
          alphaMin: 0.05,
          ...options?.layoutOverrides,
        }
      case 'd3-force':
      default:
        return {
          type: 'd3-force',
          manyBody: { strength: -300 },
          link: { distance: 150 },
          collide: { radius: 35 },
          ...options?.layoutOverrides,
        }
    }
  }

  /**
   * 初始化 G6 Graph 实例。
   * 延迟动态导入 @antv/g6 以减小首屏包体积。
   */
  async function initGraph(
    container: HTMLElement,
    width: number,
    height: number,
  ) {
    const { Graph } = await import('@antv/g6')

    const layoutConfig = buildLayoutConfig(layoutType)

    // 行为列表
    const behaviors: (string | Record<string, unknown>)[] = [
      'zoom-canvas', 'drag-canvas', 'drag-element', 'click-select',
    ]

    // LOD 优化：缩放/平移时隐藏边标签和密集标签
    if (enableLOD) {
      behaviors.push('optimize-viewport-transform')
    }

    const graphConfig: Record<string, unknown> = {
      container,
      width,
      height: height || 480,
      autoFit: 'view',
      layout: layoutConfig,
      node: {
        style: (d: G6NodeData) => defaultNodeStyle(d),
      },
      edge: {
        style: (d: G6EdgeData) => defaultEdgeStyle(d),
      },
      behaviors,
    }

    // WebGL 渲染器：GPU 加速，大图性能提升 5-10 倍
    if (rendererMode === 'webgl') {
      try {
        const { Renderer } = await import('@antv/g-webgl')
        graphConfig.renderer = () => new Renderer()
      } catch {
        // WebGL 不可用，降级到默认 Canvas 渲染
        console.warn('[useG6Graph] WebGL renderer not available, falling back to Canvas')
      }
    }

    // 插件（可选）
    if (options?.plugins?.length) {
      graphConfig.plugins = options.plugins
    }

    graphInstance.value = new Graph(graphConfig) as unknown as G6GraphInstance

    // 节点点击事件（可选）
    if (options?.onNodeClick) {
      graphInstance.value.on('node:click', (e: G6NodeClickEvent) => {
        // G6 v5: itemData may be undefined; fall back to getNodeData(id)
        let nodeData: GraphNode | undefined = e.itemData
        if (!nodeData && e.target?.id && graphInstance.value) {
          const raw = (graphInstance.value as any).getNodeData(e.target.id)
          if (raw) nodeData = raw as GraphNode
        }
        if (nodeData) {
          options.onNodeClick!(nodeData)
        }
      })
    }
  }

  /**
   * 渲染图谱：转换数据 → setData → render → fitView。
   * visibleTypes 可选，传入时只渲染指定类型的节点。
   */
  async function renderGraph(
    rawNodes: GraphNode[],
    rawEdges: GraphEdge[],
    visibleTypes?: string[],
  ) {
    if (!graphInstance.value) return
    const nodes = transformNodesForG6(rawNodes, visibleTypes)
    _currentNodeCount = nodes.length
    const nodeIds = new Set(nodes.map((n) => n.id))
    const edges = transformEdgesForG6(rawEdges, nodeIds)

    await graphInstance.value.setData({ nodes, edges })
    await graphInstance.value.render()
    await graphInstance.value.fitView()
  }

  /**
   * 增量添加节点和边到图谱，不重置视口。
   * 用于渐进式加载 / 展开邻居等场景。
   */
  async function addGraphData(
    rawNodes: GraphNode[],
    rawEdges: GraphEdge[],
    visibleTypes?: string[],
  ) {
    if (!graphInstance.value) return

    // 获取当前已有节点 ID，过滤重复
    const currentData = graphInstance.value.getData()
    const existingNodeIds = new Set(currentData.nodes.map((n: G6NodeData) => n.id))

    const newNodes = transformNodesForG6(rawNodes, visibleTypes)
      .filter((n) => !existingNodeIds.has(n.id))

    if (!newNodes.length) return

    _currentNodeCount = existingNodeIds.size + newNodes.length
    // 扩展 nodeIds 以包含现有 + 新增
    const allNodeIds = new Set([...existingNodeIds, ...newNodes.map((n) => n.id)])
    const newEdges = transformEdgesForG6(rawEdges, allNodeIds)
      .filter((e) => {
        const existingEdgeIds = new Set(currentData.edges.map((ed: G6EdgeData) => ed.id))
        return !existingEdgeIds.has(e.id)
      })

    await graphInstance.value.addData({ nodes: newNodes, edges: newEdges })
    await graphInstance.value.render()
    // 不调用 fitView，保持用户当前视口
  }

  /** 适应画布视图 */
  function fitView() {
    graphInstance.value?.fitView()
  }

  /** 销毁 G6 实例，释放资源 */
  function destroy() {
    graphInstance.value?.destroy()
    graphInstance.value = null
  }

  return {
    graphInstance,
    initGraph,
    renderGraph,
    addGraphData,
    fitView,
    destroy,
  }
}
