import { useCallback } from "react";

import createUseStyles from "./theme/createUseStyles";

import { applySpinnerAnimation, spinnerAnimation } from "./loading/Spinner";
import { cn } from "./util/cn";

const useStyles = createUseStyles<{
  color?: string;
  disabled: boolean;
  isLoading: boolean;
  size: ButtonSize;
}>({
  button: {
    "&:hover": {
      backgroundColor: (options) => {
        if (options.disabled) {
          return "var(--color-darkGrey)";
        }
        switch (options.color) {
          case "var(--color-white)":
            return "var(--color-lightGreen)";
          case "var(--color-grey)":
            return "var(--color-darkGrey)";
          case "var(--color-green)":
            return "var(--color-darkGreen)";
          case "var(--color-lightRed)":
            return "var(--color-semiLightRed)";
          case "var(--color-semiLightRed)":
            return "var(--color-lightRed)";
          case "var(--color-black)":
            return "rgba(255,255,255,0.1)";
          default:
            return options.color;
        }
      },
    },
    backgroundColor: (options) =>
      options.disabled ? "var(--color-darkGrey)" : options.color,
    border: (options) => {
      switch (options.color) {
        case "var(--color-black)":
          return "1px solid var(--color-white)";
        default:
          return "none";
      }
    },
    borderRadius: 0,
    cursor: ({ disabled }) => (disabled ? "not-allowed" : "pointer"),
    padding: ({ size }) => {
      switch (size) {
        case ButtonSize.S:
          return "var(--spacing-xs) var(--spacing-m)";
        default:
        case ButtonSize.M:
          return "var(--spacing-m) var(--spacing-l)";
      }
    },
  },
  buttonLabel: {
    color: (options) => {
      if (options.disabled) {
        return "var(--color-white)";
      }
      switch (options.color) {
        case "var(--color-white)":
          return "var(--color-black)";
        case "var(--color-grey)":
          return "var(--color-black)";
        default:
          return "var(--color-white)";
      }
    },
    fontWeight: 700,
  },
  ...spinnerAnimation,
  buttonContent: {
    display: ({ isLoading }) => (isLoading ? "inline-flex" : "flex"),
    opacity: ({ isLoading }) => (isLoading ? 0 : 1),
  },
  loading: {
    border: "3px solid var(--color-whiteOpacity)",
    borderTopColor: "var(--color-white)",
    height: 25,
    left: 0,
    right: 0,
    width: 25,
    ...applySpinnerAnimation,
  },
});

export enum ButtonSize {
  M = "M",
  S = "S",
}

export default function Button({
  children,
  label,
  className,
  isDisabled = false,
  onClick,
  color = "var(--color-black)",
  type = "button",
  isLoading = false,
  prefix,
  size = ButtonSize.M,
  isUppercase = true,
  ...buttonAttributes
}: React.PropsWithChildren<{
  className?: string;
  color?: string;
  isDisabled?: boolean;
  isLoading?: boolean;
  isUppercase?: boolean;
  label: React.ReactNode;
  onClick?: (
    event: React.MouseEvent<HTMLButtonElement>,
  ) => void | Promise<void>;
  prefix?: React.ReactNode;
  size?: ButtonSize;
  type?: React.ButtonHTMLAttributes<HTMLButtonElement>["type"];
  style?: React.CSSProperties;
} & Partial<Pick<React.ButtonHTMLAttributes<HTMLButtonElement>, "style" | "onMouseEnter" | "onMouseLeave">>>) {
  const classes = useStyles({
    color,
    disabled: isDisabled,
    isLoading,
    size,
  });

  const onClickWrapper = useCallback(
    (e: React.MouseEvent<HTMLButtonElement>) => {
      if (isDisabled) {
        return;
      }
      if (onClick) {
        void onClick(e);
      }
    },
    [isDisabled, onClick],
  );

  return (
    <button
      // eslint-disable-next-line react/button-has-type
      type={type}
      className={cn(classes.button, "relative user-select-none word-break-keep-all outline-none", className)}
      {...(isDisabled && { disabled: isDisabled })}
      {...(!isDisabled && onClick && { onClick: onClickWrapper })}
      {...buttonAttributes}
    >
      {isLoading && <div className={cn("margin-auto absolute inline-block border-radius-round", classes.loading)} />}
      <div className={cn("flex-row items-center", classes.buttonContent)}>
        {prefix}
        <span
          className={cn("flex-1 font-size-s font-letterSpacing-m font-lineHeight-l text-center", classes.buttonLabel, {
            "text-uppercase": isUppercase,
          })}
        >
          {label}
        </span>
        {children}
      </div>
    </button>
  );
};
