import React from 'react'
import PropTypes from 'prop-types'
import { Text, View, StyleSheet } from 'react-native'

import { Icon, iconComponentPropTypes } from '../Icon/Icon'
import { spacingProps } from '../utils/props/spacingProps'

export const MobileIconTextContent = React.forwardRef(
  ({ space = 0, iconPosition = 'left', icon: IconComponent, iconProps = {}, children }, ref) => {
    const [translateY, setTranslateY] = React.useState(0)
    const latestTranslateYRef = React.useRef(0)
    const layoutsRef = React.useRef({
      container: null,
      text: null,
      icon: null
    })

    const applyAlignment = React.useCallback(() => {
      const { container, text, icon } = layoutsRef.current

      if (!container || !icon || !icon.height) return

      const targetY = text ? text.y + text.height / 2 : container.height / 2
      const iconY = icon.y + icon.height / 2
      const nextTranslateY = Math.round((targetY - iconY) * 100) / 100

      if (!Number.isFinite(nextTranslateY)) return
      if (Math.abs(nextTranslateY - latestTranslateYRef.current) < 0.5) return

      latestTranslateYRef.current = nextTranslateY
      setTranslateY(nextTranslateY)
    }, [])

    const handleContainerLayout = React.useCallback(
      ({ nativeEvent: { layout } }) => {
        layoutsRef.current.container = layout
        applyAlignment()
      },
      [applyAlignment]
    )

    const handleTextLayout = React.useCallback(
      ({ nativeEvent: { layout } }) => {
        layoutsRef.current.text = layout
        applyAlignment()
      },
      [applyAlignment]
    )

    const handleIconLayout = React.useCallback(
      ({ nativeEvent: { layout } }) => {
        layoutsRef.current.icon = layout
        applyAlignment()
      },
      [applyAlignment]
    )

    const iconContent = IconComponent ? (
      <Icon ref={ref} icon={IconComponent} scalesWithText {...iconProps} />
    ) : null

    const iconWrapper = IconComponent ? (
      <View
        onLayout={handleIconLayout}
        style={[staticStyles.iconContainer, { transform: [{ translateY }] }]}
      >
        {iconContent}
      </View>
    ) : null

    if (iconPosition === 'inline') {
      return (
        <Text onLayout={handleContainerLayout}>
          <Text onLayout={handleTextLayout}>{children}</Text>{' '}
          <View style={staticStyles.inlineIconContainer}>{iconWrapper}</View>
        </Text>
      )
    }

    const iconSpaceStyle = iconPosition === 'left' ? { marginRight: space } : { marginLeft: space }

    return (
      <View onLayout={handleContainerLayout} style={staticStyles.rowContainer}>
        {iconPosition === 'left' && <View style={iconSpaceStyle}>{iconWrapper}</View>}
        <View onLayout={handleTextLayout}>{children}</View>
        {iconPosition === 'right' && <View style={iconSpaceStyle}>{iconWrapper}</View>}
      </View>
    )
  }
)

MobileIconTextContent.displayName = 'MobileIconTextContent'

MobileIconTextContent.propTypes = {
  /**
   * Amount of space between text and icon. Uses the theme spacing scale.
   */
  space: spacingProps.types.spacingValue,
  /**
   * Position of the icon relative to text.
   */
  iconPosition: PropTypes.oneOf(['left', 'right', 'inline']),
  /**
   * A valid UDS icon component imported from a UDS palette.
   */
  icon: PropTypes.elementType,
  /**
   * Props passed to the icon component.
   */
  iconProps: PropTypes.exact(iconComponentPropTypes),
  /**
   * Content rendered alongside the icon.
   */
  children: PropTypes.node
}

const staticStyles = StyleSheet.create({
  rowContainer: {
    flexDirection: 'row'
  },
  iconContainer: {
    alignSelf: 'flex-start'
  },
  inlineIconContainer: {
    position: 'absolute'
  }
})
