'use client'

import React, { useCallback, useMemo, useState, useRef } from 'react'
import {
  ReactFlow,
  Node,
  Edge,
  Controls,
  Background,
  MiniMap,
  Panel,
  useReactFlow,
  ReactFlowProvider,
  NodeTypes,
  MarkerType,
  ReactFlowProps,
  useNodesState,
  useEdgesState,
} from '@xyflow/react'
import '@xyflow/react/dist/style.css'
import { Search, Maximize2 } from 'lucide-react'
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from '@/components/ui/input-group'
import { Button } from '@/components/ui/button'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog'
import dagre from 'dagre'
import { useDebouncedCallback } from '@/hooks/use-debounced-callback'
import { useTheme } from 'next-themes'
import { cn } from '@/lib/utils'

// 布局配置类型
export interface LayoutConfig {
  direction?: 'TB' | 'LR'
  ranksep?: number
  nodesep?: number
  nodeWidth?: number
  nodeHeight?: number
}

// 自动布局函数
const getLayoutedElements = (
  nodes: Node[],
  edges: Edge[],
  config: LayoutConfig = {}
) => {
  const {
    direction = 'TB',
    ranksep = 100,
    nodesep = 80,
    nodeWidth = 200,
    nodeHeight = 80,
  } = config

  const dagreGraph = new dagre.graphlib.Graph()
  dagreGraph.setDefaultEdgeLabel(() => ({}))
  dagreGraph.setGraph({ rankdir: direction, ranksep, nodesep })

  nodes.forEach((node) => {
    dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight })
  })

  edges.forEach((edge) => {
    dagreGraph.setEdge(edge.source, edge.target)
  })

  dagre.layout(dagreGraph)

  const layoutedNodes = nodes.map((node) => {
    const nodeWithPosition = dagreGraph.node(node.id)
    return {
      ...node,
      position: {
        x: nodeWithPosition.x - nodeWidth / 2,
        y: nodeWithPosition.y - nodeHeight / 2,
      },
    }
  })

  return { nodes: layoutedNodes, edges }
}

// 通用图组件 Props
export interface AdaptiveGraphProps<TNodeData = any> {
  // 原始节点数据
  nodes: TNodeData[]
  // 原始边数据
  edges: Array<{ source: string; target: string; label?: string }>
  // 节点类型定义
  nodeTypes: NodeTypes
  // 过滤函数：根据搜索查询返回过滤后的节点 ID 集合和高亮节点 ID 集合（可选）
  onFilter?: (nodes: TNodeData[], query: string) => {
    filteredNodeIds: Set<string>
    highlightedNodeIds: Set<string>
  }
  // 节点转换函数：将原始节点数据转换为 ReactFlow 节点
  transformNode: (
    node: TNodeData,
    options: {
      isHighlighted: boolean
      isDimmed: boolean
    }
  ) => Node
  // 边转换函数：将原始边数据转换为 ReactFlow 边（可选）
  transformEdge?: (
    edge: { source: string; target: string; label?: string },
    index: number,
    options: {
      isHighlighted: boolean
    }
  ) => Edge
  // MiniMap 节点颜色函数（可选）
  getNodeColor?: (node: Node) => string
  // 节点点击回调
  onNodeClick?: (node: TNodeData) => void
  // 搜索占位符文本
  searchPlaceholder?: string
  // 统计信息渲染函数（可选）
  renderStats?: (filteredCount: number, totalCount: number, matchedCount: number) => React.ReactNode
  // 布局配置
  layoutConfig?: LayoutConfig
  // 容器样式类名（用于控制高度等样式）
  className?: string
  // ReactFlow 额外属性（可选）
  reactFlowProps?: Partial<ReactFlowProps>
}

