import React from 'react'

import { View, Platform, StyleSheet } from 'react-native'
import PropTypes from 'prop-types'
import { viewports } from '@telus-uds/system-constants'
import { applyTextStyles, applyOuterBorder } from '../ThemeProvider/utils/styles'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { useTheme } from '../ThemeProvider/useTheme'
import { a11yProps } from '../utils/props/a11yProps'
import { componentPropType } from '../utils/props/componentPropType'
import { getTokensPropType } from '../utils/props/tokens'
import { inputSupportsProps } from '../utils/props/inputSupportsProps'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { useInputValue } from '../utils/input'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { htmlAttrs } from '../utils/htmlAttrs'
import { Picker } from './Picker'
import { InputSupports } from '../InputSupports/InputSupports'

import { ANDROID_VALIDATION_ICON_CONTAINER_OFFSET } from './constants'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([
  a11yProps,
  inputSupportsProps,
  viewProps,
  htmlAttrs
])

const selectInputStyles = (
  {
    backgroundColor,
    color,
    borderWidth = 0,
    borderColor,
    borderRadius,
    paddingTop,
    paddingBottom,
    paddingLeft = 0,
    paddingRight,
    fontName,
    fontSize,
    fontWeight,
    lineHeight,
    icon,
    iconSize = 0,
    validationIcon,
    validationIconSize = 0,
    height
  },
  themeOptions,
  inactive
) => {
  // Subtract border width from padding so overall input width/height doesn't
  // jump around if the border width changes (avoiding NaN and negative padding)
  const offsetBorder = (value) =>
    typeof value === 'number' ? Math.max(0, value - borderWidth) : value

  const textStyles = applyTextStyles({
    fontName,
    fontSize,
    lineHeight,
    fontWeight,
    themeOptions
  })

  const webStyles = Platform.select({
    web: {
      appearance: 'none',
      WebkitAppearance: 'none', // since iOS Safari needs a prefix
      outline: 'none',
      cursor: inactive ? 'not-allowed' : undefined,
      opacity: inactive ? 1 : undefined, // override Chrome's default fadeout of a disabled select
      // Enhanced fix for mobile dropdown positioning: restore native appearance on touch devices
      // Using multiple media queries and higher specificity to ensure it works in all contexts
      [`@media (pointer: coarse), (maxWidth: ${viewports.map.get(
        viewports.md
      )}px) and (hover: none)`]: {
        appearance: 'auto !important',
        WebkitAppearance: 'auto !important',
        backgroundImage: 'none !important'
      }
    }
  })

  let paddingWithIcons = paddingRight

  if (Platform.OS !== 'android') {
    if (icon) {
      paddingWithIcons += iconSize
    }

    if (validationIcon) {
      paddingWithIcons += validationIconSize
    }
  }

  return {
    backgroundColor,
    color,
    borderWidth,
    borderColor,
    borderRadius,
    paddingLeft: offsetBorder(paddingLeft),
    paddingRight: offsetBorder(paddingWithIcons),
    paddingTop: offsetBorder(paddingTop),
    paddingBottom: offsetBorder(paddingBottom),
    height,
    ...textStyles,
    ...webStyles
  }
}

const selectOuterBorderStyles = ({
  outerBackgroundColor,
  outerBorderWidth,
  outerBorderColor,
  borderRadius
}) => ({
  background: outerBackgroundColor,
  ...applyOuterBorder({
    outerBorderColor,
    outerBorderWidth,
    borderRadius
  })
})

const selectIconTokens = ({ iconSize, iconColor }) => ({
  size: iconSize,
  color: iconColor
})

const selectIconContainerStyles = ({ paddingRight, paddingBottom }) => ({
  paddingRight: paddingRight + 4,
  marginRight: -4,
  paddingBottom
})

const selectValidationIconTokens = ({ validationIconSize, validationIconColor }) => ({
  size: validationIconSize,
  color: validationIconColor
})

const selectValidationIconContainerStyles = ({
  icon,
  iconSize = 0,
  paddingRight = 0,
  paddingBottom
}) => ({
  paddingRight: icon ? paddingRight + iconSize + 4 : paddingRight,
  ...(Platform.OS === 'android'
    ? {
        paddingBottom: paddingBottom + ANDROID_VALIDATION_ICON_CONTAINER_OFFSET
      }
    : { paddingBottom })
})

const selectCustomFeedbackStyles = ({ feedbackBackgroundColor }) => ({
  backgroundColor: feedbackBackgroundColor
})

