import {useState} from 'react'
import {View} from 'react-native'

import {useTheme} from '#/alf'
import {DotGrid2x3_Stroke2_Corner0_Rounded as GripIcon} from '#/components/icons/DotGrid'

/**
 * Web implementation of SortableList using pointer events.
 * See SortableList.tsx for the native version using gesture-handler + Reanimated.
 */

interface SortableListProps<T> {
  data: T[]
  keyExtractor: (item: T) => string
  renderItem: (item: T, dragHandle: React.ReactNode) => React.ReactNode
  onReorder: (data: T[]) => void
  onDragStart?: () => void
  onDragEnd?: () => void
  /** Fixed row height used for position math. */
  itemHeight: number
}

export function SortableList<T>({
  data,
  keyExtractor,
  renderItem,
  onReorder,
  onDragStart,
  onDragEnd,
  itemHeight,
}: SortableListProps<T>) {
  const t = useTheme()
  const [dragState, setDragState] = useState<{
    activeIndex: number
    currentY: number
    startY: number
  } | null>(null)

  const getNewPosition = (state: {
    activeIndex: number
    currentY: number
    startY: number
  }) => {
    const translationY = state.currentY - state.startY
    const rawNewPos = Math.round(
      (state.activeIndex * itemHeight + translationY) / itemHeight,
    )
    return Math.max(0, Math.min(rawNewPos, data.length - 1))
  }

  const handlePointerMove = (e: React.PointerEvent) => {
    if (!dragState) return
    e.preventDefault()
    setDragState(prev => (prev ? {...prev, currentY: e.clientY} : null))
  }

  const handlePointerUp = () => {
    if (!dragState) return
    const newPos = getNewPosition(dragState)
    if (newPos !== dragState.activeIndex) {
      const next = [...data]
      const [moved] = next.splice(dragState.activeIndex, 1)
      next.splice(newPos, 0, moved)
      onReorder(next)
    }
    setDragState(null)
    onDragEnd?.()
  }

  const handlePointerDown = (e: React.PointerEvent, index: number) => {
    e.preventDefault()
    ;(e.target as HTMLElement).setPointerCapture(e.pointerId)
    setDragState({activeIndex: index, currentY: e.clientY, startY: e.clientY})
    onDragStart?.()
  }

  const newPos = dragState ? getNewPosition(dragState) : -1

  return (
    <View
      style={[
        {height: data.length * itemHeight, position: 'relative'},
        t.atoms.bg_contrast_25,
      ]}
      // @ts-expect-error web-only pointer events
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerCancel={handlePointerUp}>
      {data.map((item, index) => {
        const isActive = dragState?.activeIndex === index

        // Clamp translation so the item stays within list bounds.
        const rawTranslationY = isActive
          ? dragState.currentY - dragState.startY
          : 0
        const translationY = isActive
          ? Math.max(
              -index * itemHeight,
              Math.min(rawTranslationY, (data.length - 1 - index) * itemHeight),
            )
          : 0

        // Non-dragged items shift to make room for the dragged item.
        let offset = 0
        if (dragState && !isActive) {
          const orig = dragState.activeIndex
          if (orig < newPos && index > orig && index <= newPos) {
            offset = -itemHeight
          } else if (orig > newPos && index < orig && index >= newPos) {
            offset = itemHeight
          }
        }

        const dragHandle = (
          <div
            onPointerDown={(e: React.PointerEvent<HTMLDivElement>) =>
              handlePointerDown(e, index)
            }
            style={{
              display: 'flex',
              justifyContent: 'center',
              alignItems: 'center',
              paddingLeft: 8,
              paddingRight: 8,
              paddingTop: 12,
              paddingBottom: 12,
              cursor: isActive ? 'grabbing' : 'grab',
              touchAction: 'none',
              userSelect: 'none',
            }}>
            <GripIcon
              size="lg"
              fill={t.atoms.text_contrast_medium.color}
              style={{pointerEvents: 'none'} as any}
            />
          </div>
        )

        return (
          <View
            key={keyExtractor(item)}
            style={[
              {
                position: 'absolute',
                top: index * itemHeight,
                left: 0,
                right: 0,
                height: itemHeight,
                transform: [{translateY: isActive ? translationY : offset}],
                scale: isActive ? 1.03 : 1,
                zIndex: isActive ? 999 : 0,
                boxShadow: isActive ? '0 2px 12px rgba(0,0,0,0.06)' : 'none',
                // Animate scale/shadow on pickup, and transform for
                // non-dragged items shifting into place.
                transition: isActive
                  ? 'box-shadow 200ms ease, scale 200ms ease'
                  : dragState
                    ? 'transform 200ms ease'
                    : 'none',
              } as any,
            ]}>
            {renderItem(item, dragHandle)}
          </View>
        )
      })}
    </View>
  )
}
