/**
 * Interaction System - 交互系统
 * 
 * 职责：
 * - 处理鼠标/触摸事件
 * - 节点拖拽
 * - 画布平移/缩放
 * - 框选
 * - 点击/双击
 * 
 * 设计原则：
 * - 与渲染层解耦
 * - 事件驱动
 * - 可扩展
 */

import type { Point, Rect, Viewport, InteractionEvent, InteractionEventType } from '../core/types'
import type { CanvasRenderer } from '../renderer/CanvasRenderer'
import type { IGraphModel } from '../core/GraphModel'

/** 交互状态 */
interface InteractionState {
  isDragging: boolean
  isPanning: boolean
  isSelecting: boolean
  draggedNodeId: string | null
  dragStartPos: Point
  lastMousePos: Point
  selectionBox: Rect | null
  // 多选拖动：记录起始位置用于计算增量
  dragStartPositions: Map<string, Point>
}

/**
 * 交互管理器
 */
export class InteractionManager {
  private canvas: HTMLCanvasElement
  private renderer: CanvasRenderer
  private graph: IGraphModel
  
  private state: InteractionState = {
    isDragging: false,
    isPanning: false,
    isSelecting: false,
    draggedNodeId: null,
    dragStartPos: { x: 0, y: 0 },
    lastMousePos: { x: 0, y: 0 },
    selectionBox: null,
    dragStartPositions: new Map()
  }

  // 事件监听器
  private listeners: Map<InteractionEventType, Set<(e: InteractionEvent) => void>> = new Map()
  
  // 选择状态
  private selectedIds: Set<string> = new Set()
  
  // 配置
  private config = {
    zoomSpeed: 0.1,
    minZoom: 0.1,
    maxZoom: 3,
    dragThreshold: 5,    // 拖拽阈值（像素）
    doubleClickDelay: 300
  }

  // 双击检测
  private lastClickTime = 0
  private lastClickNodeId: string | null = null
  
  // 描述图标悬浮状态缓存（优化性能，避免频繁触发事件）
  private currentDescHoverNodeId: string | null = null

  constructor(
    canvas: HTMLCanvasElement,
    renderer: CanvasRenderer,
    graph: IGraphModel
  ) {
    this.canvas = canvas
    this.renderer = renderer
    this.graph = graph
    
    this.bindEvents()
  }

  /** 绑定事件 */
  private bindEvents(): void {
    this.canvas.addEventListener('mousedown', this.onMouseDown)
    this.canvas.addEventListener('mousemove', this.onMouseMove)
    this.canvas.addEventListener('mouseup', this.onMouseUp)
    this.canvas.addEventListener('mouseleave', this.onMouseLeave)
    this.canvas.addEventListener('wheel', this.onWheel, { passive: false })
    this.canvas.addEventListener('dblclick', this.onDoubleClick)
    this.canvas.addEventListener('contextmenu', this.onContextMenu)
  }

  /** 解绑事件 */
  unbindEvents(): void {
    this.canvas.removeEventListener('mousedown', this.onMouseDown)
    this.canvas.removeEventListener('mousemove', this.onMouseMove)
    this.canvas.removeEventListener('mouseup', this.onMouseUp)
    this.canvas.removeEventListener('mouseleave', this.onMouseLeave)
    this.canvas.removeEventListener('wheel', this.onWheel)
    this.canvas.removeEventListener('dblclick', this.onDoubleClick)
    this.canvas.removeEventListener('contextmenu', this.onContextMenu)
  }

  // ===== 事件处理 =====

