import React from 'react'

import { Platform } from 'react-native'
import { a11yProps } from '../utils/props/a11yProps'
import { focusHandlerProps, textInputHandlerProps } from '../utils/props/handlerProps'
import { getTokensPropType } from '../utils/props/tokens'
import { inputSupportsProps } from '../utils/props/inputSupportsProps'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { textInputProps } from '../utils/props/textInputProps'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { InputSupports } from '../InputSupports/InputSupports'
import { TextInputBase } from './TextInputBase'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { textInputPropTypes } from './propTypes'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([
  a11yProps,
  focusHandlerProps,
  inputSupportsProps,
  textInputHandlerProps,
  textInputProps,
  viewProps
])

/**
 * Use to collect long-form information such as product feedback or support queries.
 * Due to React Native's implementation of `TextInput` it's not possible to access the current value by passing a ref.
 * The `TextArea` height will automatically increase when new lines are added - this behavior can be limited within the theme.
 *
 * ## Controlled component
 * If it is required that the state of the `TextArea` be controlled by the application or other external methods,
 * `value` and `onChange` props must be passed to the `TextArea`.
 * If the `TextArea` value should not be changed by user input, a `readOnly` prop must be provided.
 *
 * ## Uncontrolled component
 * If it is not necessary to control the `TextArea` state, you can create one without a value prop.
 * In this case its value can be accessed within the `onChange` callback.
 *
 * ## Getting feedback for entered values
 * Use the `feedback` prop to give the user feedback regarding their input.
 * You can affirm that the user's input was correct, or highlight errors that must be corrected, using the `validation` prop.
 * Keep feedback text as brief as possible, should be limited to text and links.
 *
 * ## Supplying a hint
 * Use the `hint` prop to succinctly clarify attributes of the expected input data, such as the expected format,
 * or an indicator that the field is optional.
 * It is a more usable and accessible option than the HTML `placeholder` attribute.
 */
export const TextArea = React.forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
  const themeTokens = useThemeTokens('TextArea', tokens, variant)
  const [inputHeight, setInputHeight] = React.useState()

  const isUpdatingHeight = React.useRef(false)

  // adjust the text area's height as new lines are added to the content
  const onHeightChange = ({
    nativeEvent: {
      contentSize: { height }
    }
  }) => {
    // on native this is happens out of the box
    if (Platform.OS === 'web' && !isUpdatingHeight.current) {
      isUpdatingHeight.current = true
      setInputHeight(height)
      // setTimeout is used to “wait” for the next update cycle before allowing a new height adjustment,
      // avoiding multiple updates in the same render and preventing a possible infinite loop or constant re-renders.
      setTimeout(() => {
        isUpdatingHeight.current = false
      }, 0)
    } else {
      setInputHeight(null)
    }
  }

  const { supportsProps, ...selectedProps } = selectProps(rest)

  const inputProps = {
    ...selectedProps,
    multiline: true,
    onContentSizeChange: onHeightChange,
    height: inputHeight,
    tokens: themeTokens
  }

  return (
    <InputSupports {...supportsProps} inputValue={selectedProps?.value}>
      {({ inputId, ...props }) => {
        return (
          <TextInputBase
            ref={ref}
            nativeID={inputId}
            {...props}
            {...inputProps}
            variant={{ ...variant, validation: props.validation }}
          />
        )
      }}
    </InputSupports>
  )
})
TextArea.displayName = 'TextArea'

TextArea.propTypes = {
  ...selectedSystemPropTypes,
  ...textInputPropTypes,
  tokens: getTokensPropType('TextArea', 'TextInput'), // TODO: support both TextArea and TextInput tokens
  variant: variantProp.propType
}
