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

import { applyShadowToken } from '../ThemeProvider/utils/styles'
import { getTokensPropType } from '../utils/props/tokens'
import { responsiveProps } from '../utils/props/responsiveProps'
import { useResponsiveProp } from '../utils/useResponsiveProp'
import { formatImageSource } from '../utils/formatImageSource'
import { a11yProps } from '../utils/props/a11yProps'
import { viewProps } from '../utils/props/viewProps'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { SetBackgroundImage } from './backgroundImage/SetBackgroundImage'
import { getAdjustedBorderRadius } from './backgroundImage/helpers'
import { staticStyles as backgroundImageStaticStyles } from './backgroundImage/styles'
import { selectBackgroundLayerStyles, isOverlayColor } from './composition'
import { FlexGrid } from '../FlexGrid/FlexGrid'
import { FlexGridRow } from '../FlexGrid/FlexGridRow/FlexGridRow'
import { FlexGridCol } from '../FlexGrid/FlexGridCol/FlexGridCol'

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

const GRID_COLUMNS = 12

const selectPaddedContentStyles = ({ paddingTop, paddingBottom, paddingLeft, paddingRight }) => ({
  paddingTop,
  paddingBottom,
  paddingLeft,
  paddingRight
})

const selectInteractiveOverlayStyles = ({ backgroundColor, borderRadius, borderWidth }) => {
  const adjustedBorderRadius = getAdjustedBorderRadius({ borderRadius, borderWidth })
  return {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor,
    borderRadius: adjustedBorderRadius,
    pointerEvents: 'none',
    zIndex: 3
  }
}

const selectOuterContainerStyles = ({
  containerStyle,
  backgroundColor,
  borderRadius,
  borderColor,
  borderWidth,
  hasGradientToken
}) => ({
  ...containerStyle,
  backgroundColor,
  borderRadius,
  ...(hasGradientToken ? {} : { borderColor, borderWidth })
})

// Builds the resolved Card style by composing the Layer 4 (background) styles.
export const selectStyles = (resolvedTokens, { hasBgImage = false } = {}) => {
  const {
    flex,
    borderColor,
    borderRadius,
    borderWidth,
    paddingBottom,
    paddingLeft,
    paddingRight,
    paddingTop,
    marginTop,
    marginBottom,
    marginLeft,
    marginRight,
    minWidth,
    shadow,
    maxHeight,
    overflowY
  } = resolvedTokens

  const { backgroundColor, ...gradientStyles } = selectBackgroundLayerStyles(resolvedTokens, {
    hasBgImage
  })

  return {
    flex,
    backgroundColor,
    borderColor,
    borderRadius,
    borderWidth,
    paddingBottom,
    paddingLeft,
    paddingRight,
    paddingTop,
    marginTop,
    marginBottom,
    marginLeft,
    marginRight,
    minWidth,
    ...applyShadowToken(shadow),
    ...gradientStyles,
    ...(Platform.OS === 'web' ? { maxHeight, overflowY } : {})
  }
}

/**
 * A themeless base component for Card which components can apply theme tokens to. Not
 * intended to be used in apps or sites directly: build themed components on top of this.
 */
