import React from 'react'
import PropTypes from 'prop-types'
import {
  StyleSheet,
  Text,
  Dimensions,
  TouchableWithoutFeedback,
  Modal,
  View,
  SafeAreaView,
  ScrollView
} from 'react-native'

import { dictionary as defaultDictionary } from './dictionary'
import { getTokensPropType } from '../utils/props/tokens'
import { htmlAttrs } from '../utils/htmlAttrs'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { useCopy } from '../utils/useCopy'
import { viewProps } from '../utils/props/viewProps'
import { useViewport } from '../ViewportProvider/useViewport'
import { useTheme } from '../ThemeProvider/useTheme'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { Typography } from '../Typography/Typography'
import { Icon } from '../Icon/Icon'
import { OrderedList } from '../OrderedList/OrderedList'
import { OrderedListItem } from '../OrderedList/OrderedListItem'

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

const selectFootnoteStyle = ({
  screenHeight,
  footnoteBackground,
  isVisible,
  footnoteBorderTopSizeMd,
  footnoteBorderColorMd
}) => ({
  left: 0,
  backgroundColor: footnoteBackground,
  display: isVisible ? 'flex' : 'none',
  height: screenHeight,
  borderTopColor: footnoteBorderColorMd,
  borderTopWidth: footnoteBorderTopSizeMd
})

const selectFootnoteHeaderContentStyle = ({
  footnoteHeaderPaddingTop,
  footnoteHeaderPaddingBottom,
  footnoteHeaderPaddingRight,
  footnoteHeaderPaddingLeft,
  headerMargin
}) => ({
  paddingTop: footnoteHeaderPaddingTop,
  paddingBottom: footnoteHeaderPaddingBottom,
  paddingRight: footnoteHeaderPaddingRight,
  paddingLeft: footnoteHeaderPaddingLeft,
  marginRight: headerMargin
})

const selectFootnoteCloseButtonStyle = ({
  closeButtonBorderSize,
  closeButtonBorderColor,
  closeButtonHeight,
  closeButtonBackgroundColor,
  closeButtonMarginTop,
  closeButtonMarginRight,
  closeButtonMarginBottom,
  closeButtonMarginLeft,
  closeButtonWidth
}) => ({
  backgroundColor: closeButtonBackgroundColor,
  borderWidth: closeButtonBorderSize,
  borderColor: closeButtonBorderColor,
  height: closeButtonHeight,
  marginTop: closeButtonMarginTop,
  marginRight: closeButtonMarginRight,
  marginBottom: closeButtonMarginBottom,
  marginLeft: closeButtonMarginLeft,
  width: closeButtonWidth
})

const selectFootnoteBodyStyle = ({
  maxWidth,
  footnoteBodyBackground,
  footnoteBodyPaddingTop,
  footnoteBodyPaddingRight,
  footnoteBodyPaddingBottom,
  footnoteBodyPaddingLeft,
  screenWidth
}) => ({
  maxWidth,
  backgroundColor: footnoteBodyBackground,
  paddingTop: footnoteBodyPaddingTop,
  paddingRight: footnoteBodyPaddingRight * 2,
  paddingBottom: footnoteBodyPaddingBottom,
  paddingLeft: footnoteBodyPaddingLeft,
  width: screenWidth
})

const selectCustomContentFontStyle = ({
  listItemColor,
  listItemFontSize,
  listItemLineHeight,
  listItemPaddingLeft,
  listItemFontName,
  listItemFontWeight
}) => ({
  fontSize: listItemFontSize,
  lineHeight: listItemLineHeight * listItemFontSize,
  paddingLeft: listItemPaddingLeft,
  color: listItemColor,
  fontName: listItemFontName,
  fontWeight: listItemFontWeight
})

/**
 * Use `Footnote` to display a single legal content.
 *
 * ## Usage Criteria
 *
 * - Use `Footnote` to display a single legal statement
 * - Display on top of all UI, including other sticky elements such as Cart Summary
 * - Dismiss by clicking on the close button, clicking anywhere outside of the `Footnote`
 * - Use copy to set language, ‘en’ for English or ‘fr’ for French
 *
 * ## Accessibility requirements
 *
 * - Only one instance of `Footnote` should display at a time
 * - Place `Footnote` as the last element in the body or main
 * - When `Footnote` is open, the inert prop must be set on all children of body excluding the Footnote
 * - When `Footnote` is closed, focus must return to the initiating element
 */
