import { forwardRef } from 'react';
import type { ButtonHTMLAttributes, ReactNode } from 'react';
import { cx } from '../../utils/cx';
import { Spinner } from '../Spinner/Spinner';
import './Button.css';

export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
export type ButtonSize = 'sm' | 'md' | 'lg';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  /** Visual weight. `primary` is the single main action on a screen. */
  variant?: ButtonVariant;
  size?: ButtonSize;
  /**
   * Shows a spinner in place of `iconStart` and blocks interaction. The label
   * stays visible so the button still says what it is doing.
   */
  loading?: boolean;
  /** Stretches to the width of the container. */
  fullWidth?: boolean;
  /** Icon rendered before the label. */
  iconStart?: ReactNode;
  /** Icon rendered after the label. */
  iconEnd?: ReactNode;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
  {
    variant = 'secondary',
    size = 'md',
    loading = false,
    fullWidth = false,
    iconStart,
    iconEnd,
    disabled,
    className,
    children,
    type = 'button',
    ...rest
  },
  ref,
) {
  return (
    <button
      {...rest}
      ref={ref}
      type={type}
      className={cx(
        'ds-btn',
        `ds-btn--${variant}`,
        `ds-btn--${size}`,
        fullWidth && 'ds-btn--full',
        loading && 'ds-btn--loading',
        className,
      )}
      disabled={disabled || loading}
      aria-busy={loading || undefined}
    >
      {/* The spinner takes the leading slot so the label never disappears.
          aria-busy already announces the state, so the spinner stays silent. */}
      {loading ? (
        <span className="ds-btn__spinner">
          <Spinner size="sm" label={null} />
        </span>
      ) : (
        iconStart && (
          <span className="ds-btn__icon" aria-hidden="true">
            {iconStart}
          </span>
        )
      )}
      {children != null && <span className="ds-btn__label">{children}</span>}
      {iconEnd && (
        <span className="ds-btn__icon" aria-hidden="true">
          {iconEnd}
        </span>
      )}
    </button>
  );
});
