import React from 'react'
import PropTypes from 'prop-types'
import {
  Box,
  Divider,
  selectSystemProps,
  Typography,
  useCopy,
  useThemeTokens,
  useViewport,
  getTokensPropType,
  useTheme,
  useResponsiveThemeTokens,
  createMediaQueryStyles,
  StyleSheet
} from '@telus-uds/components-base'
import styled from 'styled-components'
import ExpandCollapse from './ExpandCollapse'
import OrderedListBase from '../OrderedList/OrderedListBase'
import { htmlAttrs, media, renderStructuredContent } from '../utils'
import defaultDictionary from './dictionary'

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

const ContentContainer = styled.div(({ tokens }) => ({
  paddingBottom: tokens.contentPaddingBottom,
  paddingLeft: tokens.contentPaddingLeft,
  ...media().from('md').css({
    paddingBottom: tokens.mdContentPaddingBottom,
    paddingLeft: tokens.mdContentPaddingLeft
  })
}))

const Ordered = styled(OrderedListBase)(({ tokens }) => ({
  listStylePosition: 'outside',
  padding: tokens.orderedPadding
}))

const Unordered = styled.ul(({ tokens }) => ({
  listStyleType: 'none',
  margin: 0,
  padding: 0,
  paddingTop: tokens.unorderedPadding
}))

const ListItem = styled(OrderedListBase.Item)(({ tokens }) => ({
  display: 'list-item',
  '&::marker': {
    fontFamily: `${tokens.listFontName}${tokens.listFontWeight}normal`,
    fontSize: tokens.listFontSize,
    lineHeight: tokens.listLineHeight,
    textAlign: 'end !important'
  },
  color: tokens.listColor,
  fontFamily: `${tokens.listFontName}${tokens.listFontWeight}normal`,
  fontSize: tokens.listFontSize,
  lineHeight: tokens.listLineHeight,
  marginBottom: tokens.listMarginBottom,
  marginLeft: tokens.listMarginLeft,
  wordBreak: 'break-word'
}))

const NonIndexedContentTitle = styled.div(({ tokens }) => ({
  color: tokens.titleColor,
  fontSize: tokens.titleFontSize,
  lineHeight: tokens.titleLineHeight,
  paddingLeft: tokens.titlePaddingLeft
}))

/**
 * Use `TermsAndConditions` to display important legal content.
 *
 * ## Usage Criteria
 *
 * - Display before the page footer, except for call-to-action callback cards
 * - Responsive display based on breakpoints
 * - Use `copy` to set language, ‘en’ for English or ‘fr’ for French
 */