/**
 * A basic form single-choice select component. Use in forms or individually to receive user's input.
 *
 * ## Usage criteria
 * - Include a `placeholder` to provide instructions such as "Please select..." as an unselectable option (recommended)
 * - Use when options are between 7-15 (recommended)
 * - For options are 6 and under, use `Radio` groups or `ButtonGroup`.
 * - For options 15 and above, look for alternatives (e.g grouping into categories, or input field with auto-complete etc.)
 *
 * ## Controlled component
 * If it is required that the state of the `Select` be controlled by the application or other external methods,
 * `value` and `onChange` props must be passed to the `Select`.
 * If the `Select` value should not be changed by user input, a `readOnly` prop must be provided.
 *
 * ## Uncontrolled component
 * If it is not necessary to control the `Select` state, you can create one without a value prop.
 * In this case its value can be accessed within the `onChange` callback. Use the `initialValue` prop to set the
 * default value for an uncontrolled `Select`.
 *
 * ## Getting feedback for entered values
 * Use the `feedback` prop to give the user feedback regarding their selection.
 * You can affirm that the user's selection 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.
 *
 * ## Using a tooltip
 * When a more in-depth explanation is needed to fill out a `Select` properly, the tooltip prop may be used.
 *
 * ## Grouping options
 * `Select` supports option groups. Use a `SelectGroup` component to render an `<optgroup>` element with its items.
 * NOTE: this does not work on native platforms - the grouped items will be shown at the same level as the non-grouped items.
 *
 */
export const Select = React.forwardRef(
  (
    {
      value,
      initialValue,
      onChange,
      children,
      inactive,
      readOnly,
      placeholder,
      validation,
      tokens,
      variant,
      testID,
      ...rest
    },
    ref
  ) => {
    const { currentValue, setValue } = useInputValue({
      value,
      initialValue,
      onChange,
      readOnly
    })

    const [isFocused, setIsFocused] = React.useState(false)
    const handleFocus = () => setIsFocused(true)
    const handleBlur = () => setIsFocused(false)

    const [isHovered, setIsHovered] = React.useState(false)
    const handleMouseOver = () => setIsHovered(true)
    const handleMouseOut = () => setIsHovered(false)

    const [isOpen, setIsOpen] = React.useState(false)

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

    const themeTokens = useThemeTokens('Select', tokens, variant, {
      focus: isFocused,
      hover: isHovered,
      inactive,
      active: isOpen,
      validation
    })

    const { icon: IconComponent, validationIcon: ValidationIconComponent } = themeTokens

    const { themeOptions } = useTheme()

    return (
      <InputSupports
        {...supportsProps}
        {...selectedProps}
        validation={validation}
        feedbackTokens={selectCustomFeedbackStyles(themeTokens)}
      >
        {({ inputId, ...props }) => (
          <View style={selectOuterBorderStyles(themeTokens)}>
            <Picker
              ref={ref}
              style={selectInputStyles(themeTokens, themeOptions, inactive)}
              onFocus={handleFocus}
              onBlur={handleBlur}
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              onOpen={() => setIsOpen(true)}
              onClose={() => setIsOpen(false)}
              onChange={(e) => setValue(e, null)}
              value={currentValue}
              inactive={inactive}
              placeholder={
                placeholder !== undefined ? { label: placeholder, value: '' } : undefined
              }
              {...props}
              nativeID={inputId}
              testID={testID}
            >
              {children}
            </Picker>
            {ValidationIconComponent && (
              <View
                pointerEvents="none"
                style={[
                  staticStyles.iconContainer,
                  selectValidationIconContainerStyles(themeTokens)
                ]}
              >
                <ValidationIconComponent {...selectValidationIconTokens(themeTokens)} />
              </View>
            )}
            {IconComponent && (
              <View
                pointerEvents="none"
                style={[staticStyles.iconContainer, selectIconContainerStyles(themeTokens)]}
              >
                <IconComponent {...selectIconTokens(themeTokens)} />
              </View>
            )}
          </View>
        )}
      </InputSupports>
    )
  }
)
Select.displayName = 'Select'

Select.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Initial value for the uncontrolled version.
   */
  initialValue: PropTypes.string,
  /**
   * Current value to be set as selected.
   */
  value: PropTypes.string,
  /**
   * Callback to be called when the value changes.
   */
  onChange: PropTypes.func,
  /**
   * An array of items or item groups to be used as options.
   */
  children: componentPropType(['Item', 'Group']),
  /**
   * Whether the select is disabled.
   */
  inactive: PropTypes.bool,
  /**
   * Whether the select is read-only.
   */
  readOnly: PropTypes.bool,
  /**
   * A placeholder to provide instructions (such as "Please select...")
   * as an unselectable option
   */
  placeholder: PropTypes.string,
  /**
   * Select tokens.
   */
  tokens: getTokensPropType('Select'),
  /**
   * Select variant.
   */
  variant: variantProp.propType,
  /**
   * Dedicated ID for testing.
   */
  testID: PropTypes.string
}

const staticStyles = StyleSheet.create({
  iconContainer: {
    position: 'absolute',
    right: 0,
    bottom: 0
  }
})
