import React, { forwardRef } from 'react';
import { clsx } from 'clsx';
import type {
  ButtonProps,
  ButtonElementProps,
  AnchorElementProps,
  ButtonType,
  ButtonIconSize,
} from './Button.types';
import { PrimitiveButton, PrimitiveAnchor } from '../../../primitives';
import ProcessIndicator from '../../../processIndicator';
import AvatarLayout from '../../../avatarLayout';
import AvatarView from '../../../avatarView';
import Circle from '../../../common/circle';
import * as Icons from '@transferwise/icons';

/**
 * The Button component is a versatile and flexible component that can render
 * either a `button` or an `anchor` element based on the `as` prop. This allows
 * the component to be used in various contexts where different HTML elements
 * are needed while maintaining a consistent API and styling.
 *
 * @see {@link Button} for further information.
 * @see {@link https://storybook.wise.design/?path=/docs/new-button--docs|Storybook Wise Design}
 */

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      as = 'button' as ButtonType,
      children,
      className,
      disabled,
      loading = false,
      block = false,
      size = 'md',
      priority = 'primary',
      appearance,
      iconLeft,
      iconRight,
      iconOnly,
      avatars,
      type,
      ...props
    },
    ref,
  ) => {
    const isButtonIconSize = (value: any): value is ButtonIconSize => {
      return [16, 24, 32, 40, 48, 56, 72].includes(value);
    };

    let sizeClass: string;

    if (iconOnly && isButtonIconSize(size)) {
      // If iconOnly is true and size is a ButtonIconSize, return the size as a string
      sizeClass = size.toString();
    } else {
      // Otherwise, handle ButtonSize
      sizeClass = size === 'sm' ? 'small' : size === 'md' ? 'medium' : size === 'lg' ? 'large' : '';
    }

    const classNames = clsx(
      {
        [`wds-Button`]: as === 'a',
        [`wds-Button--block`]: block,
        [`wds-Button--${sizeClass}`]: size,
        [`wds-Button--${priority}`]: priority,
        [`wds-Button--${appearance}`]: appearance,
        [`wds-Button--iconOnly`]: iconOnly,
      },
      className,
    );

    const contentClassNames = clsx('wds-Button-content', {
      [`wds-Button-content--loading`]: loading,
    });

    const LeftIcon = iconLeft ? Icons[iconLeft as keyof typeof Icons] : null;
    const RightIcon = iconRight ? Icons[iconRight as keyof typeof Icons] : null;
    const IconOnly = iconOnly ? Icons[iconOnly as keyof typeof Icons] : null;

    const content = (
      <span className={contentClassNames}>
        {loading && (
          <ProcessIndicator
            size="xs"
            className="wds-Button-loader"
            data-testid="button-loader-indicator"
          />
        )}
        <span className="wds-Button-label" aria-hidden={loading}>
          {size === 'lg' ? (
            children
          ) : (
            <>
              {size === 'md' && avatars && (
                <span className="wds-Button-avatars">
                  {avatars.length === 1 ? (
                    <AvatarView size={24}>{avatars[0]?.asset}</AvatarView>
                  ) : (
                    <AvatarLayout orientation="horizontal" avatars={avatars} size={24} />
                  )}
                </span>
              )}
              {!avatars && LeftIcon && (
                <LeftIcon className="wds-Button-icon wds-Button-icon--left" />
              )}
              {children}
              {RightIcon && <RightIcon className="wds-Button-icon wds-Button-icon--right" />}
            </>
          )}
        </span>
      </span>
    );

    if (iconOnly) {
      const CircleContent = (
        <Circle
          ref={ref}
          as={as === 'a' ? PrimitiveAnchor : PrimitiveButton}
          size={(size as ButtonIconSize) || 40}
          fixedSize
          {...(props as any)}
          className={classNames}
          disabled={disabled}
          aria-label={children as string}
        >
          {IconOnly && <IconOnly className="wds-Button-icon" />}
        </Circle>
      );

      return CircleContent;
    }

    if (as === 'a') {
      return (
        <PrimitiveAnchor
          ref={ref as React.Ref<HTMLAnchorElement>}
          {...(props as AnchorElementProps)}
          className={classNames}
          disabled={disabled}
        >
          {content}
        </PrimitiveAnchor>
      );
    }

    return (
      <PrimitiveButton
        ref={ref as React.Ref<HTMLButtonElement>}
        {...(props as ButtonElementProps)}
        className={classNames}
        disabled={disabled}
        loading={loading}
        type={type as ButtonType}
      >
        {content}
      </PrimitiveButton>
    );
  },
);

Button.displayName = 'Button';

export default Button;
