import * as React from 'react'

import {motion, HTMLMotionProps, useAnimationControls} from 'motion/react'

/**
 * Animated wrapper providing native-like tap feedback. You can use it instead of raw div + onClick.
 * @publicDocs
 */
export interface TouchableDocProps {
  /** Click handler */
  onClick?: React.MouseEventHandler<HTMLDivElement>
  /** Prevent click event from bubbling to parent elements */
  stopPropagation?: boolean
  /** Content to render inside the touchable area */
  children?: React.ReactNode
}

export interface TouchableProps extends HTMLMotionProps<'div'> {
  onClick?: React.MouseEventHandler<HTMLDivElement>
  stopPropagation?: boolean
}

export const Touchable = ({
  children,
  onClick,
  stopPropagation = false,
  ...props
}: TouchableProps) => {
  const ref = React.useRef<HTMLDivElement>(null)
  const controls = useAnimationControls()

  // Filter out props that shouldn't be passed to motion.div
  // Any other custom props that get added to the component interface should be filtered here
  const {ref: _, ...motionProps} = props

  const handleClick = React.useCallback(
    (event: React.MouseEvent<HTMLDivElement>) => {
      if (stopPropagation) event.stopPropagation()
      onClick?.(event)
    },
    [stopPropagation, onClick]
  )

  // Handle animations manually when stopPropagation is true to prevent parent from receiving event
  React.useEffect(() => {
    if (!stopPropagation || !ref.current) return

    const element = ref.current

    const handlePointerDown = (event: PointerEvent) => {
      event.stopImmediatePropagation()
      event.stopPropagation()

      // Animate to pressed state
      controls.start({
        opacity: 0.7,
        transition: {
          opacity: {type: 'tween', duration: 0.08, ease: 'linear'},
        },
      })
    }

    const handlePointerUp = (event: PointerEvent) => {
      event.stopImmediatePropagation()
      event.stopPropagation()

      // Animate back to normal state
      controls.start({
        opacity: 1,
        transition: {
          opacity: {type: 'tween', duration: 0.08, ease: 'linear'},
        },
      })
    }

    // Capture pointer event before Motion
    element.addEventListener('pointerdown', handlePointerDown, true)
    element.addEventListener('pointerup', handlePointerUp, true)

    return () => {
      element.removeEventListener('pointerdown', handlePointerDown, true)
      element.removeEventListener('pointerup', handlePointerUp, true)
    }
  }, [stopPropagation, controls])

  return (
    <motion.div
      ref={ref}
      data-touchable="true"
      className="flex w-full"
      animate={stopPropagation ? controls : undefined}
      whileTap={stopPropagation ? undefined : {opacity: 0.7}}
      transition={{
        opacity: {type: 'tween', duration: 0.08, ease: 'linear'},
      }}
      onClick={handleClick}
      style={{
        touchAction: stopPropagation ? 'manipulation' : undefined,
      }}
      {...motionProps}
    >
      {children}
    </motion.div>
  )
}
