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

import { ToggleSwitch } from './ToggleSwitch'
import { Fieldset } from '../Fieldset/Fieldset'
import { getStackedContent } from '../StackView/getStackedContent'
import { useViewport } from '../ViewportProvider/useViewport'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { a11yProps } from '../utils/props/a11yProps'
import { containUniqueFields } from '../utils/containUniqueFields'
import { focusHandlerProps } from '../utils/props/handlerProps'
import { getTokensPropType } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { useMultipleInputValues } from '../utils/input'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
  a11yProps,
  focusHandlerProps,
  viewProps
])
export const ToggleSwitchGroup = React.forwardRef(
  (
    {
      copy = 'en',
      variant,
      tokens,
      items = [],
      values,
      initialValues,
      maxValues = 1,
      onChange,
      readOnly = false,
      inactive = false,
      feedback,
      hint,
      hintPosition = 'inline',
      tooltip,
      legend,
      name: inputGroupName,
      accessibilityRole = maxValues === 1
        ? 'radiogroup' // radiogroup is cross-platform; only web aria has generic groups
        : Platform.select({ web: 'group', default: 'none' }),
      toggleSwitchTokens,
      validation,
      ...rest
    },
    ref
  ) => {
    const viewport = useViewport()

    const { space, fieldSpace } = useThemeTokens('ToggleSwitchGroup', tokens, variant, {
      viewport
    })

    const { currentValues, toggleOneValue } = useMultipleInputValues({
      initialValues,
      values,
      maxValues,
      onChange,
      readOnly
    })

    const selectedProps = selectProps({
      accessibilityRole,
      ...rest
    })
    const itemA11yRole = selectedProps.accessibilityRole === 'radiogroup' ? 'radio' : 'switch'

    const uniqueFields = ['id', 'label']
    if (!containUniqueFields(items, uniqueFields)) {
      throw new Error(`ToggleSwitchGroup items must have unique ${uniqueFields.join(', ')}`)
    }

    const toggleSwitches = items.map(
      (
        {
          label,
          id = label,
          accessibilityLabel = label,
          onChange: itemOnChange,
          ref: itemRef,
          tooltip: itemTooltip,
          togglePosition,
          ...itemRest
        },
        index
      ) => {
        const isSelected = currentValues.includes(id)

        const handleChange = (newCheckedState, event) => {
          if (typeof itemOnChange === 'function') itemOnChange(newCheckedState, event)
          toggleOneValue(id, event)
        }

        const itemA11y = {
          accessibilityState: { checked: isSelected },
          accessibilityRole: itemA11yRole,
          accessibilityLabel,
          ...a11yProps.getPositionInSet(items.length, index)
        }

        return (
          <ToggleSwitch
            copy={copy}
            id={id}
            ref={itemRef}
            key={id}
            onChange={handleChange}
            tokens={toggleSwitchTokens}
            value={isSelected}
            inactive={inactive}
            label={label}
            tooltip={itemTooltip}
            togglePosition={togglePosition}
            {...itemA11y}
            {...selectItemProps(itemRest)}
          />
        )
      }
    )

    return (
      <Fieldset
        ref={ref}
        name={inputGroupName}
        legend={legend}
        tooltip={tooltip}
        hint={hint}
        hintPosition={hintPosition}
        space={fieldSpace}
        feedback={feedback}
        inactive={inactive}
        validation={validation}
        {...selectedProps}
      >
        {getStackedContent(toggleSwitches, { space, direction: 'column' })}
      </Fieldset>
    )
  }
)
ToggleSwitchGroup.displayName = 'ToggleSwitchGroup'

ToggleSwitchGroup.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Whether the English or French copy will be used (e.g. for accessibility labels).
   */
  copy: PropTypes.oneOf(['en', 'fr']),
  tokens: getTokensPropType('ToggleSwitchGroup'),
  variant: variantProp.propType,
  /**
   * Makes the current selection visible but prevents the user from changing it.
   */
  readOnly: PropTypes.bool,
  /**
   * If true, the switches cannot be interacted with, and if the theme supports `inactive`
   * appearance rules, these are applied.
   */
  inactive: PropTypes.bool,
  /**
   * The maximum number of items a user may select at once. Defaults to 1 and behaves
   * like radio buttons. To have no limit and allow any number of selections, pass `null`.
   */
  maxValues: PropTypes.number,
  /**
   * The options a user may select
   */
  items: PropTypes.arrayOf(
    PropTypes.shape({
      ...selectedItemPropTypes,
      /**
       * The text displayed to the user on the label.
       */
      label: PropTypes.string.isRequired,
      /**
       * An optional accessibility label may be passed to each ToggleSwitch
       * and will be applied as normal for a React Native accessibilityLabel prop.
       */
      accessibilityLabel: PropTypes.string,
      /**
       * An optional unique string may be provided to identify this option,
       * which will be used in code and passed to any onChange function.
       * If not provided, the label is used.
       */
      id: PropTypes.string,
      /**
       * An optional ref for one individual ToggleSwitch in the ToggleSwitchGroup
       */
      ref: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.any })]),
      /**
       * Controls the position of the switch in relation to the label.
       * - 'start': switch on the start, before the `label` and `tooltip`.
       * - 'end': switch to the end, after the `label` and `tooltip` (default).
       */
      togglePosition: PropTypes.oneOf(['start', 'end'])
    })
  ),
  /**
   * If provided, this function is called when the current selection is changed
   * and is passed an array of the `id`s of all currently selected `items`.
   */
  onChange: PropTypes.func,
  /**
   * If the selected item(s) in the toggle switch group are to be controlled externally by
   * a parent component, pass an array of strings as well as an `onChange` handler.
   * Passing an array for "values" makes the ToggleSwitchGroup a "controlled" component that
   * expects its state to be handled via `onChange` and so doesn't handle it itself.
   */
  values: PropTypes.arrayOf(PropTypes.string),
  /**
   * If `values` is not passed, making the ToggleSwitchGroup an "uncontrolled" component
   * managing its own selected state, a default set of selections may be provided.
   * Changing the `initialValues` does not change the user's selections.
   */
  initialValues: PropTypes.arrayOf(PropTypes.string),
  /**
   * Optional additional text giving more detail to help a user make a choice.
   */
  hint: PropTypes.string,
  /**
   * Position of the hint relative to label. Use `below` to display a larger hint below the label.
   */
  hintPosition: PropTypes.oneOf(['inline', 'below']),
  /**
   * Optional tooltip text content to include alongside the legend and hint.
   */
  tooltip: PropTypes.string,
  /**
   * If provided, a Feedback element is rendered containing this text.
   */
  feedback: PropTypes.string,
  /**
   * Main text used to describe this group, used in Fieldset's Legend element.
   */
  legend: PropTypes.string,
  /**
   * Toggle switch token overrides.
   */
  toggleSwitchTokens: getTokensPropType('ToggleSwitch'),
  /**
   * Current validation status of the group, passed to the feedback element if there is one.
   */
  validation: PropTypes.oneOf(['error', 'success'])
}
