import React from 'react'
import PropTypes from 'prop-types'

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'
import { StackView } from '../StackView/StackView'
import { StackWrap } from '../StackView/StackWrap'
import { CheckboxCard } from '../CheckboxCard/CheckboxCard'
import { Fieldset } from '../Fieldset/Fieldset'

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

export const CheckboxCardGroup = React.forwardRef(
  (
    {
      copy = 'en',
      tokens,
      checkboxCardTokens,
      variant,
      items = [],
      legend,
      tooltip,
      hint,
      hintPosition = 'inline',
      validation,
      feedback,
      initialCheckedIds,
      checkedIds,
      onChange,
      readOnly,
      name: inputGroupName,
      inactive,
      ...rest
    },
    ref
  ) => {
    const viewport = useViewport()
    const {
      space,
      fieldSpace,
      direction,
      showIcon,
      outlineWidth,
      borderTopLeftRadius,
      borderTopRightRadius,
      borderBottomLeftRadius,
      borderBottomRightRadius,
      outlineOffset
    } = useThemeTokens('CheckboxCardGroup', tokens, variant, {
      viewport
    })

    const borderTokens = {
      outlineWidth,
      borderTopLeftRadius,
      borderTopRightRadius,
      borderBottomLeftRadius,
      borderBottomRightRadius,
      outlineOffset
    }

    const { currentValues, toggleOneValue } = useMultipleInputValues({
      value: checkedIds,
      initialValues: initialCheckedIds,
      onChange,
      readOnly: readOnly || inactive
    })

    const StackContainer = direction === 'row' ? StackWrap : StackView
    // Needs 'checkboxgroup' role on direct parent of checkboxes for MacOS Voiceover's numbering to work,
    // and also needs 'checkboxgroup' role on fieldset for correct description on focusing the set.
    // TODO: test this on more web screen readers.

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

    return (
      <Fieldset
        copy={copy}
        ref={ref}
        name={inputGroupName}
        legend={legend}
        tooltip={tooltip}
        hint={hint}
        hintPosition={hintPosition}
        space={fieldSpace}
        feedback={feedback}
        inactive={inactive || readOnly}
        validation={validation}
        tokens={borderTokens}
        showErrorBorder
        showIcon={showIcon}
        accessibilityRole="checkbox"
        {...selectProps(rest)}
      >
        {(checkboxCardProperties) => (
          <StackContainer space={space} accessibilityRole="checkbox">
            {items.map(({ title, content, id, onChange: itemOnChange, ...itemRest }, index) => {
              const cardId = id || `CheckboxCard[${index}]`
              const handleChange = (newCheckedState, event) => {
                if (typeof itemOnChange === 'function') itemOnChange(newCheckedState, event)
                toggleOneValue(cardId, event)
              }

              return (
                <CheckboxCard
                  key={cardId}
                  id={cardId}
                  checked={currentValues.includes(cardId)}
                  onChange={handleChange}
                  inactive={inactive}
                  title={title}
                  name={inputGroupName}
                  tokens={checkboxCardTokens}
                  variant={variant}
                  readOnly={readOnly}
                  {...selectItemProps(itemRest)}
                  {...checkboxCardProperties}
                >
                  {content}
                </CheckboxCard>
              )
            })}
          </StackContainer>
        )}
      </Fieldset>
    )
  }
)
CheckboxCardGroup.displayName = 'CheckboxCardGroup'

CheckboxCardGroup.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Whether the English or French copy will be used (e.g. for accessibility labels).
   */
  copy: PropTypes.oneOf(['en', 'fr']),
  /**
   * Optional theme token overrides for the outer CheckboxCardGroup component
   */
  tokens: getTokensPropType('CheckboxCardGroup'),
  /**
   * Optional theme token overrides for each inner CheckboxCard component
   */
  checkboxCardTokens: getTokensPropType('CheckboxCard'),
  /**
   * Theme variants, shared between both CheckboxCardGroup and CheckboxCard
   */
  variant: variantProp.propType,
  /**
   * Array of objects containing specifics for each CheckboxCard to be rendered in the group.
   */
  items: PropTypes.arrayOf(
    PropTypes.exact({
      ...selectedItemPropTypes,
      title: PropTypes.string,
      content: PropTypes.node,
      id: PropTypes.string,
      onChange: PropTypes.func
    })
  ),
  /**
   * Main text used to describe this group, used in Fieldset's Legend element.
   */
  legend: 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,
  /**
   * Current validation status of the group, passed to the feedback element if there is one.
   */
  validation: PropTypes.oneOf(['error', 'success']),
  /**
   * If provided, a Feedback element is rendered containing this text.
   */
  feedback: PropTypes.string,
  /**
   * If provided, the checkboxed with this ids are selected on first render.
   */
  initialCheckedIds: PropTypes.arrayOf(PropTypes.string),
  /**
   * If not undefined, the checkboxes with these ids is selected, and the
   * element's selection state will be controlled by its parent using the `onChange` function.
   */
  checkedIds: PropTypes.arrayOf(PropTypes.string),
  /**
   * Function to call on change in selection state. Is required if the selection state is controlled
   * by a parent using checkedId and the input is not readOnly.
   */
  onChange: PropTypes.func,
  /**
   * If true, the checkbox cards cannot be selected by the user and simply show their current state.
   */
  readOnly: PropTypes.bool,
  /**
   * If true, the checkbox card cannot be interacted with, elements are set as `disabled` and if the
   * theme supports `inactive` appearances rules, these are applied.
   */
  inactive: PropTypes.bool,
  /**
   * On Web, this is passed to the `name` attribute of the fieldset and each checkbox input.
   */
  name: PropTypes.string
}
