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

import { ExpandCollapseControl } from './ExpandCollapseControl'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { a11yProps } from '../utils/props/a11yProps'
import { copyPropTypes } from '../utils/props/copyPropTypes'
import { getTokensPropType } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { useVerticalExpandAnimation } from '../utils/animation/useVerticalExpandAnimation'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { useCopy } from '../utils/useCopy'

import { dictionary } from './dictionary'

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

// just void functions for now, since there are no style tokens for a panel or control at this point
const selectContainerStyles = ({
  contentPaddingLeft,
  contentPaddingRight,
  contentPaddingTop,
  contentPaddingBottom
}) => ({
  paddingLeft: contentPaddingLeft,
  paddingRight: contentPaddingRight,
  paddingTop: contentPaddingTop,
  paddingBottom: contentPaddingBottom,
  flex: 1
})
const selectTextStyles = ({
  contentPanelFontSize,
  contentPanelFontName,
  contentPanelFontColor,
  contentPanelFontWeight,
  contentPanelLineHeight
}) => ({
  fontSize: contentPanelFontSize,
  fontFamily: `${contentPanelFontName}${contentPanelFontWeight}normal`,
  lineHeight: contentPanelFontSize * contentPanelLineHeight,
  color: contentPanelFontColor
})
const selectContentPanelStyles = ({
  contentPanelBackgroundColor,
  contentPanelPaddingTop,
  contentPanelPaddingBottom,
  contentPanelPaddingLeft,
  contentPanelPaddingRight,
  contentPanelBorderWidth,
  contentPanelBorderColor,
  borderRadius,
  marginBottom
}) => ({
  backgroundColor: contentPanelBackgroundColor,
  paddingTop: contentPanelPaddingTop,
  paddingBottom: contentPanelPaddingBottom,
  paddingLeft: contentPanelPaddingLeft,
  paddingRight: contentPanelPaddingRight,
  borderWidth: contentPanelBorderWidth,
  borderColor: contentPanelBorderColor,
  borderStyle: 'solid',
  borderRadius,
  marginBottom
})

const selectControlPanelStyles = ({ contentPanelBackgroundColor }) => ({
  backgroundColor: contentPanelBackgroundColor
})

/**
 * An item in an `ExpandCollapse` which contains collapsible `children` and a `control` that opens
 * and closes the collapsible children when pressed.
 *
 * Expects `openIds` and `onToggle` props from the `ExpandCollapse` render function and a unique
 * `panelId` that matches the ids passed to the `<ExpandCollapse>`.
 *
 * The panel does not need to be a direct child of the `<ExpandCollapse>` (unless this is required
 * by the chosen accessibility props for a particular accessibility tools).
 */