/**
 * GraphContent - 图表渲染核心组件
 *
 * 这是一个独立的图表渲染组件，负责实际的 ReactFlow 图表展示和交互。
 * 它必须在 ReactFlowProvider 内部使用，因为它依赖 useReactFlow hook。
 *
 * 职责：
 * - 渲染 ReactFlow 图表（节点、边、控件、小地图等）
 * - 管理节点和边的交互状态（拖拽、选择等）
 * - 处理搜索输入和全屏按钮的 UI
 * - 自动调整视野以适应过滤后的节点
 *
 * 与 AdaptiveGraph 的关系：
 * - AdaptiveGraph 是外层容器组件，负责数据处理、状态管理和布局计算
 * - GraphContent 是内层渲染组件，接收处理好的数据并负责图表的实际展示
 * - AdaptiveGraph 会在两个地方使用 GraphContent：普通视图和全屏对话框
 * - 两者通过 ReactFlowProvider 隔离，确保每个实例有独立的 ReactFlow 上下文
 */
function GraphContent<TNodeData = any>({
  flowNodes,
  flowEdges,
  handleNodeClick,
  nodeTypes,
  getNodeColor,
  onFilter,
  searchPlaceholder,
  inputValue,
  handleInputChange,
  isFullscreen,
  setFullscreenOpen,
  renderStats,
  filteredNodeIds,
  rawNodes,
  highlightedNodeIds,
  reactFlowProps,
  filteredNodeIdsForFitView,
}: {
  flowNodes: Node[]
  flowEdges: Edge[]
  handleNodeClick: (event: React.MouseEvent, node: Node) => void
  nodeTypes: NodeTypes
  getNodeColor?: (node: Node) => string
  onFilter?: any
  searchPlaceholder: string
  inputValue: string
  handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void
  isFullscreen: boolean
  setFullscreenOpen?: (open: boolean) => void
  renderStats?: (filteredCount: number, totalCount: number, matchedCount: number) => React.ReactNode
  filteredNodeIds: Set<string>
  rawNodes: TNodeData[]
  highlightedNodeIds: Set<string>
  reactFlowProps: Partial<ReactFlowProps>
  filteredNodeIdsForFitView: Set<string>
}) {
  const { fitView } = useReactFlow()
  const fitViewTimeoutRef = useRef<number>(0)
  const previousFilteredNodeIdsRef = useRef<Set<string>>(new Set())
  // 使用 useNodesState 和 useEdgesState 来管理节点和边的状态，支持拖拽等交互
  const [nodes, setNodes, onNodesChange] = useNodesState(flowNodes)
  const [edges, setEdges, onEdgesChange] = useEdgesState(flowEdges)
  const { theme, resolvedTheme } = useTheme() // Tailwind CSS的样式变化无法自动传递到ReactFlow内部，需要显式监听网站主题的变化
  const themeIsDark = resolvedTheme === 'dark' || (theme === 'system' && resolvedTheme === 'dark')
  
  // 当 flowNodes 或 flowEdges 变化时，更新内部状态
  React.useEffect(() => {
    setNodes(flowNodes)
  }, [flowNodes, setNodes])
  
  React.useEffect(() => {
    setEdges(flowEdges)
  }, [flowEdges, setEdges])


  // 当过滤节点变化时，自动调整视野
  React.useEffect(() => {
    // 比较 filteredNodeIds 的实际内容是否发生变化
    const previousIds = previousFilteredNodeIdsRef.current
    const currentIds = filteredNodeIdsForFitView
    
    // 检查节点ID集合是否真正发生了变化
    const hasChanged =
      previousIds.size !== currentIds.size ||
      Array.from(currentIds).some(id => !previousIds.has(id))
    
    if (hasChanged) {
      // 更新引用
      previousFilteredNodeIdsRef.current = new Set(currentIds)
      
      // 清除之前的定时器
      if (fitViewTimeoutRef.current) {
        window.clearTimeout(fitViewTimeoutRef.current)
      }

      // 延迟执行 fitView，确保节点已经渲染
      fitViewTimeoutRef.current = window.setTimeout(() => {
        if (flowNodes.length > 0) {
          fitView({
            padding: 0.2,
            duration: 300,
            maxZoom: 1.5,
            minZoom: 0.1,
          })
        }
      }, 100)
    }

    return () => {
      if (fitViewTimeoutRef.current) {
        window.clearTimeout(fitViewTimeoutRef.current)
      }
    }
  }, [filteredNodeIdsForFitView, fitView, flowNodes.length])

  // 默认统计信息渲染
  const defaultRenderStats = (filteredCount: number, totalCount: number, matchedCount: number) => (
    onFilter ?
    <div className="mt-2 text-xs text-muted-foreground">
      显示 {filteredCount} / {totalCount} 个节点
      {matchedCount > 0 && ` (${matchedCount} 个匹配)`}
    </div> : <></>
  )

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onNodeClick={handleNodeClick}
      nodeTypes={nodeTypes}
      fitView
      minZoom={0.1}
      maxZoom={2}
      defaultEdgeOptions={{
        type: 'smoothstep',
        animated: false,
      }}
      proOptions={{ hideAttribution: true }}
      colorMode={themeIsDark ? 'dark' : 'light'}
      {...reactFlowProps}
    >
      <Background />
      <Controls />
      <MiniMap
        nodeColor={getNodeColor || (() => '#cbd5e1')}
        maskColor="rgba(0, 0, 0, 0.1)"
      />
      <Panel position="top-left" className="bg-background/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border">
        <div className="flex items-center gap-2">
          {onFilter && (
            <InputGroup className="min-w-[300px]">
              <InputGroupAddon>
                <InputGroupText>
                  <Search className="h-4 w-4" />
                </InputGroupText>
              </InputGroupAddon>
              <InputGroupInput
                placeholder={searchPlaceholder}
                value={inputValue}
                onChange={handleInputChange}
              />
            </InputGroup>
          )}
          {!isFullscreen && setFullscreenOpen && (
            <Button
              variant="ghost"
              size="sm"
              className="h-9 px-3"
              title="全屏显示"
              onClick={() => setFullscreenOpen(true)}
            >
              <Maximize2 className="h-3.5 w-3.5 mr-1.5" />
            </Button>
          )}
        </div>
        {(renderStats || defaultRenderStats)(filteredNodeIds.size, rawNodes.length, highlightedNodeIds.size)}
      </Panel>
    </ReactFlow>
  )
}

