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

import { GutterContext } from '../providers/GutterContext'
import { applyInheritance } from '../helpers'
import { responsiveProps } from '../../utils/props/responsiveProps'
import { BaseView } from '../../utils/BaseView/BaseView'
import { StyleSheet, createMediaQueryStyles } from '../../utils/ssr-media-query/ssr-media-query'
import { useViewport } from '../../ViewportProvider/useViewport'
import { useTheme } from '../../ThemeProvider/useTheme'

export const FlexGridCol = React.forwardRef(
  (
    {
      horizontalAlign,
      children,
      xs,
      sm,
      md,
      lg,
      xl,
      xsOffset,
      smOffset,
      mdOffset,
      lgOffset,
      xlOffset,
      flex,
      ...viewProps
    },
    ref
  ) => {
    const gutter = React.useContext(GutterContext)
    const viewport = useViewport()
    const {
      themeOptions: { enableMediaQueryStyleSheet }
    } = useTheme()
    const hiddenLevels = applyInheritance([xs, sm, md, lg, xl])

    const getHorizontalAlignLevel = () => {
      if (typeof horizontalAlign === 'object') {
        return applyInheritance([
          horizontalAlign.xs,
          horizontalAlign.sm,
          horizontalAlign.md,
          horizontalAlign.lg,
          horizontalAlign.xl
        ])
      }
      if (typeof horizontalAlign === 'string') {
        return [horizontalAlign, horizontalAlign, horizontalAlign, horizontalAlign, horizontalAlign]
      }
      return ['inherit', 'inherit', 'inherit', 'inherit', 'inherit']
    }
    const horizontalAlignLevel = getHorizontalAlignLevel()

    const toPercent = (num) => {
      return `${(num / 12) * 100}%`
    }

    const calculateWidth = (value) => {
      if (typeof value === 'undefined') {
        return {}
      }

      if (typeof value === 'number') {
        const percent = toPercent(value)

        return {
          maxWidth: percent,
          flexBasis: percent
        }
      }

      return {
        flexGrow: 1,
        flexBasis: 0,
        maxWidth: '100%'
      }
    }

    const calculateOffset = (value) => {
      if (typeof value === 'number') {
        const percent = toPercent(value)

        return { marginLeft: percent }
      }
      return {}
    }

    // TODO: consider setting this to always 'flex' in a major release.
    // `display: block` is invalid in native apps.
    // See https://telusdigital.atlassian.net/browse/UDS1-92
    const shown = !flex && Platform.OS === 'web' ? 'block' : 'flex'

    const sizesArray = [xs, sm, md, lg, xl]
    const offSetsArray = [xsOffset, smOffset, mdOffset, lgOffset, xlOffset]
    const sizesWithIheritance = applyInheritance(sizesArray)
    const offsetsWithIheritance = applyInheritance(offSetsArray)
    const sizes = {
      xs: sizesWithIheritance[0],
      sm: sizesWithIheritance[1],
      md: sizesWithIheritance[2],
      lg: sizesWithIheritance[3],
      xl: sizesWithIheritance[4]
    }
    const offsets = {
      xs: offsetsWithIheritance[0],
      sm: offsetsWithIheritance[1],
      md: offsetsWithIheritance[2],
      lg: offsetsWithIheritance[3],
      xl: offsetsWithIheritance[4]
    }

    const staticStyles = {
      flexGrow: 1,
      flexShrink: 0,
      flexBasis: 'auto',
      maxWidth: '100%',
      paddingLeft: gutter ? 16 : 0,
      paddingRight: gutter ? 16 : 0
    }

    const stylesByViewport = {
      xs: {
        display: hiddenLevels[0] === 0 ? 'none' : shown,
        textAlign: horizontalAlignLevel[0],
        ...calculateWidth(sizes.xs),
        ...calculateOffset(offsets.xs)
      },
      sm: {
        display: hiddenLevels[1] === 0 ? 'none' : shown,
        textAlign: horizontalAlignLevel[1],
        ...calculateWidth(sizes.sm),
        ...calculateOffset(offsets.sm)
      },
      md: {
        display: hiddenLevels[2] === 0 ? 'none' : shown,
        textAlign: horizontalAlignLevel[2],
        ...calculateWidth(sizes.md),
        ...calculateOffset(offsets.md)
      },
      lg: {
        display: hiddenLevels[3] === 0 ? 'none' : shown,
        textAlign: horizontalAlignLevel[3],
        ...calculateWidth(sizes.lg),
        ...calculateOffset(offsets.lg)
      },
      xl: {
        display: hiddenLevels[4] === 0 ? 'none' : shown,
        textAlign: horizontalAlignLevel[4],
        ...calculateWidth(sizes.xl),
        ...calculateOffset(offsets.xl)
      }
    }

    let colStyles
    let mediaIds

    if (enableMediaQueryStyleSheet) {
      const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
      const { ids, styles } = StyleSheet.create({
        col: {
          ...staticStyles,
          ...mediaQueryStyles
        }
      })
      colStyles = styles.col
      mediaIds = ids.col
    } else {
      colStyles = {
        ...staticStyles,
        ...stylesByViewport[viewport]
      }
    }

    return (
      <BaseView
        ref={ref}
        {...viewProps}
        style={colStyles}
        dataSet={mediaIds && { media: mediaIds }}
      >
        {children}
      </BaseView>
    )
  }
)
FlexGridCol.displayName = 'FlexGridCol'

