'use client';

import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { Loader2 } from 'lucide-react';

import { cn } from '../lib/cn';

const buttonVariants = cva(
  cn(
    'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-control)]',
    'font-medium transition-colors',
    'disabled:pointer-events-none disabled:opacity-55',
    // Icons inside buttons should never capture the pointer.
    '[&_svg]:pointer-events-none [&_svg]:shrink-0',
  ),
  {
    variants: {
      variant: {
        primary: 'bg-brand-600 text-white hover:bg-brand-700 active:bg-brand-800',
        secondary:
          'bg-white text-ink border border-border-strong hover:bg-surface-muted active:bg-surface-sunken',
        ghost: 'text-ink-muted hover:bg-surface-sunken hover:text-ink',
        // Destructive actions are visually distinct AND always paired with a
        // confirmation dialog at the call site (SPEC.md §6).
        danger: 'bg-danger text-white hover:brightness-110 active:brightness-95',
        link: 'text-brand-700 underline-offset-4 hover:underline p-0 h-auto',
      },
      size: {
        sm: 'h-8 px-3 text-sm [&_svg]:size-4',
        md: 'h-9.5 px-4 text-sm [&_svg]:size-4',
        lg: 'h-11 px-5 text-base [&_svg]:size-5',
        icon: 'h-9.5 w-9.5 [&_svg]:size-4',
      },
    },
    defaultVariants: { variant: 'primary', size: 'md' },
  },
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  /** Renders the child element instead of a <button>, for links styled as buttons. */
  asChild?: boolean;
  /**
   * Shows a spinner and disables the control. The accessible name is preserved
   * so a screen reader still announces what the button does, and `aria-busy`
   * communicates the pending state.
   */
  loading?: boolean;
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
  { className, variant, size, asChild = false, loading = false, disabled, children, ...props },
  ref,
) {
  /*
   * With `asChild`, Radix's Slot merges props onto the single child element and
   * requires exactly one. Injecting a spinner alongside it would pass two
   * children and fail at render, so the loading affordance applies only to a
   * real <button>. A link styled as a button has nothing to load anyway — it
   * navigates.
   */
  if (asChild) {
    return (
      <Slot ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props}>
        {children}
      </Slot>
    );
  }

  return (
    <button
      ref={ref}
      className={cn(buttonVariants({ variant, size }), className)}
      disabled={disabled || loading}
      aria-busy={loading || undefined}
      {...props}
    >
      {loading ? <Loader2 className="animate-spin" aria-hidden="true" /> : null}
      {children}
    </button>
  );
});

export { buttonVariants };
