import React from 'react'
import PropTypes from 'prop-types'
import {
  Spacer,
  StackView,
  Typography,
  selectSystemProps,
  useViewport,
  useTheme,
  useThemeTokens,
  useResponsiveThemeTokens,
  createMediaQueryStyles,
  applyTextStyles,
  getTokensPropType,
  StyleSheet
} from '@telus-uds/components-base'
import { viewports } from '@telus-uds/system-constants'
import styled from 'styled-components'
// Reading these from the RN palette since they will be used to generate
// the `Typography` tokens
import { htmlAttrs, transformGradient } from '../utils'
import Segment from './Segment'
import useCountdown from './useCountdown'
import { countdownVariantPropType, dictionaryContentShape } from './types'

const DAYS = 'days'
const HOURS = 'hours'
const MINUTES = 'minutes'
const SECONDS = 'seconds'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])

const Container = styled.div(({ variant: { feature, inverse, large }, themeTokens, gradient }) => ({
  ...(large || feature ? { display: 'flex', flex: 0 } : {}),
  ...(feature && {
    borderRadius: themeTokens.containerBorderRadius,
    borderColor: themeTokens.containerBorderColor,
    borderStyle: 'solid',
    borderWidth: `${themeTokens.containerBorderWidth}px`,
    justifyContent: 'center',
    paddingBottom: `${themeTokens.containerPaddingBottomTop}px`,
    paddingLeft: `${themeTokens.containerPaddingLeftRight}px`,
    paddingRight: `${themeTokens.containerPaddingLeftRight}px`,
    paddingTop: `${themeTokens.containerPaddingBottomTop}px`,
    width: 'fit-content'
  }),
  ...(feature &&
    !inverse &&
    gradient && {
      background: `linear-gradient(#fff 0 0) padding-box, ${gradient} border-box`
    })
}))

const getLabelTokens = (themeTokens) => ({
  color: themeTokens.labelBorderColor,
  fontSize: themeTokens.labelFontSize,
  fontWeight: themeTokens.labelFontWeight,
  lineHeight: themeTokens.labelLineHeight
})

const getMainTextTokens = ({
  labelBorderColor,
  textTimerFontWeight,
  textFontSize,
  textLineHeight,
  textTimerFontName
}) => ({
  color: labelBorderColor,
  lineHeight: textLineHeight,
  fontWeight: textTimerFontWeight,
  fontSize: textFontSize,
  fontName: textTimerFontName
})

