import React, { forwardRef } from 'react'
import { Button as RACButton, type ButtonProps as RACButtonProps } from 'react-aria-components'
import { useReversedColors } from '~components/utils'
import { mergeClassNames } from '~components/utils/mergeClassNames'
import { ButtonContent, PendingContent } from './subcomponents'
import { type ButtonSizes, type ButtonVariants, type PendingButtonProps } from './types'
import styles from './Button.module.css'

/** Shared UI props between Button and LinkButton */
export type ButtonUIProps = {
  /** Visually hides the Button's child content used as the label and the `pendingLabel`. Use for icon-only `Button`. @default "false" */
  hasHiddenLabel?: boolean
  /** The visual style of the button.
   *  @default "default" */
  variant?: ButtonVariants
  /** The visual size of the button. `medium` was formerly `regular`
   *  @default "medium" */
  size?: ButtonSizes
  /** Renders an icon at the `iconPosition` provided. To the size scales with the button, we recommend using the `Icon` component from `"@kaizen/components/next"` */
  icon?: JSX.Element
  /** Controls the position of the Icon passed in as props. @default "start" */
  iconPosition?: 'start' | 'end'
  /** Controls if the button inherits width from its parent. @default "false" */
  isFullWidth?: boolean
}

export type ButtonProps = ButtonUIProps &
  PendingButtonProps &
  Omit<RACButtonProps, 'children'> & {
    /** Used as the label for the button. */
    children: RACButtonProps['children']
  }

export const Button = forwardRef(
  (
    {
      variant = 'primary',
      size = 'medium',
      className,
      children,
      isDisabled,
      isFullWidth = false,
      icon,
      iconPosition,
      hasHiddenLabel = false,
      isPending,
      hasHiddenPendingLabel: propsHasHiddenPendingLabel = false,
      pendingLabel,
      ...restProps
    }: ButtonProps,
    ref: React.ForwardedRef<HTMLButtonElement>,
  ) => {
    const isReversedVariant = useReversedColors()
    const pendingProps: PendingButtonProps = isPending
      ? {
          isPending,
          hasHiddenPendingLabel: hasHiddenLabel || propsHasHiddenPendingLabel,
          pendingLabel,
        }
      : {}
    const buttonContentClass = isPending
      ? !hasHiddenLabel && propsHasHiddenPendingLabel
        ? styles.retainContentWidth
        : styles.hideContentWidth
      : undefined

    return (
      <RACButton
        ref={ref}
        className={mergeClassNames(
          styles.button,
          styles[size],
          hasHiddenLabel && styles[`${size}IconButton`],
          isDisabled && styles.isDisabled,
          isReversedVariant ? styles[`${variant}Reversed`] : styles[variant],
          isFullWidth && styles.fullWidth,
          className,
        )}
        isDisabled={isDisabled}
        isPending={isPending}
        {...restProps}
      >
        {(racStateProps) => {
          const childIsFunction = typeof children === 'function'

          return (
            <>
              <ButtonContent
                size={size}
                icon={icon}
                iconPosition={iconPosition}
                hasHiddenLabel={hasHiddenLabel}
                className={buttonContentClass}
              >
                {childIsFunction ? children(racStateProps) : children}
              </ButtonContent>
              <span aria-live="polite">
                {pendingProps.isPending && <PendingContent {...pendingProps} size={size} />}
              </span>
            </>
          )
        }}
      </RACButton>
    )
  },
)

Button.displayName = 'Button'
