import { ref, onMounted, onUnmounted, type Ref } from 'vue'

export interface SwipeSelectAdapter {
  isSelected: (id: number) => boolean
  select: (id: number) => void
  deselect: (id: number) => void
}

export function useSwipeSelect(
  containerRef: Ref<HTMLElement | null>,
  adapter: SwipeSelectAdapter
) {
  const isDragging = ref(false)

  let dragMode: 'select' | 'deselect' = 'select'
  let startRowIndex = -1
  let lastEndIndex = -1
  let startY = 0
  let initialSelectedSnapshot = new Map<number, boolean>()
  let cachedRows: HTMLElement[] = []
  let marqueeEl: HTMLDivElement | null = null

  function getDataRows(): HTMLElement[] {
    const container = containerRef.value
    if (!container) return []
    return Array.from(container.querySelectorAll('tbody tr[data-row-id]'))
  }

  function getRowId(el: HTMLElement): number | null {
    const raw = el.getAttribute('data-row-id')
    if (raw === null) return null
    const id = Number(raw)
    return Number.isFinite(id) ? id : null
  }

  // --- Marquee overlay ---
  function createMarquee() {
    marqueeEl = document.createElement('div')
    const isDark = document.documentElement.classList.contains('dark')
    Object.assign(marqueeEl.style, {
      position: 'fixed',
      background: isDark ? 'rgba(96, 165, 250, 0.15)' : 'rgba(59, 130, 246, 0.12)',
      border: isDark ? '1.5px solid rgba(96, 165, 250, 0.5)' : '1.5px solid rgba(59, 130, 246, 0.4)',
      borderRadius: '4px',
      pointerEvents: 'none',
      zIndex: '9999',
      transition: 'none'
    })
    document.body.appendChild(marqueeEl)
  }

  function updateMarquee(currentY: number) {
    if (!marqueeEl || !containerRef.value) return
    const containerRect = containerRef.value.getBoundingClientRect()

    const top = Math.min(startY, currentY)
    const bottom = Math.max(startY, currentY)

    // Clamp to container horizontal bounds, extend full width
    marqueeEl.style.left = containerRect.left + 'px'
    marqueeEl.style.width = containerRect.width + 'px'
    marqueeEl.style.top = top + 'px'
    marqueeEl.style.height = (bottom - top) + 'px'
  }

  function removeMarquee() {
    if (marqueeEl) {
      marqueeEl.remove()
      marqueeEl = null
    }
  }

  // --- Row selection logic ---
  function applyRange(endIndex: number) {
    const rangeMin = Math.min(startRowIndex, endIndex)
    const rangeMax = Math.max(startRowIndex, endIndex)
    const prevMin = lastEndIndex >= 0 ? Math.min(startRowIndex, lastEndIndex) : rangeMin
    const prevMax = lastEndIndex >= 0 ? Math.max(startRowIndex, lastEndIndex) : rangeMax

    const lo = Math.min(rangeMin, prevMin)
    const hi = Math.max(rangeMax, prevMax)

    for (let i = lo; i <= hi && i < cachedRows.length; i++) {
      const id = getRowId(cachedRows[i])
      if (id === null) continue

      if (i >= rangeMin && i <= rangeMax) {
        if (dragMode === 'select') {
          adapter.select(id)
        } else {
          adapter.deselect(id)
        }
      } else {
        const wasSelected = initialSelectedSnapshot.get(id) ?? false
        if (wasSelected) {
          adapter.select(id)
        } else {
          adapter.deselect(id)
        }
      }
    }

    lastEndIndex = endIndex
  }

  function onMouseDown(e: MouseEvent) {
    if (e.button !== 0) return

    const target = e.target as HTMLElement
    if (target.closest('button, a, input, select, textarea, [role="button"], [role="menuitem"]')) return
    if (!target.closest('tbody')) return

    cachedRows = getDataRows()
    const tr = target.closest('tr[data-row-id]') as HTMLElement | null
    if (!tr) return
    const rowIndex = cachedRows.indexOf(tr)
    if (rowIndex < 0) return

    const rowId = getRowId(tr)
    if (rowId === null) return

    initialSelectedSnapshot = new Map()
    for (const row of cachedRows) {
      const id = getRowId(row)
      if (id !== null) {
        initialSelectedSnapshot.set(id, adapter.isSelected(id))
      }
    }

    isDragging.value = true
    startRowIndex = rowIndex
    lastEndIndex = -1
    startY = e.clientY
    dragMode = adapter.isSelected(rowId) ? 'deselect' : 'select'

    applyRange(rowIndex)

    // Create visual marquee
    createMarquee()
    updateMarquee(e.clientY)

    e.preventDefault()
    document.body.style.userSelect = 'none'
    document.addEventListener('mousemove', onMouseMove)
    document.addEventListener('mouseup', onMouseUp)
  }

  function onMouseMove(e: MouseEvent) {
    if (!isDragging.value) return

    // Update marquee box
    updateMarquee(e.clientY)

    const el = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null
    if (!el) return

    const tr = el.closest('tr[data-row-id]') as HTMLElement | null
    if (!tr) return
    const rowIndex = cachedRows.indexOf(tr)
    if (rowIndex < 0) return

    applyRange(rowIndex)
    autoScroll(e)
  }

  function onMouseUp() {
    isDragging.value = false
    startRowIndex = -1
    lastEndIndex = -1
    cachedRows = []
    initialSelectedSnapshot.clear()
    stopAutoScroll()
    removeMarquee()
    document.body.style.userSelect = ''

    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
  }

  // --- Auto-scroll ---
  let scrollRAF = 0
  const SCROLL_ZONE = 40
  const SCROLL_SPEED = 8

  function autoScroll(e: MouseEvent) {
    cancelAnimationFrame(scrollRAF)
    const container = containerRef.value
    if (!container) return

    const rect = container.getBoundingClientRect()
    let dy = 0
    if (e.clientY < rect.top + SCROLL_ZONE) {
      dy = -SCROLL_SPEED
    } else if (e.clientY > rect.bottom - SCROLL_ZONE) {
      dy = SCROLL_SPEED
    }

    if (dy !== 0) {
      const step = () => {
        container.scrollTop += dy
        scrollRAF = requestAnimationFrame(step)
      }
      scrollRAF = requestAnimationFrame(step)
    }
  }

  function stopAutoScroll() {
    cancelAnimationFrame(scrollRAF)
  }

  onMounted(() => {
    containerRef.value?.addEventListener('mousedown', onMouseDown)
  })

  onUnmounted(() => {
    containerRef.value?.removeEventListener('mousedown', onMouseDown)
    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
    stopAutoScroll()
    removeMarquee()
  })

  return { isDragging }
}
