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

import { ProgressBarBackground } from './ProgressBarBackground'
import { applyShadowToken } from '../ThemeProvider/utils/styles'
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 { MAX_PERCENT_VALUE, MIN_PERCENT_VALUE } from './constants'
import { ProgressContext } from './ProgressContext'

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

const selectBarStyles = ({ themeTokens, calculatedPercentage, barPosition, layers }) => {
  const { backgroundColor, borderRadius, outlineWidth, outlineColor, shadow } = themeTokens
  return {
    backgroundColor,
    borderRadius,
    outlineWidth,
    outlineColor,
    ...applyShadowToken(shadow),
    width: `${calculatedPercentage}%`,
    left: `${barPosition}%`,
    ...(layers ? { position: 'absolute' } : {})
  }
}

/**
 * The `ProgressBar` is a visual representation of linear progression.
 * It provides simple but important information at a quick glance.
 *
 * ## Props
 *
 * - Use `percentage` prop to indicate completion
 *
 * ## Tokens
 *
 * - Use the following tokens to customize the appearance:
 *   - `backgroundColor` for the background color of the progress bar,
 *   - `borderRadius` for the rounded corners,
 *   - `outlineColor` to control the color of the border (outline),
 *   - `outlineWidth` to change the outline width.
 *   - `shadow` to apply a box shadow to the progress bar.
 *
 * ## Variants
 *
 * - Use `variant={{ style: 'value' }}` to render specific types of progress bars:
 *   - `style: 'negative'` for negative theming for progress bar filling,
 *   - `style: 'inactive'` to style the progress bar as inactive.
 *
 * ## Usability and A11y guidelines
 *
 * `ProgressBar` component accepts all the standard accessibility properties including the ones that
 * are specific to the `progressbar` role. By default the following props are used:
 *
 * - `accessibilityRole` (`role`): `progressbar`,
 * - `accessibilityValue.min` (`aria-valuemin`): `0`,
 * - `accessibilityValue.max` (`aria-valuemax`): `100`,
 * - `accessibilityValue.now` (`aria-valuenow`): `percentage`,
 * - `accessibilityValue.text` (`aria-valuetext`): `%{percentage}%`,
 *
 */
export const ProgressBar = React.forwardRef(
  (
    {
      children = null,
      percentage = 0,
      tokens,
      variant,
      offset = { items: 0, current: 1 },
      ...rest
    },
    ref
  ) => {
    const { layers } = React.useContext(ProgressContext)
    const { items, current } = offset
    let calculatedPercentage = percentage
    let barPosition = MIN_PERCENT_VALUE
    const validItems = items > MIN_PERCENT_VALUE
    if (validItems) {
      calculatedPercentage = MAX_PERCENT_VALUE / items
      barPosition = calculatedPercentage * (current < 1 ? MIN_PERCENT_VALUE : current - 1)
      if (current > items) {
        barPosition = calculatedPercentage * (items - 1)
      }
    }

    const themeTokens = useThemeTokens('ProgressBar', tokens, variant)
    if (percentage < MIN_PERCENT_VALUE) {
      throw new Error('ProgressBar: `percentage` must be a positive number')
    } else if (percentage > MAX_PERCENT_VALUE) {
      throw new Error('ProgressBar: `percentage` cannot exceed 100')
    }
    const selectedProps = selectProps({
      accessibilityRole: 'progressbar',
      accessibilityValue: {
        min: MIN_PERCENT_VALUE,
        max: MAX_PERCENT_VALUE,
        now: calculatedPercentage.toFixed(2),
        text: `${
          validItems ? (barPosition + calculatedPercentage).toFixed(2) : calculatedPercentage
        }%`
      },
      ...rest
    })

    return percentage > MIN_PERCENT_VALUE || items ? (
      <View
        ref={ref}
        style={[
          staticStyles.bar,
          selectBarStyles({ themeTokens, calculatedPercentage, barPosition, layers })
        ]}
        {...selectedProps}
      >
        {children ?? <ProgressBarBackground variant={variant} />}
      </View>
    ) : null
  }
)
ProgressBar.displayName = 'ProgressBar'

ProgressBar.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Optional children that can be used to customize the progress bar filling.
   */
  children: PropTypes.node,
  /**
   * Accessibility label for the progress bar.
   */
  accessibilityLabel: PropTypes.string.isRequired,
  /**
   * Percentage of completion.
   */
  percentage: PropTypes.number,
  /**
   * Progress bar tokens.
   */
  tokens: getTokensPropType('ProgressBar'),
  /**
   * Progress bar variant.
   */
  variant: variantProp.propType,
  /**
   * Using the offset object, the `items` will determine the total number of items.
   * The `current` will determine the current position of the progress bar.
   */
  offset: PropTypes.shape({
    items: PropTypes.number,
    current: PropTypes.number
  })
}

const staticStyles = StyleSheet.create({
  bar: {
    height: '100%',
    overflow: 'hidden',
    position: 'relative',
    ...Platform.select({
      web: {
        outlineStyle: 'solid'
      }
    })
  }
})
