/**
 * Force-Directed Layout - 力导向布局
 * 
 * 基于 d3-force 理念的现代化实现
 * 
 * 结构特点：
 * - 使用物理模拟计算节点位置
 * - 多种力模型：斥力、引力、碰撞、中心力
 * - 自动避免节点重叠
 * - 自然形成美观的网络布局
 * 
 * 力模型说明：
 * 1. 斥力：节点之间相互排斥，防止重叠
 * 2. 引力：连接的节点相互吸引，保持连接
 * 3. 碰撞力：节点作为有半径的圆，互不穿透
 * 4. 中心力：将整个图拉向中心，避免漂移
 * 5. 径向力：根节点固定，其他节点围绕分布
 * 
 * 适用场景：
 * - 网络关系图
 * - 知识图谱
 * - 社交网络
 * - 复杂关系可视化
 */

import { LayoutEngine, layoutRegistry } from './LayoutEngine'
import type { Node, Point, IGraphModel } from '../core/types'

/** 物理模拟节点 */
interface PhysicsNode {
  id: string
  x: number
  y: number
  vx: number
  vy: number
  radius: number
  isFixed: boolean
  level: number
}

/** 力导向模拟配置 */
interface ForceConfig {
  /** 迭代次数 */
  iterations: number
  /** 理想边长 */
  linkDistance: number
  /** 斥力强度 */
  repulsionStrength: number
  /** 引力强度 */
  linkStrength: number
  /** 碰撞强度 */
  collisionStrength: number
  /** 中心引力强度 */
  centerStrength: number
  /** 径向力强度 */
  radialStrength: number
  /** 阻尼系数 */
  damping: number
  /** 节点半径 */
  nodeRadius: number
  /** 最小距离 */
  minDistance: number
}

export class ForceDirectedLayout extends LayoutEngine {
  readonly type = 'force-directed' as const

  /** 默认配置 */
  protected config: ForceConfig = {
    iterations: 500,
    linkDistance: 120,
    repulsionStrength: 800,
    linkStrength: 0.3,
    collisionStrength: 0.8,
    centerStrength: 0.05,
    radialStrength: 0.2,
    damping: 0.85,
    nodeRadius: 50,
    minDistance: 10
  }

  layout(graph: IGraphModel): Map<string, Point> {
    const positions = new Map<string, Point>()
    const root = graph.root
    const visibleNodes = graph.nodes.filter(n => !n.hidden)
    
    if (visibleNodes.length === 0) return positions
    
    // 单节点直接返回中心
    if (visibleNodes.length === 1) {
      positions.set(visibleNodes[0].id, { x: 400, y: 300 })
      return positions
    }

    // 初始化物理节点
    const physicsNodes = this.initializeNodes(visibleNodes, root)
    
    // 构建邻接关系
    const adjacency = this.buildAdjacency(graph, physicsNodes)
    
    // 执行力模拟
    this.simulate(physicsNodes, adjacency, root)
    
    // 提取最终位置
    physicsNodes.forEach((pn, id) => {
      positions.set(id, { x: pn.x, y: pn.y })
    })

    return positions
  }

  /**
   * 初始化节点位置
   * 使用圆形布局作为初始位置，避免随机性导致的布局不稳定
   */
  private initializeNodes(
    nodes: Node[],
    root: Node | undefined
  ): Map<string, PhysicsNode> {
    const physicsNodes = new Map<string, PhysicsNode>()
    const centerX = 400
    const centerY = 300
    
    // 按层级分组
    const nodesByLevel = new Map<number, Node[]>()
    nodes.forEach(node => {
      const level = node.level || 0
      if (!nodesByLevel.has(level)) {
        nodesByLevel.set(level, [])
      }
      nodesByLevel.get(level)!.push(node)
    })

    // 分层圆形布局初始化
    nodesByLevel.forEach((levelNodes, level) => {
      const radius = 80 + level * 100  // 每层半径递增
      const angleStep = (2 * Math.PI) / levelNodes.length
      
      levelNodes.forEach((node, index) => {
        const angle = angleStep * index - Math.PI / 2  // 从顶部开始
        
        physicsNodes.set(node.id, {
          id: node.id,
          x: centerX + radius * Math.cos(angle),
          y: centerY + radius * Math.sin(angle),
          vx: 0,
          vy: 0,
          radius: this.config.nodeRadius,
          isFixed: node.isRoot || false,
          level: level
        })
      })
    })

    // 根节点固定在中心
    if (root && physicsNodes.has(root.id)) {
      const rootNode = physicsNodes.get(root.id)!
      rootNode.x = centerX
      rootNode.y = centerY
      rootNode.isFixed = true
    }

    return physicsNodes
  }