export const Footnote = React.forwardRef(
  (
    {
      copy = 'en',
      number = undefined,
      content = undefined,
      onClose,
      isOpen = false,
      tokens,
      variant = {},
      dictionary = defaultDictionary,
      ...rest
    },
    ref
  ) => {
    const viewport = useViewport()

    const themeTokens = useThemeTokens('Footnote', tokens, variant, { viewport })
    const themeOptions = useTheme()

    const getCopy = useCopy({ dictionary, copy })

    const [isVisible, setIsVisible] = React.useState(false)

    const screenHeight = Dimensions.get('screen').height
    const screenWidth = Dimensions.get('screen').width

    const getFootnoteBodyContent = React.useCallback(() => {
      if (!number || !content) {
        return null
      }

      if (React.isValidElement(content)) {
        return <View style={selectCustomContentFontStyle(themeTokens)}>{content}</View>
      }

      return (
        <OrderedList start={number}>
          <OrderedListItem key={number}>
            <Text style={selectCustomContentFontStyle(themeTokens)}>{content}</Text>
          </OrderedListItem>
        </OrderedList>
      )
    }, [content, number, themeTokens])

    React.useEffect(() => {
      if (isOpen) {
        setIsVisible(true)
      }
    }, [isOpen])

    const closeFootnote = React.useCallback(
      (event, options) => {
        onClose(event, options)
        setIsVisible(false)
      },
      [onClose]
    )

    const handleClose = (event) => closeFootnote(event, { returnFocus: true })

    return (
      <View {...selectProps(rest)} ref={ref}>
        <Modal visible={isVisible} animationType="slide">
          <SafeAreaView style={staticStyles.container}>
            <ScrollView
              style={selectFootnoteStyle({
                screenHeight,
                isVisible,
                ...themeTokens
              })}
            >
              <View style={staticStyles.content}>
                <View
                  style={[
                    selectFootnoteHeaderContentStyle(themeTokens),
                    staticStyles.headerContent
                  ]}
                >
                  <Typography
                    tokens={{
                      fontSize: themeTokens?.headerFontSize,
                      lineHeight: themeTokens?.headerLineHeight
                    }}
                    variant={{ size: 'h4' }}
                  >
                    {getCopy('heading')}
                  </Typography>
                  <TouchableWithoutFeedback
                    onPress={handleClose}
                    accessibilityLabel={getCopy('close')}
                  >
                    <View
                      style={[
                        selectFootnoteCloseButtonStyle(themeTokens),
                        staticStyles.closeButton
                      ]}
                    >
                      <Icon
                        icon={themeTokens?.closeIcon}
                        tokens={{ size: themeTokens?.closeButtonIconSize }}
                      />
                    </View>
                  </TouchableWithoutFeedback>
                </View>
                <View
                  style={selectFootnoteBodyStyle({
                    maxWidth: themeOptions.contentMaxWidth,
                    screenWidth,
                    ...themeTokens
                  })}
                >
                  {getFootnoteBodyContent()}
                </View>
              </View>
            </ScrollView>
          </SafeAreaView>
        </Modal>
      </View>
    )
  }
)

Footnote.displayName = 'Footnote'

const copyShape = PropTypes.shape({
  close: PropTypes.string.isRequired,
  heading: PropTypes.string.isRequired
})

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

Footnote.propTypes = {
  ...selectedSystemPropTypes,
  tokens: getTokensPropType('Footnote'),
  /**
   * The content.
   */
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
  /**
   * 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 `heading` and `close`.
   */
  copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), copyShape]),
  /**
   * A boolean flag used hide or show the `Footnote`. Set to `true` to open the `Footnote`.
   */
  isOpen: PropTypes.bool,
  /**
   * The number, must match the number of the `FootnoteLink` that initiated the `Footnote`.
   */
  number: PropTypes.number,
  /**
   * A callback function to handle the closing of the footnote.
   *
   * @param {SyntheticEvent} event The React `SyntheticEvent`
   * @param {Object} options Custom options
   * @param {boolean} options.returnFocus Should the `Footnote` return focus on close
   */
  onClose: PropTypes.func.isRequired,
  /**
   * Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
   */
  dictionary: PropTypes.shape({
    en: dictionaryContentShape,
    fr: dictionaryContentShape
  })
}

const staticStyles = StyleSheet.create({
  container: {
    flex: 1
  },
  content: {
    marginLeft: 'auto',
    marginRight: 'auto',
    left: 0,
    right: 0,
    maxWidth: 1200,
    paddingBottom: 100
  },
  headerContent: {
    alignItems: 'center',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between'
  },
  closeButton: {
    alignItems: 'center',
    borderRadius: 50,
    display: 'flex',
    justifyContent: 'center'
  }
})