  private onMouseDown = (e: MouseEvent): void => {
    const pos = this.getMousePos(e)
    const worldPos = this.renderer.screenToWorld(pos)
    
    // 检查是否点击了节点
    const clickedNode = this.findNodeAt(worldPos)
    
    this.state.lastMousePos = pos
    this.state.dragStartPos = pos

    if (e.button === 0) { // 左键
      if (clickedNode) {
        // 如果点击了添加按钮，触发添加子节点事件
        if (clickedNode.hitAddButton) {
          this.emit('node:add-child', {
            nodeId: clickedNode.id,
            position: worldPos,
            originalEvent: e
          })
          return
        }
        
        // 如果点击了折叠图标，触发折叠事件而不是拖拽
        if (clickedNode.hitCollapseIcon) {
          this.emit('node:toggle-collapse', {
            nodeId: clickedNode.id,
            position: worldPos,
            originalEvent: e
          })
          return
        }
        
        // Ctrl/Cmd + 点击：多选模式
        const isMultiSelect = e.ctrlKey || e.metaKey
        
        if (isMultiSelect) {
          // 切换选中状态
          if (this.selectedIds.has(clickedNode.id)) {
            this.selectedIds.delete(clickedNode.id)
          } else {
            this.selectedIds.add(clickedNode.id)
          }
        } else {
          // 普通点击：如果点击的节点不在选中集合中，重置选择
          if (!this.selectedIds.has(clickedNode.id)) {
            this.selectedIds.clear()
            this.selectedIds.add(clickedNode.id)
          }
        }
            
        this.state.isDragging = true
        this.state.draggedNodeId = clickedNode.id
        
        // 找出顶层选中节点（那些祖先节点不在选中集合中的节点）
        const topLevelSelectedIds = new Set<string>()
        this.selectedIds.forEach(id => {
          // 检查该节点的祖先是否在选中集合中
          let hasSelectedAncestor = false
          let parentId: string | undefined = this.graph.getParent(id)?.id
          while (parentId) {
            if (this.selectedIds.has(parentId)) {
              hasSelectedAncestor = true
              break
            }
            parentId = this.graph.getParent(parentId)?.id
          }
          if (!hasSelectedAncestor) {
            topLevelSelectedIds.add(id)
          }
        })
        
        // 只记录顶层选中节点及其后代节点的起始位置
        this.state.dragStartPositions.clear()
        topLevelSelectedIds.forEach(id => {
          const node = this.graph.getNode(id)
          if (node) {
            this.state.dragStartPositions.set(id, { ...node.position })
            
            // 记录该节点所有后代节点的起始位置
            const descendants = this.graph.getDescendants(id)
            descendants.forEach(descendant => {
              if (!descendant.hidden) {
                this.state.dragStartPositions.set(descendant.id, { ...descendant.position })
              }
            })
          }
        })
            
        // 检测双击
        const now = Date.now()
        if (now - this.lastClickTime < this.config.doubleClickDelay &&
            this.lastClickNodeId === clickedNode.id) {
          // 这是双击，不触发拖拽
          this.state.isDragging = false
        }
        this.lastClickTime = now
        this.lastClickNodeId = clickedNode.id
            
        this.emit('node:click', {
          nodeId: clickedNode.id,
          position: worldPos,
          originalEvent: e,
          selectedIds: this.getSelectedIds()
        })
      } else {
        // 点击空白区域
        // 如果没有按下 Ctrl/Cmd，清除选择
        if (!e.ctrlKey && !e.metaKey) {
          this.selectedIds.clear()
        }
        
        // 开始框选或平移
        if (e.shiftKey) {
          // Shift + 点击：框选
          this.state.isSelecting = true
          this.state.selectionBox = {
            x: worldPos.x,
            y: worldPos.y,
            width: 0,
            height: 0
          }
        } else {
          // 平移画布
          this.state.isPanning = true
        }
        
        this.emit('canvas:click', {
          position: worldPos,
          originalEvent: e
        })
      }
    }
  }