  /**
   * 构建邻接关系
   */
  private buildAdjacency(
    graph: IGraphModel,
    physicsNodes: Map<string, PhysicsNode>
  ): Map<string, Set<string>> {
    const adjacency = new Map<string, Set<string>>()
    
    // 初始化邻接表
    physicsNodes.forEach((_, id) => {
      adjacency.set(id, new Set())
    })
    
    // 添加边关系
    graph.edges.forEach(edge => {
      if (!physicsNodes.has(edge.source) || !physicsNodes.has(edge.target)) return
      
      adjacency.get(edge.source)!.add(edge.target)
      adjacency.get(edge.target)!.add(edge.source)
    })
    
    return adjacency
  }

  /**
   * 力导向模拟主循环
   */
  private simulate(
    nodes: Map<string, PhysicsNode>,
    adjacency: Map<string, Set<string>>,
    root: Node | undefined
  ): void {
    const centerX = 400
    const centerY = 300
    const cfg = this.config

    // 使用衰减的温度调度
    for (let iteration = 0; iteration < cfg.iterations; iteration++) {
      // 温度衰减（模拟退火）
      const temperature = 1 - iteration / cfg.iterations
      const alpha = 0.1 + 0.9 * temperature  // 学习率衰减

      // 1. 应用斥力（所有节点对）
      this.applyRepulsion(nodes, alpha)
      
      // 2. 应用碰撞检测
      this.applyCollision(nodes, alpha)
      
      // 3. 应用引力（连接的节点）
      this.applyLinkForce(nodes, adjacency, alpha)
      
      // 4. 应用中心力
      this.applyCenterForce(nodes, centerX, centerY, alpha)
      
      // 5. 应用径向力（围绕根节点）
      if (root) {
        this.applyRadialForce(nodes, root.id, centerX, centerY, alpha)
      }

      // 6. 更新位置
      this.updatePositions(nodes, alpha)
    }
  }

  /**
   * 斥力：节点之间相互排斥
   * 使用 Barnes-Hut 近似的简化版本
   */
  private applyRepulsion(
    nodes: Map<string, PhysicsNode>,
    alpha: number
  ): void {
    const cfg = this.config
    const nodeList = Array.from(nodes.values())
    
    for (let i = 0; i < nodeList.length; i++) {
      const node1 = nodeList[i]
      if (node1.isFixed) continue
      
      for (let j = i + 1; j < nodeList.length; j++) {
        const node2 = nodeList[j]
        
        const dx = node1.x - node2.x
        const dy = node1.y - node2.y
        const distanceSq = dx * dx + dy * dy
        const distance = Math.sqrt(distanceSq) || cfg.minDistance
        
        // 斥力与距离平方成反比（库仑定律）
        const minDist = node1.radius + node2.radius
        const effectiveDist = Math.max(distance, minDist)
        
        // 斥力大小
        const force = cfg.repulsionStrength * alpha / (effectiveDist * effectiveDist)
        
        // 归一化方向
        const fx = (dx / distance) * force
        const fy = (dy / distance) * force
        
        // 应用力（牛顿第三定律）
        node1.vx += fx
        node1.vy += fy
        
        if (!node2.isFixed) {
          node2.vx -= fx
          node2.vy -= fy
        }
      }
    }
  }

