import { useRef, useState, type ReactNode } from 'react'
import { motion, useMotionValue, useTransform, type PanInfo } from 'framer-motion'

interface Action {
  label: string
  icon?: ReactNode
  /** Tailwind class set for the action button background. */
  tone: 'accent' | 'success' | 'danger' | 'neutral'
  onPress: () => void
}

interface Props {
  actions: Action[]
  children: ReactNode
  /** Pixels each action consumes; total reveal = actions.length * width. */
  actionWidth?: number
  /** Optional: skip rendering the reveal entirely (e.g. on desktop where
   *  swipe gestures are foreign). */
  enabled?: boolean
}

/**
 * Wraps a row in a horizontally-draggable surface that slides left to
 * reveal one or more action buttons in the right gutter. The actions
 * snap into place when the user drags past 50% of the reveal width;
 * otherwise the row springs back closed.
 *
 * Designed for touch + trackpad horizontal pan (framer-motion's drag
 * gesture handles both). Pointer-down is suppressed when revealed so a
 * tap on an action doesn't bubble up to the row's primary onClick.
 */
export default function SwipeReveal({
  actions,
  children,
  actionWidth = 84,
  enabled = true,
}: Props) {
  const containerRef = useRef<HTMLDivElement>(null)
  const x = useMotionValue(0)
  const [open, setOpen] = useState(false)
  const totalReveal = actions.length * actionWidth

  function onDragEnd(_: any, info: PanInfo) {
    const offset = info.offset.x + info.velocity.x * 0.05
    if (offset < -totalReveal / 2) {
      x.set(-totalReveal)
      setOpen(true)
    } else {
      x.set(0)
      setOpen(false)
    }
  }

  const overlayOpacity = useTransform(x, [-totalReveal, 0], [1, 0])

  if (!enabled || actions.length === 0) {
    return <>{children}</>
  }

  return (
    <div ref={containerRef} className="relative overflow-hidden">
      {/* Action buttons in the right gutter - revealed by sliding the row left. */}
      <div
        className="absolute inset-y-0 right-0 flex"
        style={{ width: totalReveal }}
        aria-hidden={!open}
      >
        {actions.map((a, i) => (
          <button
            key={i}
            type="button"
            onClick={(e) => {
              e.stopPropagation()
              a.onPress()
              x.set(0)
              setOpen(false)
            }}
            className={`flex flex-col items-center justify-center gap-1 text-[10.5px] font-semibold uppercase tracking-[0.1em] text-white px-1 ${actionToneClass(a.tone)}`}
            style={{ width: actionWidth }}
          >
            {a.icon}
            {a.label}
          </button>
        ))}
      </div>
      <motion.div
        drag="x"
        dragDirectionLock
        dragConstraints={{ left: -totalReveal, right: 0 }}
        dragElastic={0.1}
        dragMomentum={false}
        onDragEnd={onDragEnd}
        // touch-action: pan-y tells the browser that vertical scroll
        // should win over horizontal pan, so the user can scroll the
        // episode list naturally without the swipe gesture stealing
        // touches.
        style={{ x, touchAction: 'pan-y' }}
        animate={{ x: open ? -totalReveal : 0 }}
        transition={{ type: 'spring', stiffness: 380, damping: 32 }}
        className="relative bg-void"
      >
        {/* Subtle shadow + dim while peeking, fully solid when closed. */}
        <motion.div
          aria-hidden
          className="absolute inset-0 pointer-events-none bg-black/15"
          style={{ opacity: overlayOpacity }}
        />
        {children}
      </motion.div>
    </div>
  )
}

function actionToneClass(tone: Action['tone']): string {
  switch (tone) {
    case 'accent':
      return 'bg-accent text-void'
    case 'success':
      return 'bg-emerald-600 hover:bg-emerald-500'
    case 'danger':
      return 'bg-red-600 hover:bg-red-500'
    default:
      return 'bg-elevated/80 hover:bg-elevated text-text-1'
  }
}