const Countdown = React.forwardRef(
  (
    {
      copy = 'en',
      targetTime,
      tokens,
      variant = {},
      units = [DAYS, HOURS, MINUTES, SECONDS],
      ...rest
    },
    ref
  ) => {
    const [days, hours, minutes, seconds] = useCountdown(targetTime)
    const viewport = useViewport()
    const { feature, large, noDivider } = variant

    const { themeOptions } = useTheme()

    const { enableMediaQueryStyleSheet } = themeOptions

    const useTokens = enableMediaQueryStyleSheet ? useResponsiveThemeTokens : useThemeTokens
    const resolvedTokens = useTokens(
      'Countdown',
      tokens,
      variant,
      !enableMediaQueryStyleSheet && { viewport }
    )
    const themeTokens = enableMediaQueryStyleSheet ? resolvedTokens[viewport] : resolvedTokens

    const segmentFontSize = themeTokens.textFontSize
    const semanticGradient =
      themeTokens.containerGradient && transformGradient(themeTokens.containerGradient)
    const mainTextTokens = getMainTextTokens(themeTokens)
    const labelTokens = getLabelTokens(themeTokens)

    let labelStyles
    let labelMediaIds
    let mainTextStyles
    let mainTextMediaIds
    let containerStyles
    let containerMediaIds

    if (enableMediaQueryStyleSheet) {
      const {
        transformedLabelThemeTokens,
        transformedMainTextThemeTokens,
        transformedContainerThemeTokens
      } = Object.entries(themeTokens).reduce(
        (acc, [vp, viewportTokens]) => {
          acc.transformedLabelThemeTokens[vp] = {
            fontSize: viewportTokens.labelFontSize,
            lineHeight: viewportTokens.labelLineHeight
          }
          acc.transformedMainTextThemeTokens[vp] = {
            fontSize: viewportTokens.textFontSize,
            lineHeight: viewportTokens.textLineHeight
          }
          acc.transformedContainerThemeTokens[vp] = {
            containerPaddingBottomTop: viewportTokens.containerPaddingBottomTop,
            containerPaddingLeftRight: viewportTokens.containerPaddingLeftRight
          }
          return acc
        },
        {
          transformedLabelThemeTokens: {},
          transformedMainTextThemeTokens: {},
          transformedContainerThemeTokens: {}
        }
      )

      const labelMediaQueryStyles = createMediaQueryStyles(transformedLabelThemeTokens)
      const mainTestMediaQueryStyles = createMediaQueryStyles(transformedMainTextThemeTokens)
      const containerMediaQueryStyles = createMediaQueryStyles(transformedContainerThemeTokens)

      const { ids, styles } = StyleSheet.create({
        label: {
          ...themeTokens,
          ...labelMediaQueryStyles
        },
        mainText: {
          ...themeTokens,
          ...mainTestMediaQueryStyles
        },
        container: {
          ...themeTokens,
          ...containerMediaQueryStyles
        }
      })
      labelStyles = getLabelTokens(styles.label)
      labelMediaIds = ids.label
      mainTextStyles = getMainTextTokens(styles.mainText)
      mainTextMediaIds = ids.mainText
      containerStyles = styles.container
      containerMediaIds = ids.container
    } else {
      labelStyles = labelTokens
      mainTextStyles = mainTextTokens
      containerStyles = themeTokens
    }

    const divider =
      noDivider === true ? (
        // StackView-based subcontainer adds a 1-step space on the each side of the divider
        <Spacer direction="row" space={(feature || large) && viewport !== viewports.xs ? 4 : 1} />
      ) : (
        <Typography tokens={mainTextTokens}>:</Typography>
      )

    const segmentProps = {
      copy,
      labelTokens: labelStyles,
      numberTokens: mainTextStyles,
      labelMediaIds,
      mainTextMediaIds,
      segmentFontSize,
      variant
    }

    return (
      // Making it focusable for accessibility purposes
      <Container
        ref={ref}
        variant={variant}
        {...selectProps(rest)}
        tabIndex={0}
        themeTokens={containerStyles}
        gradient={semanticGradient}
        media={containerMediaIds}
      >
        <StackView direction="row" space={1}>
          {units.map((unit, index) => {
            let number
            let labelKey
            switch (unit) {
              case DAYS:
                number = days
                labelKey = 'day'
                break
              case HOURS:
                number = hours
                labelKey = 'hour'
                break
              case MINUTES:
                number = minutes
                labelKey = 'minute'
                break
              case SECONDS:
                number = seconds
                labelKey = 'second'
                break
              default:
                return null
            }

            return (
              <React.Fragment key={unit}>
                <Segment
                  labelKey={labelKey}
                  number={number}
                  {...(unit === DAYS && { segmentWidth: String(number).length })}
                  {...segmentProps}
                  {...applyTextStyles(themeTokens)}
                />
                {index < units.length - 1 && divider}
              </React.Fragment>
            )
          })}
        </StackView>
      </Container>
    )
  }
)
Countdown.displayName = 'Countdown'

Countdown.propTypes = {
  ...selectedSystemPropTypes,
  tokens: getTokensPropType('Countdown'),
  /**
   * Copy language identifier (`'en'` or `'fr'`) or a dictionary instance (an object with
   * the following keys: days, day, hours, hour, minutes, minute, seconds, second)
   */
  copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
  /**
   * An instance of JavaScript `Date` object or a string parseable via `Date.parse()`
   * representing a point in the future to count down to.
   */
  targetTime: PropTypes.instanceOf(Date),
  variant: countdownVariantPropType,
  /**
   * An array of strings representing the units to display in the countdown.
   * Possible values: `'days'`, `'hours'`, `'minutes'`, `'seconds'`
   */
  units: PropTypes.arrayOf(PropTypes.oneOf([DAYS, HOURS, MINUTES, SECONDS]))
}

export default Countdown
