import React from 'react'
import PropTypes from 'prop-types'
import { View, Platform } from 'react-native'
import NativePicker from 'react-native-picker-select'

import { a11yProps } from '../utils/props/a11yProps'
import { componentPropType } from '../utils/props/componentPropType'
import { SelectGroup } from './SelectGroup'

import {
  ANDROID_HORIZONTAL_PADDING_OFFSET,
  ANDROID_DEFAULT_PADDING,
  ALLOW_FONT_SCALING,
  ADJUSTS_FONT_SIZE_TO_FIT,
  MAX_FONT_SIZE_MULTIPLIER
} from './constants'

// Styling of the native input is very limited, most of the styles have to be applied to an additional View
const selectAndroidInputStyles = ({ height = 0, color }) => ({
  height,
  paddingBottom: 0,
  paddingTop: 0,
  color
})

// The native input has a side padding of 8px, which can't be adjusted, so we have to account for that in the container
const selectAndroidContainerStyles = ({
  paddingLeft = ANDROID_DEFAULT_PADDING,
  paddingRight = ANDROID_DEFAULT_PADDING,
  ...rest
}) => ({
  ...rest,
  paddingLeft:
    paddingLeft > ANDROID_HORIZONTAL_PADDING_OFFSET
      ? paddingLeft - ANDROID_HORIZONTAL_PADDING_OFFSET
      : ANDROID_DEFAULT_PADDING,
  paddingRight:
    paddingRight > ANDROID_HORIZONTAL_PADDING_OFFSET
      ? paddingRight - ANDROID_HORIZONTAL_PADDING_OFFSET
      : ANDROID_DEFAULT_PADDING,
  paddingBottom: ANDROID_DEFAULT_PADDING,
  paddingTop: ANDROID_DEFAULT_PADDING
})

export const Picker = React.forwardRef(
  ({ value, onChange, onFocus, onBlur, style, inactive, children, placeholder, ...rest }, ref) => {
    // Ungroup items, since there's no way to support groups on native
    const flatChildren = React.Children.toArray(children).flatMap((child) => {
      if (child.type === SelectGroup) {
        return child.props.children
      }

      return child
    })

    const items = flatChildren.map(({ props }) => ({
      label: props.children,
      value: props.value
    }))

    // Unlike other input components, react-native-picker-select doesn't pass an event to its change fn.
    const handleChange = (newValue, index) => {
      if (onChange)
        onChange(newValue, {
          // TODO: see if it's possible to pass a real React SyntheticEvent here. For now, just pass an
          // object with a shape where if an onChange deconstructs `event.nativeEvent`, it won't crash.
          nativeEvent: {
            index,
            value: newValue
          }
        })
    }

    const picker = (
      <NativePicker
        pickerProps={{ ref }}
        touchableWrapperProps={a11yProps.select(rest)}
        onOpen={onFocus}
        onClose={onBlur}
        disabled={inactive}
        items={items}
        value={value}
        onValueChange={handleChange}
        useNativeAndroidPickerStyle={false}
        style={{
          inputIOS: style,
          inputAndroid: selectAndroidInputStyles(style)
        }}
        placeholder={placeholder !== undefined ? placeholder : {}}
        textInputProps={{
          allowFontScaling: ALLOW_FONT_SCALING,
          adjustFontSizeToFit: ADJUSTS_FONT_SIZE_TO_FIT,
          maxFontSizeMultiplier: MAX_FONT_SIZE_MULTIPLIER
        }}
      />
    )

    return (
      <>
        {Platform.OS === 'android' ? (
          <View style={selectAndroidContainerStyles(style)}>{picker}</View>
        ) : (
          picker
        )}
      </>
    )
  }
)
Picker.displayName = 'Picker'

Picker.propTypes = {
  value: PropTypes.string,
  onChange: PropTypes.func,
  onFocus: PropTypes.func,
  onBlur: PropTypes.func,
  style: PropTypes.object,
  inactive: PropTypes.bool,
  children: componentPropType(['Item', 'Group']),
  placeholder: PropTypes.shape({
    value: PropTypes.string,
    label: PropTypes.string
  })
}
