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

import { useThemeTokensCallback } from '../ThemeProvider/useThemeTokens'
import { verticalAlignRow } from '../ThemeProvider/utils/styles'
import { a11yProps } from '../utils/props/a11yProps'
import { getTokensPropType } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'

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

function selectContainerStyles({
  backgroundColor,
  borderColor,
  borderWidth,
  borderStyle = 'solid',
  paddingLeft,
  paddingRight,
  paddingTop,
  paddingBottom,
  justifyContent,
  verticalAlign,
  iconPosition,
  borderTopRightRadius,
  borderBottomRightRadius,
  borderTopLeftRadius,
  borderBottomLeftRadius
}) {
  return {
    boxSizing: 'border-box',
    backgroundColor,
    borderColor,
    borderWidth,
    borderStyle,
    paddingLeft,
    paddingRight,
    paddingTop,
    paddingBottom,
    justifyContent,
    borderTopRightRadius,
    borderBottomRightRadius,
    borderTopLeftRadius,
    borderBottomLeftRadius,
    ...verticalAlignRow(verticalAlign, iconPosition === 'right'),
    // Updated default alignment due to the change in Pressable's behavior from div to button on web.
    textAlign: Platform.OS === 'web' ? 'inherit' : undefined
  }
}

// TODO: use stack / spacer when available
function selectIconContainerStyles({ iconGap, iconPaddingTop, iconPosition }) {
  const paddingSide = iconPosition === 'right' ? 'paddingLeft' : 'paddingRight'
  return {
    [paddingSide]: iconGap,
    ...(iconPaddingTop && { transform: [{ translateY: iconPaddingTop }] })
  }
}

function selectTextContainerStyles({ textLine, controlAlign }) {
  return {
    textDecorationLine: textLine,
    flex: 1,
    ...(controlAlign && { alignItems: controlAlign })
  }
}

function selectIconTokens(tokens) {
  return {
    color: tokens.iconColor,
    size: tokens.iconSize
  }
}

export const ExpandCollapseControl = React.forwardRef(
  (
    {
      onPress,
      isExpanded,
      children,
      tokens,
      controlAlign,
      accessibilityRole = 'button',
      variant,
      ...rest
    },
    ref
  ) => {
    const getTokens = useThemeTokensCallback('ExpandCollapseControl', tokens, variant)

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

    const getControlState = ({ pressed, hovered, focused }) => ({
      pressed,
      hover: hovered,
      focus: focused,
      expanded: isExpanded
    })
    const getControlTokens = (pressableState) => getTokens(getControlState(pressableState))
    const getPressableStyle = (pressableState) => ({
      ...selectContainerStyles(getControlTokens(pressableState)),
      ...(Platform.OS === 'web' ? { outline: 'none' } : {})
    })

    return (
      <Pressable ref={ref} {...selectedProps} onPress={onPress} style={getPressableStyle}>
        {(pressableState) => {
          const { icon: IconComponent, ...themeTokens } = getControlTokens(pressableState)
          return (
            <>
              {IconComponent && (
                <View
                  style={[selectIconContainerStyles(themeTokens), staticStyles.bubblePointerEvents]}
                >
                  <IconComponent {...selectIconTokens(themeTokens)} />
                </View>
              )}
              <View
                style={[
                  selectTextContainerStyles({
                    ...themeTokens,
                    ...(controlAlign && { controlAlign })
                  }),
                  staticStyles.bubblePointerEvents
                ]}
              >
                {typeof children === 'function'
                  ? children(getControlState(pressableState))
                  : children}
              </View>
            </>
          )
        }}
      </Pressable>
    )
  }
)
ExpandCollapseControl.displayName = 'ExpandCollapseControl'

const staticStyles = StyleSheet.create({
  bubblePointerEvents: {
    pointerEvents: 'none'
  }
})

ExpandCollapseControl.propTypes = {
  ...selectedSystemPropTypes,
  tokens: getTokensPropType('ExpandCollapseControl'),
  variant: variantProp.propType,
  /**
   * ExpandCollapseControl's children are the content within the button that opens or closes this panel. May
   * accept a render function that recieves the control state: { pressed, hover, focus, expanded }
   */
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  /**
   * Whether the linked ExpandCollapsePanel is opened or closed. Allows themes to set `expanded` styles.
   */
  isExpanded: PropTypes.bool,
  /**
   * Optional alignment for control content. Overrides token-driven alignment.
   */
  controlAlign: PropTypes.oneOf(['flex-start', 'center', 'flex-end']),
  /**
   * Function called when the ExpandCollapse is pressed.
   */
  onPress: PropTypes.func.isRequired
}
