import { type ComponentProps, forwardRef } from "react";
import type { VariantProps } from "class-variance-authority";

import { Icon, type IconProps } from "../icon/Icon";
import { Tooltip } from "../tooltip/Tooltip";
import { twMerge } from "../../../tailwind.config";
import { buttonClasses } from "./Button.styles";
import { twJoin } from "tailwind-merge";

export interface ButtonProps extends ComponentProps<"button"> {
  /** @default "primary" */
  variant?: Exclude<
    VariantProps<typeof buttonClasses>["intent"],
    null | undefined
  >;
  /** @default "32" */
  size?: Exclude<VariantProps<typeof buttonClasses>["size"], null | undefined>;
  fullWidth?: boolean;
  icon?: IconProps["name"];
  /** @default "semibold" */
  iconWeight?: IconProps["weight"];
  /** @default "start" */
  iconPosition?: "start" | "end";
  ESCAPE_className?: string;
  tooltip?: ComponentProps<typeof Tooltip>["tooltip"];
  tooltipProps?: Omit<ComponentProps<typeof Tooltip>, "tooltip" | "children">;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      children,
      disabled = false,
      variant = "primary",
      size = "32",
      fullWidth = false,
      icon,
      iconPosition = "start",
      iconWeight = "semibold",
      tooltip,
      tooltipProps = {},
      ESCAPE_className,
      ...restProps
    },
    ref
  ) => {
    const hasLabel = !!children;

    const iconComponent = icon ? (
      <Icon name={icon} weight={iconWeight} size="20" />
    ) : null;

    const buttonComponent = (
      <button
        ref={ref}
        className={twMerge(
          buttonClasses({
            intent: variant,
            size,
            shape: hasLabel ? "rectangular" : "square",
          }),
          fullWidth && "flex w-full",
          ESCAPE_className
        )}
        disabled={disabled}
        type="button"
        {...restProps}
      >
        {iconPosition === "start" && iconComponent}
        {children && (
          <span
            className={twJoin(
              "inline-flex",
              ["32", "40"].includes(size) ? "px-2" : "p-2"
            )}
          >
            {children}
          </span>
        )}
        {iconPosition === "end" && iconComponent}
      </button>
    );

    if (tooltip) {
      return (
        <Tooltip tooltip={tooltip} {...tooltipProps}>
          {/* Disabled buttons don't fire events that the tooltip listens to. */}
          {disabled ? (
            <span className="inline-flex">{buttonComponent}</span>
          ) : (
            buttonComponent
          )}
        </Tooltip>
      );
    }

    return buttonComponent;
  }
);

Button.displayName = "Button";
