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

import { applyTextStyles } from '../ThemeProvider/utils/styles'
import { useTheme } from '../ThemeProvider/useTheme'
import { useThemeTokensCallback } from '../ThemeProvider/useThemeTokens'
import { a11yProps } from '../utils/props/a11yProps'
import { focusHandlerProps } from '../utils/props/handlerProps'
import { getTokensPropType, selectTokens } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { useInputValue } from '../utils/input'
import { useUniqueId } from '../utils/useUniqueId'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { PressableCardBase, selectPressableCardTokens } from '../Card/PressableCardBase'
import { StackView } from '../StackView/StackView'
import { RadioButton, selectRadioButtonTokens } from '../Radio/RadioButton'

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

/**
 * A Card that behaves like a radio button. Use when users select a single choice from mutually exclusive options
 * with need to show additional information for each option. The whole card is interactive as one item.
 *
 * ## Component API
 *
 * Use `title` prop to provide a main description, and `children` for additional content. Avoid including
 * interactive elements within the RadioCard content.
 *
 * For a disabled `RadioCard`, set the `inactive` prop to `true`.
 *
 * ### Controlled version
 *
 * If the radio card is controlled from outside, it needs to receive `checked` and `onChange` props.
 *
 * ### Uncontrolled version
 *
 * In case of uncontrolled radio card you can use `defaultChecked` prop to provide the initial value.
 * Whenever the radio card gets toggled, it calls the `onChange` callback with the new value (boolean).
 *
 * ### Using within forms
 *
 * You can pass `name` and `value` props if you need a particular radio card to be a part of a radio group
 * on a form.
 *
 * ### Validation
 *
 * You can mark a radio card as failing validation by setting the `error` prop to `true`.
 *
 * ## A11y guidelines
 *
 * Radio card accepts all the common accessibility props, but also sets some defaults, including
 * accessibility role `'radio'` and accessibility state that depends on the other props (`checked`, `inactive`)
 * or the internal state in case of uncontrolled radio button.
 */
export const RadioCard = React.forwardRef(
  (
    {
      tokens,
      variant,
      title,
      children,
      inactive,
      defaultChecked,
      checked,
      name: inputName,
      value,
      id,
      onChange,
      ...rest
    },
    ref
  ) => {
    const {
      currentValue: isChecked,
      setValue: setIsChecked,
      isControlled
    } = useInputValue({
      value: checked,
      initialValue: defaultChecked,
      onChange
    })
    const handleChange = (event) => {
      if (!inactive && !isChecked) {
        setIsChecked(true, event)
      }
    }
    const uniqueId = useUniqueId('RadioCard')
    const inputId = id ?? uniqueId

    const getTokens = useThemeTokensCallback('RadioCard', tokens, variant)
    const getCardTokens = (cardState) => selectPressableCardTokens(getTokens(cardState))
    const { themeOptions } = useTheme()

    const selectedProps = selectProps({
      accessibilityRole: 'radio',
      accessibilityState: { checked: isChecked, disabled: inactive },
      ...rest
    })

    return (
      <PressableCardBase
        ref={ref}
        inactive={inactive}
        checked={isChecked}
        tokens={getCardTokens}
        onPress={handleChange}
        {...selectedProps}
      >
        {(cardState) => {
          const { radioSpace, contentSpace, ...themeTokens } = getTokens(cardState)
          const radioTokens = selectRadioButtonTokens(themeTokens, 'radio')
          const titleTokens = selectTokens('Typography', themeTokens)
          const textStyle = applyTextStyles({ ...titleTokens, themeOptions })
          return (
            <StackView direction="row" space={radioSpace}>
              <View style={[staticStyles.alignWithText, { height: textStyle.lineHeight }]}>
                <RadioButton
                  tokens={radioTokens}
                  isControlled={isControlled}
                  isChecked={isChecked}
                  inactive={inactive}
                  defaultChecked={defaultChecked}
                  inputId={inputId}
                  handleChange={handleChange}
                  name={inputName}
                  value={value}
                />
              </View>
              <StackView direction="column" space={contentSpace} tokens={{ flexShrink: 1 }}>
                {title && <Text style={textStyle}>{title}</Text>}
                {typeof children === 'function' ? children(cardState, textStyle) : children}
              </StackView>
            </StackView>
          )
        }}
      </PressableCardBase>
    )
  }
)
RadioCard.displayName = 'RadioCard'

RadioCard.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Content to be displayed at the top of the card alongside the radio button
   */
  title: PropTypes.string,
  /**
   * Additional content to be displayed below the button.
   */
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  /**
   * Use `checked` for controlled Radio. For uncontrolled Radio, use the `defaultChecked` prop.
   */
  checked: PropTypes.bool,
  /**
   * Use `defaultChecked` to provide the initial value for an uncontrolled Radio.
   */
  defaultChecked: PropTypes.bool,
  /**
   * An optional radio button description.
   */
  description: PropTypes.string,
  /**
   * Radio card button ID.
   */
  id: PropTypes.string,
  /**
   * Whether the corresponding input is disabled or active.
   */
  inactive: PropTypes.bool,
  /**
   * The label.
   */
  label: PropTypes.string,
  /**
   * Associate this radio card with a group (set as the name attribute).
   */
  name: PropTypes.string,
  /**
   * Whether the underlying input triggered a validation error or not.
   */
  error: PropTypes.bool,
  /**
   * The value. Must be unique within the group.
   */
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
  /**
   * Callback called when a controlled radio card gets interacted with.
   */
  onChange: PropTypes.func,
  /**
   * Radio card tokens.
   */
  tokens: getTokensPropType('RadioCard'),
  /**
   * Radio variant.
   */
  variant: variantProp.propType
}

const staticStyles = StyleSheet.create({
  alignWithText: {
    justifyContent: 'center'
  }
})
