import { ForwardedRef, ReactNode, cloneElement, forwardRef, isValidElement, useMemo } from 'react';
import type { ButtonProps as AriaButtonProps } from 'react-aria-components';
import { Button as AriaButton } from 'react-aria-components';
import type { ButtonVariantProps } from '@trail-ui/theme';
import { button } from '@trail-ui/theme';
import type { SpinnerProps } from '../spinner';
import { Spinner } from '../spinner';

export interface ButtonProps extends AriaButtonProps, Omit<ButtonVariantProps, 'isInGroup'> {
  /**
   * The button start content.
   */
  startContent?: ReactNode;
  /**
   * The button end content.
   */
  endContent?: ReactNode;
  /**
   * Spinner to display when loading.
   */
  spinner?: ReactNode;
  /**
   * Props to pass to the spinner.
   */
  spinnerProps?: SpinnerProps;
  /**
   * The spinner placement.
   * @default "start"
   */
  spinnerPlacement?: 'start' | 'end';
  /**
   * Whether the button should display a loading spinner.
   * @default false
   */
  children?: ReactNode;
  /**
   * Whether the button should display a loading spinner.
   */
  isLoading?: boolean;
  className?: string;
}

function Button(props: ButtonProps, ref: ForwardedRef<HTMLButtonElement>) {
  const {
    appearance,
    // size = 'md',
    spacing = 'default',
    fullWidth,
    isLoading,
    spinner = <Spinner color="current" size="sm" {...props.spinnerProps} />,
    spinnerPlacement = 'start',
    startContent: startContentProp,
    endContent: endContentProp,
    disableAnimation,
    className,
    children,
    ...otherProps
  } = props;

  const styles = useMemo(
    () =>
      button({
        // size,
        isLoading,
        spinnerPlacement,
        startContentProp: !!startContentProp,
        endContentProp: !!endContentProp,
        appearance,
        spacing,
        fullWidth,
        disableAnimation,
        className,
      }),
    [
      isLoading,
      spinnerPlacement,
      startContentProp,
      endContentProp,
      appearance,
      spacing,
      fullWidth,
      disableAnimation,
      className,
    ],
  );

  const getIconClone = (icon: ReactNode) =>
    isValidElement(icon)
      ? cloneElement<any>(icon, {
          'aria-hidden': true,
          focusable: false,
          tabIndex: -1,
        })
      : null;

  const startContent = getIconClone(startContentProp);
  const endContent = getIconClone(endContentProp);

  return (
    // @ts-ignore
    <AriaButton ref={ref} className={styles} {...otherProps}>
      {() => (
        <>
          {!isLoading && startContent}
          {isLoading && spinnerPlacement === 'start' && (
            <div className="shrink-0">
              {spinner}
              <span aria-atomic="true" aria-live="polite" className="sr-only">
                Loading{' '}
              </span>
            </div>
          )}
          {children}
          {isLoading && spinnerPlacement === 'end' && (
            <div className="shrink-0">
              <span aria-atomic="true" aria-live="polite" className="sr-only">
                Loading{' '}
              </span>
              {spinner}
            </div>
          )}
          {!isLoading && endContent}
        </>
      )}
    </AriaButton>
  );
}

/**
 * A button allows a user to perform an action, with mouse, touch, and keyboard interactions.
 */
const _Button = forwardRef(Button);
export { _Button as Button };