export const ExpandCollapsePanel = React.forwardRef(
  (
    {
      openIds = [],
      panelId,
      onToggle,
      onPress,
      control,
      controlTokens,
      controlAlign,
      children,
      tokens,
      variant,
      controlRef,
      content,
      copy = 'en',
      disableMobileScrollBuffer = false,
      ...rest
    },
    ref
  ) => {
    const [containerHeight, setContainerHeight] = React.useState(null)
    const isExpanded = openIds.includes(panelId)
    const getCopy = useCopy({ dictionary, copy })

    const selectedProps = selectProps({
      ...rest,
      accessibilityState: {
        ...(rest.accessibilityState || {}),
        expanded: isExpanded
      }
    })

    const themeTokens = useThemeTokens('ExpandCollapsePanel', tokens, variant, {
      expanded: isExpanded
    })

    // on mobile devices we require a scroll buffer equal to the font size
    // to avoid triggering scrolling unnecessarily
    const mobileScrollBuffer =
      Platform.OS === 'web' || disableMobileScrollBuffer
        ? 0
        : selectTextStyles(themeTokens)?.fontSize

    const handleControlPress = (event) => {
      onToggle?.(panelId, event)
      if (onPress) onPress(panelId, event)
    }

    const onContainerLayout = (event) => {
      const { layout: { height = 0 } = {} } = event.nativeEvent
      if (Platform.OS === 'web' || (Platform.OS !== 'web' && containerHeight === null)) {
        setContainerHeight(height + mobileScrollBuffer)
      }
    }

    const [animatedStyles, animatedRef] = useVerticalExpandAnimation({
      containerHeight,
      isExpanded,
      tokens: themeTokens
    })

    const focusabilityProps = isExpanded ? {} : a11yProps.nonFocusableProps

    const panelAccessibilityLabel = `${panelId} ${getCopy('panel')}`
    const subPanelAccessibilityLabel = `${panelId} ${getCopy('subPanel')}`

    return content ? (
      <View
        style={[selectContentPanelStyles(themeTokens), staticStyles.panelContainer]}
        accessibilityLabel={panelAccessibilityLabel}
      >
        {typeof children === 'string' ? (
          <Text
            style={selectTextStyles(themeTokens)}
            accessibilityLabel={subPanelAccessibilityLabel}
          >
            {children}
          </Text>
        ) : (
          children
        )}
      </View>
    ) : (
      <View
        ref={ref}
        style={[themeTokens, staticStyles.container]}
        accessibilityLabel={panelAccessibilityLabel}
      >
        <View style={selectControlPanelStyles(themeTokens)}>
          <ExpandCollapseControl
            {...selectedProps}
            isExpanded={isExpanded}
            tokens={controlTokens}
            controlAlign={controlAlign}
            variant={variant}
            onPress={handleControlPress}
            ref={controlRef}
          >
            {control}
          </ExpandCollapseControl>
        </View>
        {isExpanded && (
          <View
            style={{
              borderTopColor: themeTokens.expandDividerColor,
              borderTopWidth: themeTokens.expandDividerWidth,
              marginLeft: themeTokens.contentPaddingLeft,
              marginRight: themeTokens.contentPaddingRight
            }}
          />
        )}
        <Animated.ScrollView ref={animatedRef} style={animatedStyles} {...focusabilityProps}>
          <View
            onLayout={onContainerLayout}
            style={{
              ...Platform.select({
                default: {
                  flex: 1,
                  overflow: 'hidden'
                },
                web: {
                  overflow: 'hidden'
                }
              })
            }}
          >
            {Platform.OS === 'web' ? (
              <View
                style={selectContainerStyles(themeTokens)}
                accessibilityLabel={subPanelAccessibilityLabel}
              >
                {children}
              </View>
            ) : (
              <ScrollView
                onContentSizeChange={(_, height) => {
                  setContainerHeight(height + mobileScrollBuffer)
                }}
                style={selectContainerStyles(themeTokens)}
                accessibilityLabel={subPanelAccessibilityLabel}
              >
                {children}
              </ScrollView>
            )}
          </View>
        </Animated.ScrollView>
      </View>
    )
  }
)
ExpandCollapsePanel.displayName = 'ExpandCollapsePanel'

const staticStyles = StyleSheet.create({
  container: {
    ...Platform.select({
      web: {
        flexGrow: 1,
        flexshrink: 1,
        flexbasis: 'auto'
      },
      default: { flex: 1 }
    }),
    justifyContent: 'flex-start'
  },
  panelContainer: {
    flexGrow: 1,
    alignSelf: 'stretch'
  }
})

ExpandCollapsePanel.propTypes = {
  ...selectedSystemPropTypes,
  variant: variantProp.propType,
  tokens: getTokensPropType('ExpandCollapsePanel'),
  /**
   * Whether the English or French copy will be used (e.g. for accessibility labels).
   */
  copy: copyPropTypes,
  /**
   * Array of the ids of currently open groups
   */
  openIds: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
  /**
   * Unique name by which the ExpandCollapse controls this element.
   */
  panelId: PropTypes.string.isRequired,
  /**
   * Function to call on pressing the panel's control, which should open or close the panel.
   * If Panel is a direct child of `ExpandCollapse`, this prop will be provided by the ExpandCollapse parent.
   */
  onToggle: PropTypes.func,
  /**
   * Optional function to call on pressing the panel's control, in addition to opening or closing the panel.
   */
  onPress: PropTypes.func,
  /**
   * ExpandCollapsePanel's children are the content that is hidden or revealed on open or close, and may be any
   * single React element or a render function which takes this group's isExpanded state, onToggle function and panelId
   */
  children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
  /**
   * The content inside the control element that opens and closes the ExpandCollapse when pressed.
   */
  control: ExpandCollapseControl.propTypes.children,
  /**
   * Optional theme token overrides that may be passed to the ExpandCollapseControl element.
   */
  controlTokens: getTokensPropType('ExpandCollapseControl'),
  /**
   * Optional alignment for control content.
   */
  controlAlign: PropTypes.oneOf(['flex-start', 'center', 'flex-end']),
  /**
   * An optional ref to be attached to the control
   */
  controlRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.any })]),
  /**
   * A boolean prop to determine if the panel is a content panel or not. If true, the panel will not have a control
   */
  content: PropTypes.bool,
  /**
   * A boolean prop to disable the extra scroll buffer on mobile devices (only applicable on iOS/Android, ignored on web)
   */
  disableMobileScrollBuffer: PropTypes.bool
}