const TermsAndConditions = React.forwardRef(
  (
    {
      copy = 'en',
      indexedContent = [],
      nonIndexedContent = [],
      tokens = {},
      variant = {},
      dictionary = defaultDictionary,
      ...rest
    },
    ref
  ) => {
    const getCopy = useCopy({ dictionary, copy })
    const hasIndexedContent = indexedContent.length > 0
    const hasNonIndexedContent = nonIndexedContent.length > 0

    const viewport = useViewport()
    const { themeOptions } = useTheme()

    const { enableMediaQueryStyleSheet } = themeOptions

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

    let listItemStyles
    let listItemMediaIds
    let nonIndexedContentStyles
    let nonIndexedContentMediaIds

    if (enableMediaQueryStyleSheet) {
      const { transformedListItemThemeTokens, transformedNonIndexedContentThemeTokens } =
        Object.entries(themeTokens).reduce(
          (acc, [vp, viewportTokens]) => {
            acc.transformedListItemThemeTokens[vp] = {
              marginLeft: viewportTokens.listMarginLeft
            }
            acc.transformedNonIndexedContentThemeTokens[vp] = {
              paddingLeft: viewportTokens.titlePaddingLeft
            }
            return acc
          },
          {
            transformedListItemThemeTokens: {},
            transformedNonIndexedContentThemeTokens: {}
          }
        )

      const listItemMediaQueryStyles = createMediaQueryStyles(transformedListItemThemeTokens)
      const nonIndexedContentMediaQueryStyles = createMediaQueryStyles(
        transformedNonIndexedContentThemeTokens
      )

      const { ids, styles } = StyleSheet.create({
        listItem: {
          ...themeTokens[viewport],
          ...listItemMediaQueryStyles
        },
        nonIndexedContent: {
          ...themeTokens[viewport],
          ...nonIndexedContentMediaQueryStyles
        }
      })

      listItemStyles = styles.listItem
      listItemMediaIds = ids.listItem
      nonIndexedContentStyles = styles.nonIndexedContent
      nonIndexedContentMediaIds = ids.nonIndexedContent
    } else {
      listItemStyles = themeTokens
      nonIndexedContentStyles = themeTokens
    }

    return (
      <div {...selectProps(rest)}>
        <Divider tokens={{ color: themeTokens.dividerColor }} />
        <ExpandCollapse
          collapseTitle={getCopy('headingView')}
          expandTitle={getCopy('headingHide')}
          collapseLabel={getCopy('collapseLabel')}
          expandLabel={getCopy('expandLabel')}
          ref={ref}
        >
          <ContentContainer tokens={themeTokens}>
            {hasIndexedContent && (
              <Ordered tokens={themeTokens}>
                {indexedContent.map((contentItem, idx) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <ListItem tokens={listItemStyles} key={idx} media={listItemMediaIds}>
                    {renderStructuredContent(contentItem)}
                  </ListItem>
                ))}
              </Ordered>
            )}
            {hasNonIndexedContent && (
              <Box between={3}>
                <NonIndexedContentTitle
                  tokens={nonIndexedContentStyles}
                  media={nonIndexedContentMediaIds}
                >
                  <Typography
                    block
                    heading
                    tokens={{
                      fontName: themeTokens.expandTitleFontName,
                      fontWeight: themeTokens.expandTitleFontWeight,
                      fontSize: themeTokens.titleFontSize,
                      lineHeight: themeTokens.titleLineHeight
                    }}
                  >
                    {getCopy('nonIndexedTitle')}
                  </Typography>
                </NonIndexedContentTitle>
                <Unordered tokens={themeTokens}>
                  {nonIndexedContent.map((contentItem, idx) => (
                    // eslint-disable-next-line react/no-array-index-key
                    <ListItem tokens={listItemStyles} key={idx} media={listItemMediaIds}>
                      {renderStructuredContent(contentItem)}
                    </ListItem>
                  ))}
                </Unordered>
              </Box>
            )}
          </ContentContainer>
        </ExpandCollapse>
        <Divider tokens={{ color: themeTokens.dividerColor }} />
      </div>
    )
  }
)

TermsAndConditions.displayName = 'TermsAndConditions'

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

TermsAndConditions.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Use the `copy` prop to either select provided English or French copy by passing 'en' or 'fr' respectively.
   *
   * To provide your own, pass a JSON object with the keys `headingView`, `headingHide`, and `nonIndexedTitle`.
   */
  copy: PropTypes.oneOfType([
    PropTypes.oneOf(['en', 'fr']),
    PropTypes.shape({
      headingView: PropTypes.string,
      headingHide: PropTypes.string,
      nonIndexedTitle: PropTypes.string
    })
  ]),
  /**
   * Sets the tokens for TermsAndConditions element.
   */
  tokens: getTokensPropType('TermsAndConditions'),
  /**
   * An array of nodes, strings, or a combination to be displayed in an ordered list.
   *
   * Each item in the array must have a corresponding superscript in the page.
   */
  indexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string])),
  /**
   * An array of nodes, strings, or a combination to be displayed in an unordered list.
   *
   * nonIndexedContent do not have a corresponding superscript and instead apply to the page as a whole.
   */
  nonIndexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string])),
  /**
   * Custom dictionary containing the labels to use for `TermsAndConditions`
   */
  dictionary: PropTypes.shape({
    en: dictionaryContentShape,
    fr: dictionaryContentShape
  })
}

export default TermsAndConditions
