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

import { a11yProps } from '../utils/props/a11yProps'
import { componentPropType } from '../utils/props/componentPropType'
import { copyPropTypes } from '../utils/props/copyPropTypes'
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 { withLinkRouter } from '../utils/withLinkRouter'
import { applyTextStyles } from '../ThemeProvider/utils/styles'
import { useTheme } from '../ThemeProvider/useTheme'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { useViewport } from '../ViewportProvider/useViewport'
import { Box } from '../Box/Box'

import { usePagination } from './usePagination'
import { PaginationPageButton } from './PaginationPageButton'
import { SideButton } from './SideButton'

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

const selectTextStyles = ({ color, fontName, fontSize, fontWeight, lineHeight }, themeOptions) =>
  applyTextStyles({
    color,
    fontName,
    fontSize,
    fontWeight,
    lineHeight,
    themeOptions
  })

export const Pagination = React.forwardRef(
  (
    {
      children,
      copy = 'en',
      variant,
      tokens,
      sideButtonVariant,
      sideButtonTokens,
      LinkRouter,
      linkRouterProps,
      numberOfPagesBeforeEllipsis = 4,
      ...rest
    },
    ref
  ) => {
    const viewport = useViewport()
    const { truncateAbove, gap, ...themeTokens } = useThemeTokens('Pagination', tokens, variant, {
      viewport
    })
    const { themeOptions } = useTheme()

    const {
      borderColor,
      borderWidth,
      borderLeftWidth,
      borderRightWidth,
      borderTopWidth,
      borderBottomWidth,
      ellipsisPadding,
      ellipsisBorderRightWidth,
      ellipsisBorderLeftWidth,
      ellipsisBorderTopWidth,
      ellipsisBorderBottomWidth,
      ellipsisHeight
    } = themeTokens

    const items = React.Children.toArray(children)

    const {
      isItemActive,
      getItemProps,
      showPrevious,
      showNext,
      nextItemProps,
      previousItemProps,
      shouldRenderButton,
      shouldRenderEllipsis
    } = usePagination({
      items,
      truncateAbove,
      numberOfPagesBeforeEllipsis
    })

    const ellipsisTextStyles = selectTextStyles(themeTokens, themeOptions)
    const ellipisContainerStyles = StyleSheet.create({
      container: {
        height: ellipsisHeight,
        padding: ellipsisPadding,
        borderRightWidth: ellipsisBorderRightWidth,
        borderLeftWidth: ellipsisBorderLeftWidth,
        borderTopWidth: ellipsisBorderTopWidth,
        borderBottomWidth: ellipsisBorderBottomWidth,
        borderColor
      }
    })

    if (items.length === 0) {
      return null
    }

    // TODO: replace with a spacing component when https://github.com/telus/universal-design-system/pull/531 is implemented
    // concatenate all items to easily wrap them in identical spacing components
    const buttons = [
      showPrevious && (
        <SideButton
          {...previousItemProps}
          direction="previous"
          copy={copy}
          variant={sideButtonVariant}
          tokens={sideButtonTokens}
          LinkRouter={LinkRouter}
          linkRouterProps={linkRouterProps}
        />
      ),
      ...items.map((_child, itemIndex) => {
        const buttonLabel = `${itemIndex + 1}`
        const itemProps = getItemProps(itemIndex)
        const ItemLinkRouter = itemProps.LinkRouter ?? LinkRouter
        const itemLinkRouterProps = { ...linkRouterProps, ...itemProps.linkRouterProps }

        if (shouldRenderButton(itemIndex)) {
          return (
            <PaginationPageButton
              {...itemProps}
              LinkRouter={ItemLinkRouter}
              linkRouterProps={itemLinkRouterProps}
              label={buttonLabel}
              copy={copy}
              isActive={isItemActive(itemIndex)}
              key={buttonLabel}
            />
          )
        }

        if (shouldRenderEllipsis(itemIndex)) {
          return (
            <View key="..." style={{ ...ellipisContainerStyles.container }}>
              <Text
                style={{
                  ...ellipsisTextStyles
                }}
              >
                ...
              </Text>
            </View>
          )
        }

        return null
      }),
      showNext && (
        <SideButton
          {...nextItemProps}
          direction="next"
          copy={copy}
          variant={sideButtonVariant}
          tokens={sideButtonTokens}
          LinkRouter={LinkRouter}
          linkRouterProps={linkRouterProps}
        />
      )
    ]

    return (
      <View
        style={{
          ...staticStyles.container
        }}
        ref={ref}
        {...selectProps(rest)}
      >
        <View
          style={{
            borderColor,
            borderWidth,
            borderLeftWidth,
            borderRightWidth,
            borderTopWidth,
            borderBottomWidth,
            ...staticStyles.container
          }}
        >
          {buttons
            // keep the keys in-line with the page numbers regardless of which buttons are actually rendered
            .map((element, index) => [element, `page-${index + 1}`])
            .filter(([element]) => element !== null)
            .map(([element, key]) => (
              <Box right={gap} key={key}>
                {element}
              </Box>
            ))}
        </View>
      </View>
    )
  }
)

Pagination.displayName = 'Pagination'

Pagination.propTypes = {
  ...selectedSystemPropTypes,
  ...withLinkRouter.propTypes,
  /**
   * `Pagination.PageButton` elements rendered as the page buttons.
   */
  children: componentPropType('PageButton'),
  /**
   * To change the language for labels
   */
  copy: copyPropTypes,
  variant: variantProp.propType,
  tokens: getTokensPropType('Pagination'),
  /**
   * When passed as `{{ compact: true }}`, `Pagination` does not render labels along side buttons
   */
  sideButtonVariant: variantProp.propType,
  /**
   * Custom tokens for `PaginationSideButton`
   */
  sideButtonTokens: getTokensPropType('PaginationSideButton'),
  /**
   * The number of pages before the ellipsis is shown
   */
  numberOfPagesBeforeEllipsis: PropTypes.number
}

const staticStyles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    alignContent: 'stretch',
    alignItems: 'center'
  }
})