  /**
   * 碰撞检测：防止节点重叠
   */
  private applyCollision(
    nodes: Map<string, PhysicsNode>,
    alpha: number
  ): void {
    const cfg = this.config
    const nodeList = Array.from(nodes.values())
    
    for (let i = 0; i < nodeList.length; i++) {
      const node1 = nodeList[i]
      if (node1.isFixed) continue
      
      for (let j = i + 1; j < nodeList.length; j++) {
        const node2 = nodeList[j]
        
        const dx = node1.x - node2.x
        const dy = node1.y - node2.y
        const distance = Math.sqrt(dx * dx + dy * dy) || cfg.minDistance
        
        // 最小距离 = 两个节点半径之和
        const minDist = (node1.radius + node2.radius) * 1.2  // 增加20%安全边距
        
        if (distance < minDist) {
          // 计算重叠量
          const overlap = minDist - distance
          const force = overlap * cfg.collisionStrength * alpha
          
          // 分离力
          const fx = (dx / distance) * force
          const fy = (dy / distance) * force
          
          node1.vx += fx
          node1.vy += fy
          
          if (!node2.isFixed) {
            node2.vx -= fx
            node2.vy -= fy
          }
        }
      }
    }
  }

  /**
   * 引力：连接的节点相互吸引
   */
  private applyLinkForce(
    nodes: Map<string, PhysicsNode>,
    adjacency: Map<string, Set<string>>,
    alpha: number
  ): void {
    const cfg = this.config
    
    nodes.forEach((node, id) => {
      if (node.isFixed) return
      
      const neighbors = adjacency.get(id)
      if (!neighbors) return
      
      neighbors.forEach(neighborId => {
        const neighbor = nodes.get(neighborId)
        if (!neighbor) return
        
        const dx = neighbor.x - node.x
        const dy = neighbor.y - node.y
        const distance = Math.sqrt(dx * dx + dy * dy) || cfg.minDistance
        
        // 弹簧力：距离越远引力越大
        const displacement = distance - cfg.linkDistance
        const force = displacement * cfg.linkStrength * alpha
        
        // 归一化方向
        const fx = (dx / distance) * force
        const fy = (dy / distance) * force
        
        node.vx += fx
        node.vy += fy
      })
    })
  }

  /**
   * 中心力：防止整体漂移
   */
  private applyCenterForce(
    nodes: Map<string, PhysicsNode>,
    centerX: number,
    centerY: number,
    alpha: number
  ): void {
    const cfg = this.config
    
    nodes.forEach(node => {
      if (node.isFixed) return
      
      const dx = centerX - node.x
      const dy = centerY - node.y
      
      node.vx += dx * cfg.centerStrength * alpha
      node.vy += dy * cfg.centerStrength * alpha
    })
  }

  /**
   * 径向力：节点按层级围绕根节点分布
   */
  private applyRadialForce(
    nodes: Map<string, PhysicsNode>,
    rootId: string,
    centerX: number,
    centerY: number,
    alpha: number
  ): void {
    const cfg = this.config
    const rootNode = nodes.get(rootId)
    if (!rootNode) return
    
    nodes.forEach(node => {
      if (node.isFixed) return
      
      // 计算到根节点的距离
      const dx = node.x - centerX
      const dy = node.y - centerY
      const distance = Math.sqrt(dx * dx + dy * dy) || cfg.minDistance
      
      // 理想半径 = 层级 * 层级间距
      const idealRadius = 100 + node.level * 80
      
      // 径向力：拉向理想半径
      const displacement = distance - idealRadius
      const force = displacement * cfg.radialStrength * alpha
      
      if (distance > 0) {
        node.vx -= (dx / distance) * force
        node.vy -= (dy / distance) * force
      }
    })
  }

  /**
   * 更新节点位置
   */
  private updatePositions(
    nodes: Map<string, PhysicsNode>,
    alpha: number
  ): void {
    const cfg = this.config
    
    nodes.forEach(node => {
      if (node.isFixed) return
      
      // 应用阻尼
      node.vx *= cfg.damping
      node.vy *= cfg.damping
      
      // 限制最大速度
      const maxSpeed = 50 * alpha
      const speed = Math.sqrt(node.vx * node.vx + node.vy * node.vy)
      if (speed > maxSpeed) {
        node.vx = (node.vx / speed) * maxSpeed
        node.vy = (node.vy / speed) * maxSpeed
      }
      
      // 更新位置
      node.x += node.vx
      node.y += node.vy
      
      // 边界约束
      node.x = Math.max(50, Math.min(750, node.x))
      node.y = Math.max(50, Math.min(550, node.y))
    })
  }
}

// 注册布局引擎
layoutRegistry.register(new ForceDirectedLayout())

export default ForceDirectedLayout
