/* eslint-disable react/button-has-type */
import React, { forwardRef, useCallback } from 'react';
import { clsx } from 'clsx';
import { useIntl, MessageDescriptor } from 'react-intl';
import messages from '../../../i18n/commonMessages/Button.messages';
import type { PrimitiveButtonProps } from '..';

/**
 * The Primitive `PrimitiveButton` component can be used in various parts the Wise Design
 * System internally. It supports different states such as disabled and loading,
 * and provides event handlers for common interactions like click, focus, blur,
 * mouse enter, mouse leave, and keydown.
 *
 * @see {@link PrimitiveButton} for further information.
 * @see {@link https://storybook.wise.design/?path=/docs/primitive-button--docs|Storybook Wise Design}
 */
const PrimitiveButton = forwardRef<HTMLButtonElement, PrimitiveButtonProps>(
  (
    {
      children,
      className,
      id,
      disabled = false,
      loading = false,
      testId,
      type = 'button',
      onClick,
      onFocus,
      onBlur,
      onMouseEnter,
      onMouseLeave,
      onKeyDown,
      ...props
    },
    ref,
  ) => {
    const intl = useIntl();
    const classNames = clsx(
      'wds-Button',
      {
        'wds-Button--disabled': disabled,
        'wds-Button--loading': loading,
      },
      className,
    );

    const handleClick = useCallback(
      (event: React.MouseEvent<HTMLButtonElement>) => {
        if (disabled || loading) {
          event.preventDefault();
        } else {
          onClick?.(event);
        }
      },
      [disabled, loading, onClick],
    );

    const handleFocus = useCallback(
      (event: React.FocusEvent<HTMLButtonElement>) => {
        onFocus?.(event);
      },
      [onFocus],
    );

    const handleBlur = useCallback(
      (event: React.FocusEvent<HTMLButtonElement>) => {
        onBlur?.(event);
      },
      [onBlur],
    );

    const handleMouseEnter = useCallback(
      (event: React.MouseEvent<HTMLButtonElement>) => {
        onMouseEnter?.(event);
      },
      [onMouseEnter],
    );

    const handleMouseLeave = useCallback(
      (event: React.MouseEvent<HTMLButtonElement>) => {
        onMouseLeave?.(event);
      },
      [onMouseLeave],
    );

    const handleKeyDown = useCallback(
      (event: React.KeyboardEvent<HTMLButtonElement>) => {
        onKeyDown?.(event);
      },
      [onKeyDown],
    );

    /**
     * The following props are used to manage the `disabled` and `loading`
     * states for the button:
     *
     * - `aria-disabled`: Communicates to assistive technologies that the button
     *   is disabled when it is either disabled or loading.
     * - `aria-label`: Provides an accessible label for the button, using a
     *   localized loading message when the button is in a loading state.
     * - `aria-live`: Ensures that updates to the button's content are announced
     *   by assistive technologies, set to 'polite' during loading.
     */
    const buttonProps = {
      'aria-disabled': disabled || loading,
      'aria-label': loading
        ? intl.formatMessage(messages.loadingAriaLabel as MessageDescriptor)
        : props['aria-label'],
      'aria-live': (loading ? 'polite' : 'off') as 'polite' | 'off' | 'assertive' | undefined,
      className: classNames,
      'data-testid': testId,
      disabled,
      id,
      ref,
      type,
      onBlur: handleBlur,
      onClick: handleClick,
      onFocus: handleFocus,
      onMouseEnter: handleMouseEnter,
      onMouseLeave: handleMouseLeave,
      onKeyDown: handleKeyDown,
      ...props,
    };

    return <button {...buttonProps}>{children}</button>;
  },
);

PrimitiveButton.displayName = 'PrimitiveButton';

export default PrimitiveButton;