  private onMouseMove = (e: MouseEvent): void => {
    const pos = this.getMousePos(e)
    const worldPos = this.renderer.screenToWorld(pos)
    const delta = {
      x: pos.x - this.state.lastMousePos.x,
      y: pos.y - this.state.lastMousePos.y
    }

    if (this.state.isDragging && this.state.draggedNodeId) {
      // 拖拽节点（支持多选）
      const draggedNode = this.graph.getNode(this.state.draggedNodeId)
      if (draggedNode) {
        const newWorldPos = this.renderer.screenToWorld(pos)
        const startPos = this.state.dragStartPositions.get(this.state.draggedNodeId)
        
        if (startPos) {
          // 计算世界坐标的增量
          const worldDelta = {
            x: newWorldPos.x - startPos.x,
            y: newWorldPos.y - startPos.y
          }
          
          // 直接移动 dragStartPositions 中的所有节点（顶层选中节点 + 后代）
          // 由于初始化时已经去重，每个节点只会被处理一次
          this.state.dragStartPositions.forEach((nodeStartPos, nodeId) => {
            this.graph.updateNodePosition(nodeId, {
              x: nodeStartPos.x + worldDelta.x,
              y: nodeStartPos.y + worldDelta.y
            })
          })
        }
        
        this.emit('node:drag:move', {
          nodeId: this.state.draggedNodeId,
          position: newWorldPos,
          delta,
          originalEvent: e,
          selectedIds: this.getSelectedIds()
        })
      }
    } else if (this.state.isPanning) {
      // 平移画布
      const viewport = this.renderer.getViewport()
      this.renderer.setViewport({
        panX: viewport.panX + delta.x,
        panY: viewport.panY + delta.y
      })
      
      this.emit('pan', {
        delta,
        position: worldPos,
        originalEvent: e
      })
    } else if (this.state.isSelecting && this.state.selectionBox) {
      // 框选
      const startWorld = this.renderer.screenToWorld(this.state.dragStartPos)
      this.state.selectionBox = {
        x: Math.min(startWorld.x, worldPos.x),
        y: Math.min(startWorld.y, worldPos.y),
        width: Math.abs(worldPos.x - startWorld.x),
        height: Math.abs(worldPos.y - startWorld.y)
      }
      
      // 同步选框到渲染器
      this.renderer.setSelectionBox(this.state.selectionBox)
      
      this.emit('selection:move', {
        position: worldPos,
        originalEvent: e
      })
    } else {
      // 悬停检测：检查是否在折叠图标上
      const hoverNode = this.findNodeAt(worldPos)
      if (hoverNode?.hitCollapseIcon) {
        this.canvas.style.cursor = 'pointer'
      } else {
        this.canvas.style.cursor = 'default'
      }
      
      // 检测描述图标悬停（只在状态变化时触发，优化性能）
      const descIconInfo = this.findDescriptionIconAt(worldPos)
      const hoverNodeId = descIconInfo?.nodeId || null
      
      if (hoverNodeId !== this.currentDescHoverNodeId) {
        // 状态变化，更新缓存并发出事件
        this.currentDescHoverNodeId = hoverNodeId
        
        if (descIconInfo) {
          this.emit('description:hover', {
            nodeId: descIconInfo.nodeId,
            description: descIconInfo.description,
            position: worldPos,
            originalEvent: e
          })
        } else {
          // 鼠标离开描述图标，隐藏 tooltip
          this.emit('description:hover', {})
        }
      }
    }

    this.state.lastMousePos = pos
  }

  private onMouseUp = (e: MouseEvent): void => {
    const pos = this.getMousePos(e)
    const worldPos = this.renderer.screenToWorld(pos)

    if (this.state.isDragging) {
      this.emit('node:drag:end', {
        nodeId: this.state.draggedNodeId || undefined,
        position: worldPos,
        originalEvent: e,
        selectedIds: this.getSelectedIds()
      })
    }

    if (this.state.isSelecting && this.state.selectionBox) {
      // 查找框选范围内的节点
      this.selectNodesInBox(this.state.selectionBox)
      this.state.selectionBox = null
      
      // 清除渲染器中的选框
      this.renderer.setSelectionBox(null)
      
      // 发送选择完成事件
      this.emit('selection:end', {
        position: worldPos,
        originalEvent: e,
        selectedIds: this.getSelectedIds()
      })
    }

    // 重置状态
    this.state.isDragging = false
    this.state.isPanning = false
    this.state.isSelecting = false
    this.state.draggedNodeId = null
    this.state.dragStartPositions.clear()
  }

  private onMouseLeave = (): void => {
    this.state.isDragging = false
    this.state.isPanning = false
    this.state.isSelecting = false
    
    // 清除选框
    if (this.state.selectionBox) {
      this.state.selectionBox = null
      this.renderer.setSelectionBox(null)
    }
    
    // 隐藏描述 tooltip 并清除缓存
    this.currentDescHoverNodeId = null
    this.emit('description:hover', {})
  }