/*
 * We're disabling default props since passing undefined props to
 * the react-flexbox-grid component sets up blank classes that may cause
 * styling issues.
 */
FlexGridCol.propTypes = {
  /**
   * Specify number of columns within the 'xs' breakpoint range. `0` hides the column.
   *
   * `true` sets the column width automatically;
   * `false` disables the prop
   *
   */
  xs: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, true, false]),
  /**
   * Specify number of columns within the 'sm' breakpoint range. `0` hides the column.
   *
   * `true` sets the column width automatically;
   * `false` disables the prop
   *
   */
  sm: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, true, false]),
  /**
   * Specify number of columns within the 'md' breakpoint range. `0` hides the column.
   *
   * `true` sets the column width automatically;
   * `false` disables the prop
   *
   */
  md: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, true, false]),
  /**
   * Specify number of columns within the 'lg' breakpoint range. `0` hides the column.
   *
   * `true` sets the column width automatically;
   * `false` disables the prop
   *
   */
  lg: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, true, false]),
  /**
   * Specify number of columns after the 'xl' breakpoint. `0` hides the column.
   *
   * `true` sets the column width automatically;
   * `false` disables the prop
   *
   */
  xl: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, true, false]),
  /**
   * Offset the specified number of columns within the 'xs' breakpoint range. `0` hides the column.
   *
   */
  xsOffset: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
  /**
   * Offset the specified number of columns within the 'sm' breakpoint range.
   *
   */
  smOffset: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
  /**
   * Offset the specified number of columns within the 'md' breakpoint range.
   *
   */
  mdOffset: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
  /**
   * Offset the specified number of columns within the 'lg' breakpoint range.
   *
   */
  lgOffset: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
  /**
   * Offset the specified number of columns within the 'xl' breakpoint range.
   */
  xlOffset: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
  /**
   * The columns of the Grid. Will typically be `FlexGridCol` components.
   */
  children: PropTypes.node.isRequired,
  /**
   *
   * Align content horizontally within the column.
   *
   * Accepts a `PropType.string` following the [responsive prop](#/Layout?id=responsive) structure.
   */
  horizontalAlign: responsiveProps.getTypeOptionallyByViewport(
    PropTypes.oneOf(['left', 'center', 'right'])
  ),
  /**
   * (web only) Stretches the column to fill vertical space using `display: flex`.
   * In native apps, FlexGridCol behaves as if this is always true (as do all `View`s).
   */
  flex: PropTypes.bool
}
