/**
 * useMindMap - Vue 组合式函数
 * 
 * 整合四层架构：
 * - Graph Model
 * - Layout Engine
 * - Canvas Renderer
 * - Interaction System
 */

import { ref, watch, onUnmounted, computed, type Ref } from 'vue'
import { GraphModel } from './core/GraphModel'
import { HistoryManager } from './core/HistoryManager'
import { layoutRegistry } from './layout'
import { CanvasRenderer } from './renderer'
import { InteractionManager } from './interaction'
import { getTextMeasurer } from './core/TextNodeMeasurer'
import type { LayoutType, Point, Node, SerializedGraph } from './core/types'
import type { ThemeConfig } from './theme'

interface UseMindMapOptions {
  /** 初始数据 */
  initialData?: SerializedGraph
  /** 初始布局类型 */
  layoutType?: LayoutType
  /** 画布宽度 */
  width?: number
  /** 画布高度 */
  height?: number
}

interface UseMindMapReturn {
  /** 图模型 */
  graph: Ref<GraphModel>
  /** 当前布局类型 */
  layoutType: Ref<LayoutType>
  /** 渲染器 */
  renderer: Ref<CanvasRenderer | null>
  /** 交互管理器 */
  interaction: Ref<InteractionManager | null>
  /** Canvas 元素引用 */
  canvasRef: Ref<HTMLCanvasElement | null>
  
  /** 方法 */
  initCanvas: (canvas: HTMLCanvasElement, initialData?: SerializedGraph | null) => boolean
  applyLayout: (type?: LayoutType) => void
  addNode: (parentId: string, label?: string) => Node | undefined
  addParent: (nodeId: string, label?: string) => Node | undefined
  removeNode: (id: string) => void
  removeNodes: (ids: string[]) => void
  updateNodeLabel: (id: string, label: string) => void
  updateNodeData: (id: string, data: { label?: string; description?: string }) => void
  toggleCollapse: (id: string) => void
  exportData: () => SerializedGraph
  importData: (data: SerializedGraph) => void
  fitView: () => void
  zoomIn: () => void
  zoomOut: () => void
  resetZoom: () => void
  setSelectedNodeId: (nodeId: string | null) => void
  setSelectedNodeIds: (ids: string[]) => void
  getSelectedNodeIds: () => string[]
  resize: () => void
  setThemeConfig: (theme: ThemeConfig) => void
  
  /** 历史管理 */
  canUndo: Readonly<Ref<boolean>>
  canRedo: Readonly<Ref<boolean>>
  undo: () => boolean
  redo: () => boolean
  saveHistory: (action?: string) => void
  clearHistory: () => void
}