/**
 * AdaptiveGraph - 自适应图表容器组件
 * 
 * 这是一个高度可配置的图表容器组件，提供完整的图表展示解决方案。
 * 它处理数据转换、过滤、布局计算和状态管理，然后将处理好的数据传递给 GraphContent 进行渲染。
 * 
 * 主要功能：
 * - 接收原始节点和边数据，通过 transformNode/transformEdge 转换为 ReactFlow 格式
 * - 支持自定义过滤逻辑（搜索、高亮）
 * - 使用 dagre 算法自动计算节点布局
 * - 提供普通视图和全屏对话框两种展示模式
 * - 管理搜索状态和防抖处理
 * ```
 */
export function AdaptiveGraph<TNodeData = any>({
  nodes: rawNodes,
  edges: rawEdges,
  nodeTypes,
  onFilter,
  transformNode,
  transformEdge,
  getNodeColor,
  onNodeClick,
  searchPlaceholder = '搜索...',
  renderStats,
  layoutConfig,
  className = 'h-[800px] max-h-[calc(100vh-12rem)]',
  reactFlowProps = {},
}: AdaptiveGraphProps<TNodeData>) {
  const [searchQuery, setSearchQuery] = useState('')
  const [inputValue, setInputValue] = useState('')
  const [fullscreenOpen, setFullscreenOpen] = useState(false)

  // 使用外部提供的过滤函数，如果未提供则显示所有节点
  const { filteredNodeIds, highlightedNodeIds } = useMemo(() => {
    if (!onFilter) {
      // 未提供过滤函数时，显示所有节点且不高亮
      const allNodeIds = new Set(
        rawNodes.map((node) => {
          const transformed = transformNode(node, { isHighlighted: false, isDimmed: false })
          return transformed.id
        })
      )
      return {
        filteredNodeIds: allNodeIds,
        highlightedNodeIds: new Set<string>(),
      }
    }
    return onFilter(rawNodes, searchQuery)
  }, [rawNodes, searchQuery, onFilter, transformNode])

  // 转换为 ReactFlow 节点
  const nodes = useMemo(() => {
    return rawNodes
      .filter((node) => {
        const nodeData = transformNode(node, { isHighlighted: false, isDimmed: false })
        return filteredNodeIds.has(nodeData.id)
      })
      .map((node) => {
        const nodeData = transformNode(node, { isHighlighted: false, isDimmed: false })
        return transformNode(node, {
          isHighlighted: highlightedNodeIds.has(nodeData.id),
          isDimmed: searchQuery.trim() !== '' && !highlightedNodeIds.has(nodeData.id),
        })
      })
  }, [rawNodes, filteredNodeIds, highlightedNodeIds, searchQuery, transformNode])

  // 转换为 ReactFlow 边
  const edges = useMemo(() => {
    const defaultTransform = (
      edge: { source: string; target: string; label?: string },
      index: number,
      options: { isHighlighted: boolean }
    ): Edge => ({
      id: `${edge.source}-${edge.target}-${index}`,
      source: edge.source,
      target: edge.target,
      type: 'smoothstep',
      animated: options.isHighlighted,
      style: {
        stroke: options.isHighlighted ? '#3b82f6' : '#94a3b8',
        strokeWidth: options.isHighlighted ? 2 : 1,
      },
      markerEnd: {
        type: MarkerType.ArrowClosed,
        color: options.isHighlighted ? '#3b82f6' : '#94a3b8',
      },
    })

    const transform = transformEdge || defaultTransform

    return rawEdges
      .filter((edge) => filteredNodeIds.has(edge.source) && filteredNodeIds.has(edge.target))
      .map((edge, index) =>
        transform(edge, index, {
          isHighlighted: highlightedNodeIds.has(edge.source) || highlightedNodeIds.has(edge.target),
        })
      )
  }, [rawEdges, filteredNodeIds, highlightedNodeIds, transformEdge])

  // 应用自动布局
  const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => {
    return getLayoutedElements(nodes, edges, layoutConfig)
  }, [nodes, edges, layoutConfig])

  // 防抖更新搜索查询
  const debouncedSetSearchQuery = useDebouncedCallback((value: string) => {
    setSearchQuery(value)
  }, 300)

  // 处理输入变化
  const handleInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const value = e.target.value
      setInputValue(value)
      debouncedSetSearchQuery(value)
    },
    [debouncedSetSearchQuery]
  )

  // 处理节点点击
  const handleNodeClick = useCallback(
    (_event: React.MouseEvent, node: Node) => {
      const nodeData = rawNodes.find((n) => {
        const transformed = transformNode(n, { isHighlighted: false, isDimmed: false })
        return transformed.id === node.id
      })
      if (nodeData && onNodeClick) {
        onNodeClick(nodeData)
      }
    },
    [rawNodes, transformNode, onNodeClick]
  )

  // 共享的 GraphContent props
  const graphContentProps = {
    flowNodes: layoutedNodes,
    flowEdges: layoutedEdges,
    handleNodeClick,
    nodeTypes,
    getNodeColor,
    onFilter,
    searchPlaceholder,
    inputValue,
    handleInputChange,
    isFullscreen: false,
    setFullscreenOpen,
    renderStats,
    filteredNodeIds,
    rawNodes,
    highlightedNodeIds,
    reactFlowProps,
    filteredNodeIdsForFitView: filteredNodeIds,
  }

  return (
    <>
      <div className={cn('w-full border rounded-lg bg-background', className)}>
        <ReactFlowProvider>
          <GraphContent {...graphContentProps} />
        </ReactFlowProvider>
      </div>
      {/* 全屏对话框 */}
      <Dialog open={fullscreenOpen} onOpenChange={setFullscreenOpen}>
        <DialogContent className="p-0" variant="fullscreen">
          <DialogHeader className="pt-5 pb-3 m-0 border-b border-border">
            <DialogTitle className="px-6 text-base">图表查看</DialogTitle>
            <DialogDescription className="sr-only">全屏查看图表</DialogDescription>
          </DialogHeader>
          <div className="h-full my-3 px-6">
            <ReactFlowProvider>
              <GraphContent {...graphContentProps} isFullscreen={true} setFullscreenOpen={undefined} />
            </ReactFlowProvider>
          </div>
          <DialogFooter className="px-6 py-4 border-t border-border">
            <DialogClose asChild>
              <Button type="button">关闭</Button>
            </DialogClose>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  )
}
