import {
  AriaAttributes,
  ComponentType,
  forwardRef,
  MouseEventHandler,
  PropsWithChildren,
  ReactNode,
} from 'react';
import clsx from 'clsx';

import { AutomationTestingProps } from '@/types';

import { Icon } from '@@/Icon';
import './Button.css';

type Type = 'submit' | 'button' | 'reset';
type Color =
  | 'default'
  | 'primary'
  | 'secondary'
  | 'danger'
  | 'link'
  | 'light'
  | 'dangerlight'
  | 'warninglight'
  | 'warning'
  | 'none';
type Size = 'xsmall' | 'small' | 'medium' | 'large';

export interface Props<TasProps = unknown>
  extends AriaAttributes,
    AutomationTestingProps {
  icon?: ReactNode | ComponentType<unknown>;

  color?: Color;
  size?: Size;
  disabled?: boolean;
  title?: string;
  className?: string;
  type?: Type;
  as?: ComponentType<TasProps> | string;
  onClick?: MouseEventHandler<HTMLButtonElement>;
  mRef?: React.ForwardedRef<HTMLButtonElement>;
  props?: Omit<TasProps, keyof Props>;
}

export const ButtonWithRef = forwardRef<HTMLButtonElement, Omit<Props, 'mRef'>>(
  (props, ref) => (
    // eslint-disable-next-line react/jsx-props-no-spreading
    <Button {...props} mRef={ref} />
  )
);

export function Button<TasProps = unknown>({
  type = 'button',
  color = 'primary',
  size = 'small',
  disabled = false,
  className,
  onClick,
  title,
  icon,
  children,
  as = 'button',
  props,
  mRef,
  ...ariaProps
}: PropsWithChildren<Props<TasProps>>) {
  const Component = as as 'button';
  return (
    <Component
      ref={mRef}
      /* eslint-disable-next-line react/button-has-type */
      type={type}
      disabled={disabled}
      className={clsx(`btn btn-${color}`, sizeClass(size), className, {
        disabled,
      })}
      onClick={(e) => {
        if (!disabled) {
          onClick?.(e);
        }
      }}
      title={title}
      // eslint-disable-next-line react/jsx-props-no-spreading
      {...ariaProps}
      // eslint-disable-next-line react/jsx-props-no-spreading
      {...props}
    >
      {icon && <Icon icon={icon} size={getIconSize(size)} />}
      {children}
    </Component>
  );
}

function getIconSize(size: Size) {
  switch (size) {
    case 'xsmall':
      return 'xs';
    case 'medium':
      return 'md';
    case 'large':
      return 'lg';
    case 'small':
    default:
      return 'sm';
  }
}

function sizeClass(size?: Size) {
  switch (size) {
    case 'large':
      return 'btn-lg';
    case 'medium':
      return 'btn-md';
    case 'xsmall':
      return 'btn-xs';
    default:
      return 'btn-sm';
  }
}