export function useMindMap(options: UseMindMapOptions = {}): UseMindMapReturn {
  // 状态
  const graph = ref<GraphModel>(new GraphModel())
  const layoutType = ref<LayoutType>(options.layoutType || 'radial-mindmap')
  const renderer = ref<CanvasRenderer | null>(null)
  const interaction = ref<InteractionManager | null>(null)
  const canvasRef = ref<HTMLCanvasElement | null>(null)
  
  // 历史管理器
  const historyManager = new HistoryManager({ maxHistory: 50 })
  
  // 历史状态的响应式包装（用于按钮状态更新）
  const historyVersion = ref(0)

  // 内部状态
  let animationFrame: number | null = null
  let graphChangeListener: (() => void) | null = null
  // 缓存的主题配置（在 renderer 初始化前设置的主题）
  let cachedThemeConfig: ThemeConfig | null = null

  /**
   * 绑定图变化监听器
   */
  function bindGraphChangeListener(): void {
    // 移除旧监听器
    if (graphChangeListener) {
      graphChangeListener()
    }
    // 添加新监听器
    graphChangeListener = graph.value.onChange(() => {
      requestRender()
    })
  }

  /**
   * 初始化 Canvas
   * @param canvas Canvas 元素
   * @param initialData 可选的初始数据，如果提供则直接使用，否则创建默认图
   * @returns 是否需要适应画布（用于新建或无视口数据时）
   */
  function initCanvas(canvas: HTMLCanvasElement, initialData?: SerializedGraph | null): boolean {
    canvasRef.value = canvas

    // 使用父容器的尺寸，因为 canvas 本身可能还没有正确渲染
    const parent = canvas.parentElement
    const width = options.width || parent?.clientWidth || canvas.clientWidth || 800
    const height = options.height || parent?.clientHeight || canvas.clientHeight || 600

    // 创建渲染器
    renderer.value = new CanvasRenderer(canvas, { width, height })

    // 应用缓存的主题配置（如果在初始化前设置了主题）
    if (cachedThemeConfig) {
      renderer.value.setThemeConfig(cachedThemeConfig)
    }

    // 标记是否需要布局
    let needsLayout = true
    // 标记是否需要适应画布
    let needsFitView = false

    // 加载数据（必须在创建 InteractionManager 之前）
    if (initialData) {
      graph.value = GraphModel.deserialize(initialData)
      // 恢复折叠状态（隐藏已折叠节点的子节点）
      graph.value.restoreCollapseState()
      
      // 同步布局类型到 ref（关键修复！）
      if (graph.value.layoutType) {
        layoutType.value = graph.value.layoutType
      }
      
      // 检查数据中是否已有位置信息
      // 只要节点的 position 字段存在且是有效对象，就认为有位置
      const hasValidPositions = graph.value.nodes.every(n => 
        n.position && typeof n.position.x === 'number' && typeof n.position.y === 'number'
      )
      
      if (hasValidPositions && graph.value.nodes.length > 0) {
        needsLayout = false
        // 仍需更新渲染器的布局类型
        renderer.value?.setLayoutType(graph.value.layoutType || layoutType.value)
        // 更新节点尺寸（即使不重新布局也需要正确的尺寸信息）
        updateNodeSizes()
        // 更新节点方向
        updateNodeDirections()
        
        // 恢复视口状态
        if (initialData.viewport) {
          renderer.value.setViewport(initialData.viewport)
        } else {
          // 没有保存的视口状态，需要适应画布
          needsFitView = true
        }
      }
    } else if (graph.value.nodes.length === 0) {
      graph.value = GraphModel.createDefault('中心主题')
      needsFitView = true
    }

    // 应用初始布局（仅在需要时）
    if (needsLayout) {
      applyLayout(graph.value.layoutType || layoutType.value)
      needsFitView = true
    }

    // 创建交互管理器（在 graph 初始化后）
    interaction.value = new InteractionManager(canvas, renderer.value, graph.value)

    // 监听图变化
    bindGraphChangeListener()

    // 开始渲染循环
    startRenderLoop()
    
    return needsFitView
  }

  /** 更新节点尺寸（使用独立 TextNodeMeasurer，不依赖 renderer） */
  function updateNodeSizes(): void {
    const measurer = getTextMeasurer()
    
    graph.value.nodes.forEach(node => {
      if (node.hidden) return
      
      const children = graph.value.getChildren(node.id)
      const hasChildren = children.length > 0
      let allDescendants = 0
      if (node.collapsed && hasChildren) {
        allDescendants = graph.value.getDescendants(node.id).length
      }
      
      const size = measurer.measureNodeSize(
        node.label,
        node.isRoot,
        hasChildren,
        node.collapsed,
        allDescendants
      )
      
      node.size = size
    })
  }

  /**
   * 渲染循环
   */
  function startRenderLoop(): void {
    const loop = () => {
      if (renderer.value) {
        renderer.value.render(graph.value)
      }
      animationFrame = requestAnimationFrame(loop)
    }
    loop()
  }

  /**
   * 请求渲染
   */
  function requestRender(): void {
    renderer.value?.forceRedraw()
  }

  /**
   * 应用布局
   */
  function applyLayout(type?: LayoutType): void {
    if (type) {
      layoutType.value = type
      // 同步更新 graph.layoutType，确保 serialize() 包含正确的布局类型
      graph.value.setLayoutType(type)
    }

    const engine = layoutRegistry.get(layoutType.value)
    if (!engine) {
      console.warn(`Layout engine "${layoutType.value}" not found`)
      return
    }

    // 更新节点尺寸（文本换行需要使用实际尺寸进行布局）
    updateNodeSizes()
    
    // 计算布局
    const positions = engine.layout(graph.value)
    
    // 更新节点位置（直接更新到节点对象）
    positions.forEach((pos, nodeId) => {
      // 跳过特殊的骨干线信息
      if (nodeId.startsWith('__')) return
      const node = graph.value.getNode(nodeId)
      if (node) {
        node.position = pos
      }
    })

    // 更新渲染器
    renderer.value?.setLayoutType(layoutType.value)

    // 更新节点方向
    updateNodeDirections()

    // 重绘
    requestRender()
  }

  /**
   * 添加节点
   */
  function addNode(parentId: string, label: string = '新节点'): Node | undefined {
    const node = graph.value.addChild(parentId, { label })
    applyLayout()
    return node
  }

  /**
   * 在当前节点和其父节点之间插入一个新父节点
   */
  function addParent(nodeId: string, label: string = '新节点'): Node | undefined {
    const node = graph.value.insertParent(nodeId, { label })
    applyLayout()
    return node
  }

  /**
   * 删除节点
   */
  function removeNode(id: string): void {
    graph.value.removeNode(id)
    applyLayout()
  }

  /**
   * 批量删除节点
   */
  function removeNodes(ids: string[]): void {
    ids.forEach(id => {
      // 不删除根节点
      const node = graph.value.getNode(id)
      if (node && !node.isRoot) {
        graph.value.removeNode(id)
      }
    })
    applyLayout()
  }

  /**
   * 更新节点标签
   */
  function updateNodeLabel(id: string, label: string): void {
    graph.value.updateNode(id, { label })
    requestRender()
  }

  /**
   * 更新节点数据（标题和描述）
   * 标题变更会影响节点尺寸和换行缓存，需要重新布局
   */
  function updateNodeData(id: string, data: { label?: string; description?: string }): void {
    graph.value.updateNode(id, data)
    if (data.label !== undefined) {
      // 标签变更影响节点尺寸和文本缓存，需要重新布局
      renderer.value?.clearTextCache()
      updateNodeSizes()
      applyLayout()
    } else {
      requestRender()
    }
  }

  /**
   * 折叠/展开节点
   */
  function toggleCollapse(id: string): void {
    graph.value.toggleCollapse(id)
    updateNodeDirections()
    requestRender()
  }

  /**
   * 设置主题配置
   */
  function setThemeConfig(theme: ThemeConfig): void {
    // 始终缓存主题配置，以便在 renderer 初始化时应用
    cachedThemeConfig = theme
    
    if (renderer.value) {
      renderer.value.setThemeConfig(theme)
      requestRender()
    }
  }

  /**
   * 导出数据
   */
  function exportData(): SerializedGraph {
    const data = graph.value.serialize()
    // 保存视口状态
    if (renderer.value) {
      data.viewport = renderer.value.getViewport()
    }
    return data
  }

  /**
   * 导入数据
   */
  function importData(data: SerializedGraph): void {
    graph.value = GraphModel.deserialize(data)
    // 恢复折叠状态（隐藏已折叠节点的子节点）
    graph.value.restoreCollapseState()
    interaction.value?.setGraph(graph.value)
    bindGraphChangeListener()
    
    // 同步布局类型到 ref（关键修复！）
    if (graph.value.layoutType) {
      layoutType.value = graph.value.layoutType
    }
    
    // 检查数据中是否已有位置信息
    // 只要节点的 position 字段存在且是有效对象，就认为有位置
    const hasValidPositions = graph.value.nodes.every(n => 
      n.position && typeof n.position.x === 'number' && typeof n.position.y === 'number'
    )
    
    if (hasValidPositions && graph.value.nodes.length > 0) {
      // 已有位置信息，只更新渲染器状态，不重新布局
      renderer.value?.setLayoutType(graph.value.layoutType)
      updateNodeDirections()
      
      // 恢复视口状态
      if (data.viewport) {
        renderer.value?.setViewport(data.viewport)
      }
      requestRender()
    } else {
      // 没有位置信息，需要重新布局
      applyLayout(graph.value.layoutType)
    }
  }

  /**
   * 适应画布
   */
  function fitView(): void {
    if (!renderer.value || !canvasRef.value) return

    // 先同步 canvas 尺寸（确保使用最新的父容器尺寸）
    const parent = canvasRef.value.parentElement
    if (parent) {
      const newWidth = parent.clientWidth
      const newHeight = parent.clientHeight
      if (newWidth > 0 && newHeight > 0) {
        renderer.value.resize(newWidth, newHeight)
      }
    }

    // 计算图的边界
    let minX = Infinity, minY = Infinity
    let maxX = -Infinity, maxY = -Infinity

    graph.value.nodes.forEach(node => {
      if (node.hidden) return
      const halfWidth = (node.size?.width || 100) / 2
      const halfHeight = (node.size?.height || 36) / 2
      
      minX = Math.min(minX, node.position.x - halfWidth)
      maxX = Math.max(maxX, node.position.x + halfWidth)
      minY = Math.min(minY, node.position.y - halfHeight)
      maxY = Math.max(maxY, node.position.y + halfHeight)
    })

    if (!isFinite(minX)) return

    const graphWidth = maxX - minX
    const graphHeight = maxY - minY
    
    // 使用实际的 canvas 尺寸
    const canvasWidth = canvasRef.value.clientWidth
    const canvasHeight = canvasRef.value.clientHeight
    
    if (canvasWidth <= 0 || canvasHeight <= 0) return

    // 计算合适的缩放（留出边距）
    const padding = 60
    const zoom = Math.min(
      (canvasWidth - padding * 2) / graphWidth,
      (canvasHeight - padding * 2) / graphHeight,
      1.5  // 最大缩放
    )

    // 计算平移，使图形居中
    const centerX = (minX + maxX) / 2
    const centerY = (minY + maxY) / 2
    const panX = canvasWidth / 2 - centerX * zoom
    const panY = canvasHeight / 2 - centerY * zoom

    renderer.value.setViewport({ zoom, panX, panY })
    requestRender()
  }

  /**
   * 放大
   */
  function zoomIn(): void {
    const viewport = renderer.value?.getViewport()
    if (!viewport) return

    renderer.value?.setViewport({
      zoom: Math.min(viewport.zoom * 1.2, 3)
    })
    requestRender()
  }

  /**
   * 缩小
   */
  function zoomOut(): void {
    const viewport = renderer.value?.getViewport()
    if (!viewport) return

    renderer.value?.setViewport({
      zoom: Math.max(viewport.zoom / 1.2, 0.1)
    })
    requestRender()
  }

  /**
   * 重置缩放
   */
  function resetZoom(): void {
    renderer.value?.setViewport({ zoom: 1, panX: 0, panY: 0 })
    requestRender()
  }

  /**
   * 设置选中节点
   */
  function setSelectedNodeId(nodeId: string | null): void {
    renderer.value?.setSelectedNodeId(nodeId)
    // 同步到交互管理器
    if (nodeId) {
      interaction.value?.setSelectedIds([nodeId])
    } else {
      interaction.value?.clearSelection()
    }
    // 更新节点方向
    updateNodeDirections()
    requestRender()
  }

  /**
   * 设置多选节点
   */
  function setSelectedNodeIds(ids: string[]): void {
    renderer.value?.setSelectedNodeIds(ids)
    // 同步到交互管理器
    interaction.value?.setSelectedIds(ids)
    // 更新节点方向
    updateNodeDirections()
    requestRender()
  }

  /**
   * 获取选中的节点 ID 列表
   */
  function getSelectedNodeIds(): string[] {
    return interaction.value?.getSelectedIds() || []
  }

  /**
   * 更新节点方向（用于添加按钮位置）
   */
  function updateNodeDirections(): void {
    const directions = new Map<string, 'left' | 'right' | 'top' | 'bottom'>()
    
    graph.value.nodes.forEach(node => {
      if (node.isRoot) {
        directions.set(node.id, 'right')
        return
      }
      
      // 找到父节点
      const parent = graph.value.getParent(node.id)
      if (!parent) {
        directions.set(node.id, 'right')
        return
      }
      
      // 根据节点相对于父节点的位置确定方向
      const dx = node.position.x - parent.position.x
      const dy = node.position.y - parent.position.y
      const angle = Math.atan2(dy, dx)
      
      // 子节点在父节点的哪个方向，按钮就显示在该方向
      if (angle >= -Math.PI / 4 && angle < Math.PI / 4) {
        directions.set(node.id, 'right')
      } else if (angle >= Math.PI / 4 && angle < Math.PI * 3 / 4) {
        directions.set(node.id, 'bottom')
      } else if (angle >= -Math.PI * 3 / 4 && angle < -Math.PI / 4) {
        directions.set(node.id, 'top')
      } else {
        directions.set(node.id, 'left')
      }
    })
    
    // 同步到渲染器和交互管理器
    renderer.value?.setNodeDirections(directions)
    interaction.value?.setNodeDirections(directions)
  }

  // 注意：不使用 watch(layoutType) 来自动触发 applyLayout
  // 原因：
  // 1. 用户切换布局时，handleLayoutChange 已调用 applyLayout
  // 2. 加载数据时设置 layoutType 会触发 watch，覆盖保存的位置
  // 3. applyLayout 内部设置 layoutType 会导致循环调用

  // 清理
  onUnmounted(() => {
    if (animationFrame) {
      cancelAnimationFrame(animationFrame)
    }
    interaction.value?.unbindEvents()
    if (graphChangeListener) {
      graphChangeListener()
    }
  })

  /**
   * 调整 Canvas 尺寸
   * 全屏切换时调用
   */
  function resize(): void {
    if (!canvasRef.value || !renderer.value) return
    // 使用父容器的尺寸，而不是 Canvas 自身的尺寸
    const parent = canvasRef.value.parentElement
    if (!parent) return
    const width = parent.clientWidth
    const height = parent.clientHeight
    renderer.value.resize(width, height)
    requestRender()
  }

  // ===== 历史管理 =====

  /**
   * 保存当前状态到历史记录
   */
  function saveHistory(action?: string): void {
    historyManager.push(exportData(), action)
    // 触发响应式更新
    historyVersion.value++
  }

  /**
   * 清空历史记录
   */
  function clearHistory(): void {
    historyManager.clear()
    // 触发响应式更新
    historyVersion.value++
  }

  /**
   * 是否可以撤销（响应式）
   */
  const canUndo = computed(() => {
    // 依赖 historyVersion 触发更新
    historyVersion.value
    return historyManager.canUndo
  })

  /**
   * 是否可以重做（响应式）
   */
  const canRedo = computed(() => {
    // 依赖 historyVersion 触发更新
    historyVersion.value
    return historyManager.canRedo
  })

  /**
   * 撤销操作
   * @returns 是否成功撤销
   */
  function undo(): boolean {
    const item = historyManager.undo()
    if (item) {
      importData(item.data)
      // 触发响应式更新
      historyVersion.value++
      return true
    }
    return false
  }

  /**
   * 重做操作
   * @returns 是否成功重做
   */
  function redo(): boolean {
    const item = historyManager.redo()
    if (item) {
      importData(item.data)
      // 触发响应式更新
      historyVersion.value++
      return true
    }
    return false
  }

  return {
    graph,
    layoutType,
    renderer,
    interaction,
    canvasRef,
    initCanvas,
    applyLayout,
    addNode,
    addParent,
    removeNode,
    removeNodes,
    updateNodeLabel,
    updateNodeData,
    toggleCollapse,
    setThemeConfig,
    exportData,
    importData,
    fitView,
    zoomIn,
    zoomOut,
    resetZoom,
    setSelectedNodeId,
    setSelectedNodeIds,
    getSelectedNodeIds,
    resize,
    // 历史管理
    canUndo,
    canRedo,
    undo,
    redo,
    saveHistory,
    clearHistory
  }
}

export default useMindMap