export const CardBase = React.forwardRef(
  (
    { children, tokens, dataSet, backgroundImage, fullBleedContent, cardState, testID, ...rest },
    ref
  ) => {
    const resolvedTokens = typeof tokens === 'function' ? tokens(cardState) : tokens
    const hasBgImage = Boolean(backgroundImage && backgroundImage.src)
    const cardStyle = selectStyles(resolvedTokens, { hasBgImage })
    const props = selectProps(rest)

    let content = children

    const { src = '', alt = '', resizeMode = '', position = '', align = '' } = backgroundImage || {}
    const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover')
    const backgroundImagePosition = useResponsiveProp(position)
    const backgroundImageAlign = useResponsiveProp(align)
    const imageSourceViewport = formatImageSource(useResponsiveProp(src))

    const {
      content: fullBleedImageContent,
      position: fullBleedContentPosition,
      imgCol
    } = fullBleedContent || {}
    const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom')

    if (backgroundImage && src) {
      const {
        paddingTop,
        paddingBottom,
        paddingLeft,
        paddingRight,
        borderWidth,
        borderColor,
        borderRadius,
        backgroundColor,
        ...containerStyle
      } = cardStyle

      const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
      const hasInteractiveOverlay = isOverlayColor(backgroundColor)
      const outerBackgroundColor = hasInteractiveOverlay ? undefined : backgroundColor
      const hasGradientToken = Boolean(resolvedTokens.gradient) && Platform.OS === 'web'

      const paddedContent = hasPadding ? (
        <View
          style={selectPaddedContentStyles({
            paddingTop,
            paddingBottom,
            paddingLeft,
            paddingRight
          })}
        >
          {children}
        </View>
      ) : (
        children
      )

      const contentWithOverlay = (
        <>
          <View style={backgroundImageStaticStyles.contentOverlay}>{paddedContent}</View>
          {hasInteractiveOverlay && Platform.OS === 'web' && (
            <View
              style={selectInteractiveOverlayStyles({
                backgroundColor,
                borderRadius,
                borderWidth
              })}
              testID={testID && `${testID}-card-base-bg-overlay`}
            />
          )}
        </>
      )

      content = SetBackgroundImage({
        src: imageSourceViewport,
        alt,
        backgroundImageResizeMode,
        backgroundImagePosition,
        backgroundImageAlign,
        content: contentWithOverlay,
        cardStyle: { borderRadius, borderWidth },
        testID: testID && `${testID}-card-base-bg-image`
      })

      return (
        <View
          style={selectOuterContainerStyles({
            containerStyle,
            backgroundColor: outerBackgroundColor,
            borderRadius,
            borderColor,
            borderWidth,
            hasGradientToken
          })}
          dataSet={dataSet}
          ref={ref}
          testID={testID}
          {...props}
        >
          {content}
        </View>
      )
    }

    if (fullBleedContent && fullBleedImageContent) {
      const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
      const imageColumns = imgCol || { xs: GRID_COLUMNS }

      const textColumns = {}
      Object.keys(imageColumns).forEach((breakpoint) => {
        textColumns[breakpoint] = GRID_COLUMNS - (imageColumns[breakpoint] || GRID_COLUMNS)
      })

      const imageFirst = fullBleedPosition === 'top' || fullBleedPosition === 'left'

      const imageColContent = (
        <FlexGridCol {...imageColumns} style={staticStyles.fullBleedImageCol}>
          {fullBleedImageContent}
        </FlexGridCol>
      )

      const textColContent = (
        <FlexGridCol
          {...textColumns}
          style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}
        >
          {children}
        </FlexGridCol>
      )

      return (
        <View style={containerStyle} dataSet={dataSet} ref={ref} testID={testID} {...props}>
          <FlexGrid>
            <FlexGridRow>
              {imageFirst ? (
                <>
                  {imageColContent}
                  {textColContent}
                </>
              ) : (
                <>
                  {textColContent}
                  {imageColContent}
                </>
              )}
            </FlexGridRow>
          </FlexGrid>
        </View>
      )
    }

    return (
      <View style={cardStyle} dataSet={dataSet} ref={ref} testID={testID} {...props}>
        {content}
      </View>
    )
  }
)
CardBase.displayName = 'CardBase'

export const fullBleedContentPropTypes = PropTypes.shape({
  content: PropTypes.node.isRequired,
  alt: PropTypes.string,
  position: responsiveProps.getTypeOptionallyByViewport(
    PropTypes.oneOf(['bottom', 'left', 'right', 'top'])
  ),
  align: responsiveProps.getTypeOptionallyByViewport(
    PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
  ),
  imgCol: PropTypes.object
})

const staticStyles = StyleSheet.create({
  fullBleedImageCol: {
    padding: 0
  }
})

CardBase.propTypes = {
  ...selectedSystemPropTypes,
  children: PropTypes.node,
  tokens: getTokensPropType('Card'),
  /**
   * Identifier for testing purposes.
   */
  testID: PropTypes.string,
  /**
   * Apply background image to the card.
   */
  backgroundImage: PropTypes.shape({
    // The image src is either a URI string or a number (when a local image src is bundled in IOS or Android app)
    // src is an object when used responsively to provide different image sources for different screen sizes
    src: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]).isRequired,
    alt: PropTypes.string,
    resizeMode: responsiveProps.getTypeOptionallyByViewport(
      PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
    ),
    position: responsiveProps.getTypeOptionallyByViewport(
      PropTypes.oneOf(['bottom', 'left', 'right', 'top'])
    ),
    align: responsiveProps.getTypeOptionallyByViewport(
      PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
    )
  }),
  fullBleedContent: fullBleedContentPropTypes
}
