import React from 'react'
import PropTypes from 'prop-types'
import { Platform, StyleSheet, Text, View } from 'react-native'
import { StackView } from '../StackView/StackView'
import { applyTextStyles } from '../ThemeProvider/utils/styles'
import { useTheme } from '../ThemeProvider/useTheme'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { a11yProps } from '../utils/props/a11yProps'
import { getTokensPropType } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { useViewport } from '../ViewportProvider/useViewport'
import { useCopy } from '../utils/useCopy'
import { Step } from './Step'
import { dictionary as defaultDictionary } from './dictionary'

const STYLE_BAR_VARIANT = 'bar'

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

const selectContainerStyles = ({
  containerPaddingBottom,
  containerPaddingLeft,
  containerPaddingRight,
  containerPaddingTop
}) => ({
  paddingBottom: containerPaddingBottom,
  paddingLeft: containerPaddingLeft,
  paddingRight: containerPaddingRight,
  paddingTop: containerPaddingTop
})
const selectStepTrackerLabelContainerStyles = ({ labelMarginTop }) => ({
  marginTop: labelMarginTop
})
const selectStepTrackerLabelStyles = (
  { labelColor, labelFontSize, labelFontWeight, labelFontName, labelLineHeight },
  themeOptions
) =>
  applyTextStyles({
    color: labelColor,
    fontSize: labelFontSize,
    lineHeight: labelLineHeight,
    fontWeight: labelFontWeight,
    fontName: labelFontName,
    themeOptions
  })

const selectStepsContainerStyles = ({ barGap }) => ({
  gap: barGap
})

/**
 * StepTracker component shows the current position in a sequence of steps.
 *
 * ## Component API
 *
 * - `current` prop allows to control the current step, 0-based number;
 * - use `copy` and `dictionary` props to internationalize the labels;
 * - `steps` is and array of strings defining step titles;
 * - tokens and variant props define the appearance of the `StepTracker`:
 *
 * ## Usability and A11y guidelines
 *
 * By default this is a display-only component: it assumes the `progressbar`
 * role and the application must provide its own navigation mechanism and
 * state control. Pass an `onStepPress` callback to make each step a `button`
 * that reports its index when pressed (used by the Carousel to navigate to a
 * slide); supply `getStepPressLabel` so each step button has an accessible
 * label. When `onStepPress` is omitted the steps stay non-interactive. This
 * makes it extremely important to make sure you set a11y on controls used to
 * modify the state of the step tracker.
 *
 * You can pass standard a11y props to the `StepTracker` component, including
 * the ones that are specific to the `progressbar` role. By default the
 * following props are used:
 *
 * - `accessibilityRole` (`role`): `progressbar`,
 * - `accessibilityLabel` (`aria-label`): `Step I of N: <Current Step Label>`,
 * - `accessibilityLevel` (`aria-level`): 1,
 * - `accessibilityValue.min` (`aria-valuemin`): `0`,
 * - `accessibilityValue.max` (`aria-valuemax`): `steps.length - 1`,
 * - `accessibilityValue.now` (`aria-valuenow`): `current`,
 * - `accessibilityValue.text` (`aria-valuetext`): `<Current Step Label>`,
 *
 */