  private onWheel = (e: WheelEvent): void => {
    e.preventDefault()
    
    const pos = this.getMousePos(e)
    const viewport = this.renderer.getViewport()
    
    // 计算新的缩放
    const zoomDelta = e.deltaY > 0 ? -this.config.zoomSpeed : this.config.zoomSpeed
    const newZoom = Math.max(
      this.config.minZoom,
      Math.min(this.config.maxZoom, viewport.zoom + zoomDelta)
    )
    
    // 以鼠标位置为中心缩放
    const worldPos = this.renderer.screenToWorld(pos)
    const newPanX = pos.x - worldPos.x * newZoom
    const newPanY = pos.y - worldPos.y * newZoom
    
    this.renderer.setViewport({
      zoom: newZoom,
      panX: newPanX,
      panY: newPanY
    })
    
    this.emit('zoom', {
      position: worldPos,
      viewport: this.renderer.getViewport(),
      originalEvent: e
    })
  }

  private onDoubleClick = (e: MouseEvent): void => {
    const pos = this.getMousePos(e)
    const worldPos = this.renderer.screenToWorld(pos)
    const clickedNode = this.findNodeAt(worldPos)

    if (clickedNode) {
      this.emit('node:dblclick', {
        nodeId: clickedNode.id,
        position: worldPos,
        originalEvent: e
      })
    }
  }

  private onContextMenu = (e: MouseEvent): void => {
    e.preventDefault()
    const pos = this.getMousePos(e)
    const worldPos = this.renderer.screenToWorld(pos)
    const clickedNode = this.findNodeAt(worldPos)

    if (clickedNode) {
      this.emit('node:contextmenu', {
        nodeId: clickedNode.id,
        position: worldPos,
        originalEvent: e
      })
    }
  }

  // ===== 辅助方法 =====

  /** 获取鼠标位置 */
  private getMousePos(e: MouseEvent): Point {
    const rect = this.canvas.getBoundingClientRect()
    return {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    }
  }

  /** 查找位置处的节点 */
  private findNodeAt(pos: Point): { id: string; hitCollapseIcon?: boolean; hitAddButton?: boolean } | null {
    for (const node of this.graph.nodes) {
      if (node.hidden) continue
      
      const nodePos = node.position
      const children = this.graph.getChildren(node.id)
      const hasChildren = children.length > 0
      const isSelected = this.renderer.getSelectedNodeId() === node.id
      
      // 计算节点尺寸（与 CanvasRenderer 保持一致）
      // 根节点宽度为 140，普通节点宽度为 120，不再因折叠增加宽度
      const baseWidth = node.isRoot ? 140 : (node.size?.width || 120)
      const actualWidth = baseWidth
      const halfWidth = actualWidth / 2
      const halfHeight = (node.size?.height || 36) / 2
      
      // 检查是否点击添加按钮（选中节点时）
      if (isSelected) {
        const addButtonHit = this.checkAddButtonHit(pos, nodePos, halfWidth, halfHeight, node.id)
        if (addButtonHit) {
          return { id: node.id, hitAddButton: true }
        }
      }
      
      if (
        pos.x >= nodePos.x - halfWidth &&
        pos.x <= nodePos.x + halfWidth &&
        pos.y >= nodePos.y - halfHeight &&
        pos.y <= nodePos.y + halfHeight
      ) {
        // 检测是否点击了折叠图标
        // 图标在内容区域左侧，需要计算实际位置（与 CanvasRenderer 保持一致）
        if (hasChildren) {
          // 图标位置计算：与 CanvasRenderer 保持一致
          // 图标宽度 16，间距 6
          // 文字宽度近似为节点宽度的 50%（这个近似足够用于图标点击检测）
          const iconWidth = 16
          const iconGap = 6
          const approxTextWidth = actualWidth * 0.5  // 近似文字宽度
          const totalContentWidth = iconWidth + iconGap + approxTextWidth
          const contentStartX = nodePos.x - totalContentWidth / 2
          
          const iconCenterX = contentStartX + iconWidth / 2
          const iconLeft = iconCenterX - 12
          const iconRight = iconCenterX + 12
          const iconTop = nodePos.y - 12
          const iconBottom = nodePos.y + 12
          
          const hitCollapseIcon = 
            pos.x >= iconLeft && pos.x <= iconRight &&
            pos.y >= iconTop && pos.y <= iconBottom
          
          if (hitCollapseIcon) {
            return { id: node.id, hitCollapseIcon: true }
          }
        }
        
        return { id: node.id, hitCollapseIcon: false }
      }
    }
    return null
  }

