import React from 'react'
import PropTypes from 'prop-types'
import { View, StyleSheet, Platform } from 'react-native'
import { viewports } from '@telus-uds/system-constants'
import { Spacer } from '../Spacer/Spacer'
import { a11yProps } from '../utils/props/a11yProps'
import { getTokensPropType } from '../utils/props/tokens'
import { responsiveProps } from '../utils/props/responsiveProps'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { spacingProps } from '../utils/props/spacingProps'
import { useResponsiveProp, resolveResponsivePropByViewport } from '../utils/useResponsiveProp'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import {
  StyleSheet as StyleSheetUtils,
  createMediaQueryStyles
} from '../utils/ssr-media-query/ssr-media-query'
import { useAllViewportTokens } from '../utils/ssr-media-query/utils/use-all-viewport-tokens'
import { useTheme } from '../ThemeProvider/useTheme'

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

/**
 * @typedef {import('../utils/props/spacingProps.js').SpacingIndex} SpacingIndex
 * @typedef {import('../utils/props/spacingProps.js').SpacingObject} SpacingObject
 */

/**
 * The border a divider draws for one orientation: a vertical divider draws its right edge and a
 * horizontal one its bottom edge, each coloured and sized from the resolved theme tokens.
 *
 * @param {boolean} vertical
 * @param {{ color: string, width: number }} tokens
 * @returns {object}
 */
const selectBorderStyles = (vertical, { color, width }) =>
  vertical
    ? { borderRightColor: color, borderRightWidth: width }
    : { borderBottomColor: color, borderBottomWidth: width }

/**
 * A basic divider component, horizontal by default. Color and thickness can be controlled by theme.
 * Divider is based on the flexbox model and uses `alignSelf: flex` to span its flex parent.
 *
 * ## Vertical
 *
 * Vertical dividers can be created by passing a `vertical` boolean prop.
 * Note that for a vertical divider the parent flex element **must** have flex direction set to row.
 * This is the default for web but not for React Native.
 *
 * In a flexbox row, vertical dividers will automatically size to their parent height.
 *
 * `vertical` is an optionally responsive prop and may be an object keyed by viewports, for a divider
 * inside a container whose own direction changes between viewports, such as a `StackView` with a
 * responsive `direction`. With the `enableMediaQueryStyleSheet` theme option the orientation is
 * emitted as CSS media rules, so a server render does not draw the line on the wrong edge until
 * hydration. The `aria-orientation` attribute has no CSS equivalent and follows the JavaScript
 * viewport.
 *
 * ## Space
 *
 * Use this to create **space either side of the divider**. For simple cases, pass a number referring to
 * a position on the theme's spacing scale; for example, this will put a Spacer of the smallest supported
 * size either side of the divider:
 *
 * ```jsx
 * <Divider space={1} />
 * ```
 *
 * `space` prop uses `useSpacingScale` and may accept a {@link SpacingObject} or a {@link SpacingIndex} number.
 *
 * To **reduce the length of a divider** as well as creating space between it and its neighbours, wrap it in
 * a `Box` component. For example, this will have the second-smallest theme-supported spacing value between
 * the dividing line and its neighbours, and will shorten the line at either end by the same amount:
 *
 * ```jsx
 * <Box space={2}><Divider /></Box>
 * ```
 *
 * ## Accessibility
 *
 * For accessibility purposes a divider component will be described with ARIA attributes, i.e. `role="separator"` and `aria-orientation="vertical/horizontal"`.
 */
export const Divider = React.forwardRef(
  ({ variant, vertical: verticalProp = false, space, tokens, dataSet, ...rest }, ref) => {
    const {
      themeOptions: { enableMediaQueryStyleSheet }
    } = useTheme()
    const allTokens = useAllViewportTokens('Divider', tokens, variant)
    const vertical = useResponsiveProp(verticalProp, false)

    let borderStyles
    let dataSetValue = dataSet
    let spacerDirection = vertical ? 'row' : 'column'

    if (enableMediaQueryStyleSheet) {
      const verticalByViewport = resolveResponsivePropByViewport(verticalProp, false)
      const stylesByViewport = viewports.keys.reduce(
        (byViewport, viewport) => ({
          ...byViewport,
          [viewport]: selectBorderStyles(verticalByViewport[viewport], allTokens[viewport])
        }),
        {}
      )

      const { ids, styles } = StyleSheetUtils.create({
        divider: createMediaQueryStyles(stylesByViewport)
      })

      borderStyles = styles.divider
      dataSetValue = { media: ids.divider, ...dataSet }
      spacerDirection = viewports.keys.reduce(
        (byViewport, viewport) => ({
          ...byViewport,
          [viewport]: verticalByViewport[viewport] ? 'row' : 'column'
        }),
        {}
      )
    } else {
      borderStyles = selectBorderStyles(vertical, allTokens.current)
    }

    /**
     * Web has aria attributes for dividers and native has no equivalent, so they are only set on web.
     * Early versions of React Native Web support `aria-orientation` while later versions only support
     * `accessibilityOrientation` and map it, so both are supplied.
     */
    const a11y =
      Platform.OS === 'web'
        ? {
            'aria-orientation': vertical ? 'vertical' : 'horizontal',
            accessibilityOrientation: vertical ? 'vertical' : 'horizontal',
            accessibilityRole: 'separator'
          }
        : {}

    const { testID, ...selectedProps } = selectProps({ ...a11y, ...rest })

    const divider = (
      <View
        ref={ref}
        style={[staticStyles.divider, borderStyles]}
        testID={testID}
        dataSet={dataSetValue}
        {...selectedProps}
      />
    )
    if (!space) return divider
    const spacerProps = { space, direction: spacerDirection }
    return (
      <>
        <Spacer {...spacerProps} testID={testID ? `${testID}-Spacer-before` : undefined} />
        {divider}
        <Spacer {...spacerProps} testID={testID ? `${testID}-Spacer-after` : undefined} />
      </>
    )
  }
)
Divider.displayName = 'Divider'

Divider.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Overrides for the theme's `Divider` tokens, `color` and `width`.
   */
  tokens: getTokensPropType('Divider'),
  /**
   * A number referring to a position on the space scale to create space on either side of the divider
   */
  space: spacingProps.types.spacingValue,
  /**
   * Theme variant, such as `{ weight: 'thick' }`, `{ decorative: true }` or `{ inverse: true }`.
   */
  variant: variantProp.propType,
  /**
   * By default, the divider is a horizontal line the full width of its parent.
   * With `vertical` prop and when inside a container with { flexDirection: row }, draws a full height vertical line.
   *
   * Optionally responsive: an object keyed by viewports, for a divider inside a container whose
   * direction changes between viewports.
   */
  vertical: responsiveProps.getTypeOptionallyByViewport(PropTypes.bool),
  /**
   * Data attributes to be applied to the element. When media query stylesheet is enabled,
   * this will include media query IDs for responsive styling.
   */
  dataSet: PropTypes.object
}

const staticStyles = StyleSheet.create({
  divider: {
    alignSelf: 'stretch'
  }
})