export const StepTracker = React.forwardRef(
  (
    {
      current = 0,
      copy = 'en',
      dictionary = defaultDictionary,
      steps = [],
      tokens,
      variant,
      onStepPress,
      getStepPressLabel,
      ...rest
    },
    ref
  ) => {
    const isBarVariant = variant?.style === STYLE_BAR_VARIANT
    const viewport = useViewport()
    const { showStepTrackerLabel, ...themeTokens } = useThemeTokens(
      'StepTracker',
      tokens,
      variant,
      {
        viewport
      }
    )
    const { textStepTrackerLabel } = themeTokens
    const getCopy = useCopy({ dictionary, copy })
    const stepTrackerLabel = showStepTrackerLabel
      ? (typeof copy === 'string'
          ? getCopy(textStepTrackerLabel ?? 1).stepTrackerLabel
          : getCopy('stepTrackerLabel')
        )
          .replace('%{stepNumber}', current < steps.length ? current + 1 : steps.length)
          .replace('%{stepCount}', steps.length)
          .replace(
            '%{stepLabel}',
            current < steps.length ? steps[current] : steps[steps.length - 1]
          )
      : getCopy('stepTrackerLabel')

    const getStepLabel = (index) => {
      if (themeTokens.showStepLabel) {
        return getCopy(index + 1)?.stepLabel.replace('%{stepNumber}', index + 1)
      }
      return ''
    }

    const { themeOptions } = useTheme()
    if (!steps.length) return null
    const selectedProps = selectProps({
      accessibilityLabel: stepTrackerLabel,
      accessibilityRole: 'progressbar',
      accessibilityValue: {
        min: 0,
        max: steps.length - 1,
        now: current,
        text: steps[current]
      },
      ...rest
    })

    const stepsContainerAccessibilityRole = Platform.OS === 'web' ? 'list' : 'tablist'
    const stepAccessibilityRole = Platform.OS === 'web' ? 'listitem' : 'tab'

    return (
      <View ref={ref} style={selectContainerStyles(themeTokens)} {...selectedProps}>
        <StackView space={0}>
          <View
            style={[staticStyles.stepsContainer, selectStepsContainerStyles(themeTokens)]}
            accessibilityRole={stepsContainerAccessibilityRole}
          >
            {steps.map((label, index) => {
              return (
                <Step
                  status={current}
                  key={label}
                  label={label}
                  name={getStepLabel(index)}
                  stepIndex={index}
                  stepCount={steps.length}
                  tokens={themeTokens}
                  accessibilityRole={stepAccessibilityRole}
                  accessibilityCurrent={current === index && Platform.OS === 'web' && 'step'}
                  isBarVariant={isBarVariant}
                  onStepPress={onStepPress}
                  pressLabel={
                    onStepPress && getStepPressLabel ? getStepPressLabel(index) : undefined
                  }
                />
              )
            })}
          </View>
          {showStepTrackerLabel && (
            <View
              style={[
                staticStyles.stepTrackerLabelContainer,
                selectStepTrackerLabelContainerStyles(themeTokens)
              ]}
            >
              <Text
                style={selectStepTrackerLabelStyles(themeTokens, themeOptions)}
                accessibilityRole="header"
                accessibilityLevel={2}
              >
                {stepTrackerLabel}
              </Text>
            </View>
          )}
        </StackView>
      </View>
    )
  }
)
StepTracker.displayName = 'StepTracker'

// If a language dictionary entry is provided, it must contain every key
const dictionaryContentShape = PropTypes.shape({
  stepLabel: PropTypes.string.isRequired,
  stepTrackerLabel: PropTypes.string.isRequired
})

StepTracker.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * The current step, 0-based number
   */
  current: PropTypes.number,
  /**
   * The language to use for the labels
   */
  copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
  /**
   * Custom dictionary containing the labels to use for the steps
   */
  dictionary: PropTypes.shape({
    en: dictionaryContentShape,
    fr: dictionaryContentShape
  }),
  /**
   * An array of strings defining the step titles
   */
  steps: PropTypes.arrayOf(PropTypes.string),
  tokens: getTokensPropType('StepTracker'),
  variant: variantProp.propType,
  /**
   * Called with the 0-based step index when a step is pressed. Providing it turns each step into a
   * `button`; omitting it keeps the StepTracker display-only.
   */
  onStepPress: PropTypes.func,
  /**
   * Returns the accessible label for the step button at the given 0-based index. Only used when
   * `onStepPress` is provided.
   */
  getStepPressLabel: PropTypes.func
}

const staticStyles = StyleSheet.create({
  stepsContainer: {
    alignItems: 'flex-start',
    flexDirection: 'row',
    justifyContent: 'space-evenly',
    flexGrow: 1
  },
  stepTrackerLabelContainer: { textAlign: 'center', flexDirection: 'row', justifyContent: 'center' }
})