  /** 查找描述图标位置 */
  private findDescriptionIconAt(worldPos: Point): { nodeId: string; description: string } | null {
    const iconPositions = this.renderer.getDescriptionIconPositions()
    const viewport = this.renderer.getViewport()
    
    for (const [_, info] of iconPositions) {
      // 描述图标在世界坐标中的位置
      const iconWorldX = info.x
      const iconWorldY = info.y
      
      // 检测悬浮范围（半径 12 像素，转换到世界坐标）
      const hitRadius = 12 / viewport.zoom
      const dx = worldPos.x - iconWorldX
      const dy = worldPos.y - iconWorldY
      
      if (dx * dx + dy * dy <= hitRadius * hitRadius) {
        return { nodeId: info.nodeId, description: info.description }
      }
    }
    
    return null
  }

  /** 检查是否点击了添加按钮 */
  private checkAddButtonHit(pos: Point, nodePos: Point, halfWidth: number, halfHeight: number, nodeId: string): boolean {
    const offset = 20
    const buttonSize = 14  // 点击区域稍大一些
    
    // 获取节点方向
    const direction = this.getNodeDirection(nodeId)
    let btnX: number, btnY: number
    
    switch (direction) {
      case 'left':
        btnX = nodePos.x - halfWidth - offset
        btnY = nodePos.y
        break
      case 'top':
        btnX = nodePos.x
        btnY = nodePos.y - halfHeight - offset
        break
      case 'bottom':
        btnX = nodePos.x
        btnY = nodePos.y + halfHeight + offset
        break
      case 'right':
      default:
        btnX = nodePos.x + halfWidth + offset
        btnY = nodePos.y
        break
    }
    
    const dx = pos.x - btnX
    const dy = pos.y - btnY
    return Math.sqrt(dx * dx + dy * dy) <= buttonSize
  }

  /** 获取节点方向（与添加按钮位置相关） */
  private nodeDirections: Map<string, 'left' | 'right' | 'top' | 'bottom'> = new Map()
  
  /** 设置节点方向 */
  setNodeDirections(directions: Map<string, 'left' | 'right' | 'top' | 'bottom'>): void {
    this.nodeDirections = directions
  }

  /** 获取节点方向 */
  private getNodeDirection(nodeId: string): 'left' | 'right' | 'top' | 'bottom' {
    return this.nodeDirections.get(nodeId) || 'right'
  }

  /** 框选范围内的节点 */
  private selectNodesInBox(box: Rect): void {
    this.selectedIds.clear()
    
    for (const node of this.graph.nodes) {
      if (node.hidden) continue
      
      const pos = node.position
      const halfWidth = (node.size?.width || 100) / 2
      const halfHeight = (node.size?.height || 36) / 2
      
      if (
        pos.x - halfWidth >= box.x &&
        pos.x + halfWidth <= box.x + box.width &&
        pos.y - halfHeight >= box.y &&
        pos.y + halfHeight <= box.y + box.height
      ) {
        this.selectedIds.add(node.id)
      }
    }
  }

  // ===== 事件发射 =====

  private emit(type: InteractionEventType, data: Partial<InteractionEvent>): void {
    const listeners = this.listeners.get(type)
    if (listeners) {
      const event: InteractionEvent = {
        type,
        viewport: this.renderer.getViewport(),
        ...data
      } as InteractionEvent
      
      listeners.forEach(listener => listener(event))
    }
  }

  // ===== 公共API =====

  /** 监听事件 */
  on(type: InteractionEventType, callback: (e: InteractionEvent) => void): () => void {
    if (!this.listeners.has(type)) {
      this.listeners.set(type, new Set())
    }
    this.listeners.get(type)!.add(callback)
    
    return () => this.off(type, callback)
  }

  /** 移除监听 */
  off(type: InteractionEventType, callback: (e: InteractionEvent) => void): void {
    this.listeners.get(type)?.delete(callback)
  }

  /** 获取选中的节点 */
  getSelectedIds(): string[] {
    return Array.from(this.selectedIds)
  }

  /** 设置选中节点 */
  setSelectedIds(ids: string[]): void {
    this.selectedIds = new Set(ids)
  }

  /** 清除选择 */
  clearSelection(): void {
    this.selectedIds.clear()
  }

  /** 获取框选框 */
  getSelectionBox(): Rect | null {
    return this.state.selectionBox
  }

  /** 更新图模型引用 */
  setGraph(graph: IGraphModel): void {
    this.graph = graph
  }
}

export default InteractionManager
