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

import { applyOuterBorder, applyShadowToken } from '../ThemeProvider/utils/styles'
import { useThemeTokensCallback } from '../ThemeProvider/useThemeTokens'
import { a11yProps } from '../utils/props/a11yProps'
import { clickProps } from '../utils/props/clickProps'
import { getTokensPropType, selectTokens } from '../utils/props/tokens'
import { hrefAttrsProp } from '../utils/props/hrefAttrsProp'
import { linkProps } from '../utils/props/linkProps'
import { resolvePressableState } from '../utils/pressability'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { withLinkRouter } from '../utils/withLinkRouter'
import { Icon } from '../Icon/Icon'

// function to get the dimentionals of the conditionals
const getPasswordDimensions = (password, width, height) => {
  return password ? { width, height } : {}
}

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

const selectOuterStyle = (
  {
    backgroundColor,
    outerBorderWidth,
    outerBorderColor,
    outerBorderGap,
    borderRadius,
    borderTopLeftRadius,
    borderTopRightRadius,
    borderBottomLeftRadius,
    borderBottomRightRadius,
    shadow,
    width,
    height
  },
  password
) => [
  {
    backgroundColor,
    ...applyShadowToken(shadow),
    ...applyOuterBorder({
      borderRadius,
      borderTopLeftRadius,
      borderTopRightRadius,
      borderBottomLeftRadius,
      borderBottomRightRadius,
      outerBorderWidth,
      outerBorderColor,
      outerBorderGap
    }),
    ...getPasswordDimensions(password, width, height),
    ...Platform.select({
      web: {
        outline: 'none',
        display: 'inline-flex',
        alignItems: 'center',
        justifyContent: 'center'
      }
    })
  },
  staticStyles.outer
]

const calculatePadding = (padding, borderWidth) => padding && Math.max(0, padding - borderWidth) // Stable size as border changes

const selectInnerStyle = (
  {
    borderColor,
    borderWidth,
    borderTopLeftRadius,
    borderTopRightRadius,
    borderBottomLeftRadius,
    borderBottomRightRadius,
    borderRadius,
    padding = 0,
    borderTopWidth,
    borderRightWidth,
    borderBottomWidth,
    borderLeftWidth,
    paddingLeft,
    paddingRight,
    paddingTop,
    paddingBottom,
    width,
    height
  },
  password
) => {
  const basePadding = calculatePadding(padding, borderWidth)

  const calculateSpecificPadding = (specificPadding, specificBorderWidth) => {
    const calculated = calculatePadding(
      specificPadding ?? padding,
      specificBorderWidth ?? borderWidth
    )
    return calculated !== basePadding && calculated !== undefined ? calculated : undefined
  }

  return {
    // Inner borders animate with the icon and should be treated like a themable feature of the icon
    borderColor,
    borderRadius,
    borderWidth,
    borderTopLeftRadius,
    borderTopRightRadius,
    borderBottomLeftRadius,
    borderBottomRightRadius,
    borderTopWidth,
    borderRightWidth,
    borderBottomWidth,
    borderLeftWidth,
    padding: basePadding,
    paddingLeft: calculateSpecificPadding(paddingLeft, borderLeftWidth),
    paddingRight: calculateSpecificPadding(paddingRight, borderRightWidth),
    paddingTop: calculateSpecificPadding(paddingTop, borderTopWidth),
    paddingBottom: calculateSpecificPadding(paddingBottom, borderBottomWidth),
    ...Platform.select({
      web: {
        pointerEvents: 'none',
        display: 'inline-flex',
        alignItems: 'center',
        justifyContent: 'center'
      }
    }),
    ...getPasswordDimensions(password, width, height)
  }
}

/**
 * A pressable themeless base component that handles pressable states and passes tokens
 * based on these to an outer border and a base Card component. Not intended to be used in
 * apps or sites directly: build themed components on top of this.
 */
const IconButtonBase = React.forwardRef(
  (
    {
      tokens,
      variant = {},
      icon: IconComponent,
      href,
      hrefAttrs,
      testID,
      accessibilityRole = href ? 'link' : 'button',
      inactive = false,
      hitSlop,
      ...rawRest
    },
    ref
  ) => {
    const { onPress, ...rest } = clickProps.toPressProps(rawRest)
    const selectedProps = selectProps({
      ...rest,
      accessibilityRole
    })
    const handlePress = () => {
      linkProps.handleHref({ href, onPress })({ nativeEvent: { target: ref?.current?.id } })
    }

    const mergedVariant = inactive ? { ...variant, inactive: true } : variant
    const getTokens = useThemeTokensCallback('IconButton', tokens, mergedVariant)
    const getOuterStyle = (pressableState) =>
      selectOuterStyle(getTokens(resolvePressableState(pressableState), mergedVariant.password))

    return (
      <Pressable
        ref={ref}
        href={href}
        onPress={handlePress}
        hrefAttrs={hrefAttrs}
        style={getOuterStyle}
        hitSlop={hitSlop}
        {...selectedProps}
        testID={testID}
        disabled={inactive}
      >
        {(pressableState) => {
          const themeTokens = getTokens(resolvePressableState(pressableState))
          return (
            <View style={selectInnerStyle(themeTokens, mergedVariant.password)}>
              <Icon
                icon={IconComponent || themeTokens.icon}
                title={selectedProps.accessibilityLabel}
                tokens={selectTokens('Icon', themeTokens, 'icon')}
                variant={mergedVariant}
              />
            </View>
          )
        }}
      </Pressable>
    )
  }
)
IconButtonBase.displayName = 'IconButton'

IconButtonBase.propTypes = {
  ...selectedSystemPropTypes,
  variant: variantProp.propType,
  tokens: getTokensPropType('IconButton'),
  /**
   * A unique identifier for testing purposes.
   * Will be added as a `data-testid` attribute.
   */
  testID: PropTypes.string,
  /**
   * Defines the icon to be rendered
   */
  icon: PropTypes.elementType,
  /**
   * URL to navigate to when the `Iconbutton` is pressed
   */
  href: PropTypes.string,
  /**
   * URL options to navigate to when the `Iconbutton` is pressed
   */
  hrefAttrs: PropTypes.shape(hrefAttrsProp.types),
  /**
   * Function to execute when the `Iconbutton` is pressed
   */
  onPress: PropTypes.func,
  /**
   * When true, applies the inactive variant styling
   */
  inactive: PropTypes.bool,
  /**
   * Additional distance (in pixels) outside the button's visual bounds that still
   * registers a press, used to meet the minimum tap target on touch devices
   */
  hitSlop: PropTypes.number
}

const staticStyles = StyleSheet.create({
  outer: {
    alignSelf: 'baseline'
  }
})

// Export unwrapped component for react-docgen to extract info for docsite
export { IconButtonBase }

export const IconButton = withLinkRouter(IconButtonBase)
