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

import { RadioInput } from './RadioInput'
import { applyOuterBorder, applyShadowToken } from '../ThemeProvider/utils/styles'
import { resolveThemeTokens, validateThemeTokens } from '../ThemeProvider/utils/theme-tokens'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { a11yProps } from '../utils/props/a11yProps'
import { getTokenNames, getTokensSetPropType, selectTokens } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { viewProps } from '../utils/props/viewProps'

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

// TODO: roll out a standard approach to token sets
// https://github.com/telus/universal-design-system/issues/782
const tokenKeys = [
  'checkedBackgroundColor',
  'checkedSize',
  'inputBackgroundColor',
  'inputBorderColor',
  'inputBorderWidth',
  'inputOutlineColor',
  'inputOutlineWidth',
  'inputSize',
  'outerBorderColor',
  'outerBorderWidth',
  'outerBorderGap'
]

export const selectRadioButtonTokens = (themeTokens, prefix) =>
  selectTokens(tokenKeys, themeTokens, prefix)

/**
 * This component renders from its own resolved `Radio` theme, so every token set it can receive
 * belongs to the `Radio` schema: the narrow set a parent hands down (`tokenKeys`, a strict subset)
 * and, when a theme supplies an `icon`, the full resolved theme. Validating either against
 * `tokenKeys` alone reports the parent's icon tokens and every unrelated theme token as invalid.
 * Partial because callers pass a subset and brands resolve `icon` and `containerShadow` to `null`,
 * which the default validator rejects.
 */
const radioTokensValidator = getTokensSetPropType(getTokenNames('Radio'), { partial: true })

const getBorderRadius = (inputSize) => inputSize / 2

const selectCheckedStyles = ({ checkedBackgroundColor, checkedSize }) => ({
  backgroundColor: checkedBackgroundColor,
  borderRadius: checkedSize / 2,
  height: checkedSize,
  width: checkedSize
})

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

const selectInputIconStyles = (
  {
    iconBackgroundColor,
    inputBackgroundColor,
    inputBorderColor,
    inputBorderWidth,
    inputSize,
    containerShadow
  },
  isChecked
) => ({
  borderColor: inputBorderColor,
  borderRadius: getBorderRadius(inputSize),
  borderWidth: inputBorderWidth,
  backgroundColor: isChecked ? iconBackgroundColor : inputBackgroundColor,
  height: inputSize,
  width: inputSize,
  ...applyShadowToken(containerShadow)
})

const selectInputStyles = ({
  inputBackgroundColor,
  inputBorderColor,
  inputBorderWidth,
  inputOutlineColor,
  inputOutlineWidth,
  inputSize
}) => ({
  borderColor: inputBorderColor,
  borderRadius: getBorderRadius(inputSize),
  borderWidth: inputBorderWidth,
  backgroundColor: inputBackgroundColor,
  height: inputSize,
  width: inputSize,
  ...Platform.select({
    web: {
      outlineStyle: 'solid',
      outlineColor: inputOutlineColor,
      outlineWidth: inputOutlineWidth
    }
  })
})

const selectOuterBorderStyles = ({
  outerBorderColor,
  outerBorderWidth,
  outerBorderGap,
  inputSize
}) => ({
  ...applyOuterBorder({
    outerBorderColor,
    outerBorderWidth,
    outerBorderGap,
    borderRadius: getBorderRadius(inputSize)
  })
})

/**
 * The visual toggle of a radio input. Is not interactive on its own, should be used inside
 * an interactive parent that passes down props when interacted with.
 */
export const RadioButton = React.forwardRef(
  (
    {
      isChecked,
      tokens,
      inactive,
      defaultChecked,
      inputId,
      isControlled,
      handleChange,
      name: inputName,
      value,
      testID = 'Radio-Checked',
      ...rest
    },
    ref
  ) => {
    const radioTokens = useThemeTokens('Radio', tokens, {
      checked: isChecked
    })
    const { icon: IconComponent, ...iconTokens } = selectIconTokens(radioTokens)
    const themeTokens = validateThemeTokens(
      resolveThemeTokens(IconComponent ? radioTokens : tokens, {
        checked: isChecked
      }),
      radioTokensValidator,
      'RadioButton'
    )

    const getCheckedInput = (checked) => {
      if (!checked) return null
      if (!IconComponent) {
        return <View style={selectCheckedStyles(themeTokens)} testID={testID} />
      }
      return <IconComponent {...iconTokens} />
    }

    return (
      <View style={selectOuterBorderStyles(themeTokens)} {...selectProps(rest)}>
        <View
          style={[
            staticStyles.defaultInputStyles,
            IconComponent
              ? selectInputIconStyles(themeTokens, isChecked)
              : selectInputStyles(themeTokens)
          ]}
          testID="Radio-Input"
        >
          {/* Add a real input on Web, skip on native platforms */}
          <RadioInput
            ref={ref}
            checked={isChecked}
            defaultChecked={defaultChecked}
            disabled={inactive}
            id={inputId}
            isControlled={isControlled}
            onChange={handleChange}
            name={inputName}
            value={value}
          />
          {getCheckedInput(isChecked)}
        </View>
      </View>
    )
  }
)
RadioButton.displayName = 'RadioButton'

RadioButton.propTypes = {
  ...selectedSystemPropTypes,
  isChecked: PropTypes.bool,
  tokens: getTokensSetPropType(getTokenNames('Radio'), { partial: true, allowFunction: true }),
  inactive: PropTypes.bool,
  defaultChecked: PropTypes.bool,
  inputId: PropTypes.string,
  isControlled: PropTypes.bool,
  handleChange: PropTypes.func,
  name: PropTypes.string,
  value: PropTypes.bool
}

const staticStyles = StyleSheet.create({
  defaultInputStyles: { alignItems: 'center', justifyContent: 'center' }
})
