import { forwardRef } from "react";
import type { ButtonHTMLAttributes } from "react";
import { Spinner } from "@/components/ui/spinner";
import { FOCUS_RING_CLASSES } from "@/components/ui/styles";
import { cn } from "@/lib/utils";

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

const VARIANT_CLASSES: Record<ButtonVariant, string> = {
  primary: "bg-kolabora-primary text-kolabora-neutral-white hover:opacity-90",
  secondary: "border border-kolabora-neutral-dark/20 hover:bg-kolabora-neutral-dark/5",
  danger: "border border-red-300 text-red-600 hover:bg-red-50",
};

const SIZE_CLASSES: Record<ButtonSize, string> = {
  sm: "px-3 py-1.5 text-sm",
  md: "px-4 py-2 text-sm",
  lg: "px-6 py-3 text-base",
};

// NOT `transition` (bare) — it includes box-shadow in its transition-property
// list, which breaks the focus-visible ring below: box-shadow's value is
// built from unregistered custom properties (--tw-ring-*), and transitioning
// a property whose value depends on an untyped custom property is undefined
// per spec — Chromium ends up dropping the new value entirely, so the ring
// never renders. Scope the transition to color/background/border/opacity,
// none of which touch box-shadow.
const BASE_CLASSES =
  "relative inline-flex items-center justify-center gap-2 rounded-full font-medium transition-[color,background-color,border-color,opacity] " +
  "disabled:opacity-50 disabled:pointer-events-none " +
  FOCUS_RING_CLASSES;

/**
 * Single source of truth for button styling (variant + size), shared by the
 * `<Button>` component below and by `<Link>`-as-button CTAs that can't use a
 * native `<button>` element — see components/ui/README usage in call sites.
 */
export function buttonClasses(
  variant: ButtonVariant = "primary",
  size: ButtonSize = "md",
  className = "",
): string {
  return cn(BASE_CLASSES, VARIANT_CLASSES[variant], SIZE_CLASSES[size], className);
}

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  /** Shows an inline spinner and disables the button without shifting its width. */
  loading?: boolean;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
  { variant = "primary", size = "md", className, loading = false, disabled, children, ...props },
  ref,
) {
  return (
    <button
      ref={ref}
      className={buttonClasses(variant, size, className)}
      disabled={disabled || loading}
      aria-busy={loading || undefined}
      {...props}
    >
      <span className={`inline-flex items-center gap-2 ${loading ? "invisible" : ""}`}>{children}</span>
      {loading && (
        <span className="absolute inset-0 flex items-center justify-center">
          <Spinner className="h-4 w-4" />
        </span>
      )}
    </button>
  );
});
