"use client"

import { cva, type VariantProps } from 'class-variance-authority';
import { Loader2 } from 'lucide-react';
import * as React from 'react';

import { Slot } from '@radix-ui/react-slot';

import { cn } from '../../../lib/utils';
import { Link } from '../../navigation/link';

const buttonVariants = cva(
  "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius)] text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground shadow hover:bg-primary/90",
        destructive:
          "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
        outline:
          "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-[var(--radius)] px-3 text-sm",
        xs: "h-7 rounded px-2 py-1 text-xs",
        lg: "h-11 rounded-[var(--radius)] px-8",
        huge: "h-14 rounded-[var(--radius)] px-10 text-lg",
        icon: "h-9 w-9",
      },
      // Most variants ship a baked-in `shadow`/`shadow-sm` for a lifted look.
      // `elevated={false}` flattens that (e.g. controls grouped inside a card —
      // a table's pagination — where a per-button shadow fights the panel).
      // `shadow-none` wins over the variant's shadow: both set `--tw-shadow`
      // and this class is appended after the variant in the utility order.
      elevated: {
        true: "",
        false: "shadow-none",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
      elevated: true,
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
  loading?: boolean
}

// Filter out SVG icons from children when loading
function filterIcons(children: React.ReactNode): React.ReactNode {
  return React.Children.map(children, (child) => {
    if (!React.isValidElement(child)) return child;

    const type = child.type;
    // Skip native svg or ForwardRef components (Lucide icons)
    if (type === 'svg' || (typeof type === 'object' && type !== null)) {
      return null;
    }

    return child;
  });
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, elevated, asChild = false, loading = false, onClick, children, disabled, ...props }, ref) => {
    const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
      if (!loading) {
        onClick?.(e);
      }
    };

    // When asChild is true, Slot expects exactly ONE child element
    // Don't render loading spinner with asChild to avoid "Children.only" error
    if (asChild) {
      return (
        <Slot
          className={cn(buttonVariants({ variant, size, elevated, className }))}
          ref={ref}
          onClick={onClick}
          {...props}
        >
          {children}
        </Slot>
      )
    }

    return (
      <button
        className={cn(buttonVariants({ variant, size, elevated, className }))}
        ref={ref}
        onClick={handleClick}
        disabled={disabled || loading}
        {...props}
      >
        {loading && <Loader2 className="size-4 animate-spin" />}
        {loading ? filterIcons(children) : children}
      </button>
    )
  }
)
Button.displayName = "Button"

/**
 * ButtonLink — anchor styled as a Button.
 *
 * Renders the framework-agnostic `<Link>` underneath, so SPA navigation,
 * cmd-click, and the Next.js adapter (if mounted) all work out of the box.
 * Drop-in replacement for the previous native-`<a>` version.
 */
/**
 * ButtonLink — anchor styled as a Button.
 *
 * **Disabled / loading.** An `<a>` has no native `disabled`, so we emulate it
 * the shadcn-recommended way: `aria-disabled` + `tabIndex={-1}` +
 * `pointer-events-none opacity-50`, and we cancel the click. `loading` adds the
 * same inert state plus a spinner — keeping parity with `<Button>`.
 */
export interface ButtonLinkProps
  extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>,
    VariantProps<typeof buttonVariants> {
  href: string
  /** Use replaceState instead of pushState. Default: false. */
  replace?: boolean
  /** Scroll to top after SPA navigation. Default: false. */
  scroll?: boolean
  /** Hint for the host framework's prefetcher (Next.js). */
  prefetch?: boolean | null
  /** Inert + dimmed; click is cancelled and it's removed from tab order. */
  disabled?: boolean
  /** Inert + dimmed + spinner (e.g. while an action resolves). */
  loading?: boolean
}

const ButtonLink = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
  (
    { className, variant, size, elevated, href, replace, scroll, prefetch, disabled = false, loading = false, onClick, children, ...props },
    ref,
  ) => {
    // `<a>` can't be natively disabled — emulate it (aria + tab-order + no
    // pointer events) and hard-cancel the click so navigation can't fire.
    const inert = disabled || loading
    const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
      if (inert) {
        e.preventDefault()
        return
      }
      onClick?.(e)
    }

    return (
      <Link
        href={href}
        replace={replace}
        scroll={scroll}
        prefetch={prefetch}
        className={cn(
          buttonVariants({ variant, size, elevated, className }),
          inert && 'pointer-events-none opacity-50',
        )}
        ref={ref}
        onClick={handleClick}
        aria-disabled={inert || undefined}
        tabIndex={inert ? -1 : undefined}
        {...props}
      >
        {loading && <Loader2 className="size-4 animate-spin" />}
        {loading ? filterIcons(children) : children}
      </Link>
    )
  }
)
ButtonLink.displayName = "ButtonLink"

export { Button, ButtonLink, buttonVariants }
