/* eslint-disable camelcase */
import React from 'react'
import { Text, TouchableWithoutFeedback } from 'react-native'
import PropTypes from 'prop-types'

/**
 * @typedef {import('react-native').PressableProps} PressableProps
 */

// TouchableWithoutFeedback and Pressable have similar but not identical props APIs
const pressablePropsToTouchable = ({
  unstable_pressDelay,
  android_disableSound,
  android_ripple, // Unsupported, discard it
  ...props
}) => ({
  ...props,
  touchSoundDisabled: android_disableSound,
  delayPressIn: unstable_pressDelay
})

/**
 * InlinePressable is an alternative to React Native's Pressable that works better when nested
 * inline inside Text. It accepts the same props as React Native's Pressable.
 *
 * There are a lot of React Native bugs around Views/Pressables nested in Text, e.g.:
 *
 * - https://github.com/facebook/react-native/issues/23601#issuecomment-468069822
 * - https://github.com/facebook/react-native/issues/30375
 * - https://github.com/facebook/react-native/issues/31955
 *
 * On Native, these can be avoided using a `Text` wrapped in a `TouchableWithoutFeedback`; the latter
 * injects additional handlers such as onPressIn to `Text`, resulting in a tree that is purely `Text`.
 *
 * Note that this should only be used on Native, not Web, because React Navigation's Web navigation
 * functions don't work in Touchables (but do work in Pressable) due to different event handling.
 *
 * @param {PressableProps} PressableProps
 */
export const InlinePressable = React.forwardRef(({ onPress, children, style, ...rest }, ref) => {
  const [isFocused, setIsFocused] = React.useState(false)
  const handleFocus = () => setIsFocused(true)
  const handleBlur = () => setIsFocused(false)

  const [isPressed, setIsPressed] = React.useState(false)
  const handlePressIn = () => setIsPressed(true)
  const handlePressOut = () => setIsPressed(false)

  const pressState = {
    pressed: isPressed,
    focus: isFocused, // limited support on native
    hover: false // not yet supported on native
  }
  const currentStyle = typeof style === 'function' ? style(pressState) : style

  return (
    <TouchableWithoutFeedback
      onPress={onPress}
      onPressIn={handlePressIn}
      onPressOut={handlePressOut}
      onFocus={handleFocus}
      onBlur={handleBlur}
      ref={ref}
      {...pressablePropsToTouchable(rest)}
    >
      <Text style={currentStyle}>
        {typeof children === 'function' ? children(pressState) : children}
      </Text>
    </TouchableWithoutFeedback>
  )
})

InlinePressable.displayName = 'InlinePressable'

InlinePressable.propTypes = {
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  style: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.func]),
  onPress: PropTypes.func
}
