import Link from "next/link";
import type { ComponentPropsWithoutRef, ComponentPropsWithRef, ReactNode } from "react";

export type ButtonVariant = "primary" | "secondary" | "subtle" | "danger" | "plain";
export type ButtonSize = "xs" | "sm" | "md";

type ButtonClassNameOptions = {
  variant?: ButtonVariant;
  size?: ButtonSize;
  className?: string;
};

const BASE_CLASS =
  "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-55";

const VARIANT_CLASS: Record<ButtonVariant, string> = {
  primary: "border border-accent bg-accent text-on-accent hover:bg-accent-hover hover:border-accent-hover",
  secondary: "border border-surface-2 bg-surface-0 text-text-primary hover:border-accent/35 hover:bg-surface-1",
  subtle: "border border-accent/30 bg-accent-muted text-accent hover:border-accent/50 hover:bg-accent/15",
  danger: "border border-danger/30 bg-danger-muted text-danger hover:border-danger/50 hover:bg-danger/15",
  plain: "border border-transparent bg-transparent text-accent hover:text-accent-hover hover:bg-accent/10",
};

const SIZE_CLASS: Record<ButtonSize, string> = {
  xs: "h-7 px-2.5 text-xs",
  sm: "h-8 px-3 text-sm",
  md: "h-9 px-4 text-sm",
};

function cx(...classes: Array<string | undefined | false>): string {
  return classes.filter(Boolean).join(" ");
}

export function buttonClassName({
  variant = "primary",
  size = "md",
  className,
}: ButtonClassNameOptions = {}): string {
  return cx(BASE_CLASS, SIZE_CLASS[size], VARIANT_CLASS[variant], className);
}

// ComponentPropsWithRef: React 19 passes ref through function components as
// a plain prop, which the {…props} spread forwards to the DOM button —
// callers can focus a specific Button (the confirm dialog does).
type ButtonProps = ComponentPropsWithRef<"button"> & ButtonClassNameOptions & {
  children: ReactNode;
};

export function Button({
  variant = "primary",
  size = "md",
  className,
  children,
  type = "button",
  ...props
}: ButtonProps) {
  return (
    <button
      type={type}
      className={buttonClassName({ variant, size, className })}
      {...props}
    >
      {children}
    </button>
  );
}

type ButtonLinkProps = ComponentPropsWithoutRef<typeof Link> & ButtonClassNameOptions & {
  children: ReactNode;
};

export function ButtonLink({
  variant = "primary",
  size = "md",
  className,
  children,
  ...props
}: ButtonLinkProps) {
  return (
    <Link className={buttonClassName({ variant, size, className })} {...props}>
      {children}
    </Link>
  );
}

type RefreshButtonProps = Omit<ButtonProps, "children" | "variant"> & {
  busy?: boolean;
};

function RefreshIcon({ busy }: { busy?: boolean }) {
  return (
    <svg
      aria-hidden="true"
      viewBox="0 0 24 24"
      className={cx("h-4 w-4", busy && "animate-spin", "opacity-70")}
      fill="currentColor"
    >
      <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />
    </svg>
  );
}

export function RefreshButton({
  busy,
  size = "sm",
  className,
  "aria-label": ariaLabel = "Refresh",
  title,
  ...props
}: RefreshButtonProps) {
  return (
    <Button
      {...props}
      aria-label={ariaLabel}
      aria-busy={busy || props["aria-busy"] ? "true" : undefined}
      title={title ?? ariaLabel}
      variant="subtle"
      size={size}
      className={cx("overflow-visible", size === "xs" ? "w-7 px-0" : size === "sm" ? "w-8 px-0" : "w-9 px-0", className)}
    >
      <RefreshIcon busy={busy} />
    </Button>
  );
}
