import { getElementWorldRect } from '../math'
import type { SceneElement, SnapLine, PlaceholderElement } from '../domino'

interface BoundingBox {
  x: number
  y: number
  width: number
  height: number
}

export function calculateSnap(
  selectedIds: string[],
  dragStartElements: Record<string, BoundingBox>,
  elements: Record<string, SceneElement>,
  placeholders: Record<string, PlaceholderElement>,
  totalDx: number,
  totalDy: number,
  scale: number,
  shouldSnap: boolean,
) {
  let snapOffsetX = 0
  let snapOffsetY = 0
  const activeSnapLines: SnapLine[] = []

  if (!shouldSnap || selectedIds.length === 0) {
    return { snapOffsetX, snapOffsetY, finalSnapLines: null }
  }

  // 1. 计算选中项在世界坐标系下的“理想包围盒” (无吸附时的位置)
  let minX = Infinity
  let minY = Infinity
  let maxX = -Infinity
  let maxY = -Infinity

  selectedIds.forEach(id => {
    const start = dragStartElements[id]
    const rawEl = elements[id] || placeholders[id]
    if (start && rawEl) {
      // 获取父级当前的绝对位置（如果父级也在动，这里能拿到动之后的位置）
      let parentWorldX = 0
      let parentWorldY = 0
      let curr = rawEl
      while (curr.parentId) {
        const parent = elements[curr.parentId]
        if (!parent) break
        parentWorldX += parent.x
        parentWorldY += parent.y
        curr = parent
      }

      // 理想世界位置 = 当前父级位置 + 初始局部位置 + 鼠标偏移
      const idealWorldX = parentWorldX + start.x + totalDx
      const idealWorldY = parentWorldY + start.y + totalDy

      minX = Math.min(minX, idealWorldX)
      minY = Math.min(minY, idealWorldY)
      maxX = Math.max(maxX, idealWorldX + rawEl.width)
      maxY = Math.max(maxY, idealWorldY + rawEl.height)
    }
  })

  const selectionRect = {
    x: minX,
    y: minY,
    width: maxX - minX,
    height: maxY - minY,
  }
  const threshold = 5 / scale // 5px 屏幕空间阈值

  // 2. 找到画布上所有“其他”元素的世界位置作为吸附目标
  const others = [
    ...Object.values(elements),
    ...Object.values(placeholders),
  ].filter(el => !selectedIds.includes(el.id) && el.type !== 'artboard')

  let bestDistX = threshold
  let bestDistY = threshold

  const movingBounds = {
    left: selectionRect.x,
    right: selectionRect.x + selectionRect.width,
    centerX: selectionRect.x + selectionRect.width / 2,
    top: selectionRect.y,
    bottom: selectionRect.y + selectionRect.height,
    centerY: selectionRect.y + selectionRect.height / 2,
  }

  others.forEach(other => {
    const worldOther = getElementWorldRect(other, elements)
    const otherBounds = {
      left: worldOther.x,
      right: worldOther.x + worldOther.width,
      centerX: worldOther.x + worldOther.width / 2,
      top: worldOther.y,
      bottom: worldOther.y + worldOther.height,
      centerY: worldOther.y + worldOther.height / 2,
    }

    // 比对 X 方向 (左、中、右)
    const mX = [movingBounds.left, movingBounds.centerX, movingBounds.right]
    const oX = [otherBounds.left, otherBounds.centerX, otherBounds.right]
    mX.forEach(mx => {
      oX.forEach(ox => {
        const d = Math.abs(mx - ox)
        if (d < bestDistX) {
          bestDistX = d
          snapOffsetX = ox - mx
          activeSnapLines.push({ type: 'x', value: ox })
        }
      })
    })

    // 比对 Y 方向 (上、中、下)
    const mY = [movingBounds.top, movingBounds.centerY, movingBounds.bottom]
    const oY = [otherBounds.top, otherBounds.centerY, otherBounds.bottom]
    mY.forEach(my => {
      oY.forEach(oy => {
        const d = Math.abs(my - oy)
        if (d < bestDistY) {
          bestDistY = d
          snapOffsetY = oy - my
          activeSnapLines.push({ type: 'y', value: oy })
        }
      })
    })
  })

  // 3. 过滤并返回最终的吸附线
  const finalSnapLines = activeSnapLines.filter(line => {
    if (line.type === 'x') {
      const currentL = minX + snapOffsetX
      const currentC = minX + (maxX - minX) / 2 + snapOffsetX
      const currentR = maxX + snapOffsetX
      return (
        Math.abs(line.value - currentL) < 0.01 ||
        Math.abs(line.value - currentC) < 0.01 ||
        Math.abs(line.value - currentR) < 0.01
      )
    } else {
      const currentT = minY + snapOffsetY
      const currentC = minY + (maxY - minY) / 2 + snapOffsetY
      const currentB = maxY + snapOffsetY
      return (
        Math.abs(line.value - currentT) < 0.01 ||
        Math.abs(line.value - currentC) < 0.01 ||
        Math.abs(line.value - currentB) < 0.01
      )
    }
  })

  return {
    snapOffsetX,
    snapOffsetY,
    finalSnapLines: finalSnapLines.length > 0 ? finalSnapLines : null,
  }
}
