'use client'

import { Slot } from '@radix-ui/react-slot'
import { cva } from 'class-variance-authority'
import type * as React from 'react'
import { cn } from '../../utils/cn'
import { typographyVariants } from '../Typography'
import { LoadingRing } from './LoadingRing'

export const sizeVariants = {
  default: cn(
    'max-h-12 min-h-12 px-6 py-3.5 [&_svg]:size-4',
    typographyVariants({ variant: 'body-semi-s' }),
  ),
  sm: cn(
    'max-h-10 min-h-10 px-6 py-3 [&_svg]:size-3',
    typographyVariants({ variant: 'body-semi-xs' }),
  ),
  xs: cn(
    'max-h-8 min-h-8 px-4 py-2 [&_svg]:size-3',
    typographyVariants({ variant: 'body-semi-xs' }),
  ),
  none: cn(
    'w-none min-w-0 max-w-none',
    typographyVariants({ variant: 'body-semi-s' }),
  ),
} as const

export const widthVariants = {
  responsive: 'w-full md:w-fit',
  hug: 'w-fit',
  full: 'w-full',
} as const

export const visualVariants = {
  primary: cn(`bg-primary text-primary-foreground hover:bg-primary-hover`),
  secondary: cn(
    `border border-solid border-secondary-border bg-secondary text-secondary-foreground hover:border-secondary-border-hover`,
  ),
  tertiary: cn(
    `border border-solid border-tertiary-border bg-tertiary text-tertiary-foreground hover:border-tertiary-border-hover`,
  ),
  destructive: cn(
    `bg-destructive text-destructive-foreground hover:bg-destructive-hover`,
  ),
  ghost: cn(`bg-ghost text-ghost-foreground hover:bg-ghost-hover`),
} as const

const buttonVariants = cva(
  cn(
    'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded transition-colors duration-100 ease-linear focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:text-current',
  ),
  {
    variants: {
      size: sizeVariants,
      width: widthVariants,
      loading: {
        true: 'pointer-events-none',
        false: '',
      },
      variant: visualVariants,
    },
    compoundVariants: [
      {
        variant: 'primary',
        loading: true,
        className: cn('bg-primary-disabled text-primary-foreground'),
      },
      {
        variant: 'secondary',
        loading: true,
        className: cn(
          'border-secondary-border-disabled text-secondary-foreground',
        ),
      },
      {
        variant: 'tertiary',
        loading: true,
        className: cn(
          'border-tertiary-border-disabled text-tertiary-foreground',
        ),
      },
      {
        variant: 'ghost',
        loading: true,
        className: cn('bg-ghost-disabled text-ghost-foreground'),
      },
      {
        variant: 'destructive',
        loading: true,
        className: cn('bg-destructive-disabled text-destructive-foreground'),
      },
      {
        variant: 'primary',
        loading: false,
        className:
          'disabled:bg-primary-disabled disabled:text-primary-disabled-foreground',
      },
      {
        variant: 'secondary',
        loading: false,
        className:
          'disabled:border-secondary-border-disabled disabled:text-secondary-disabled-foreground',
      },
      {
        variant: 'tertiary',
        loading: false,
        className:
          'disabled:border-tertiary-border-disabled disabled:text-tertiary-disabled-foreground',
      },
      {
        variant: 'ghost',
        loading: false,
        className:
          'disabled:bg-ghost-disabled disabled:text-ghost-disabled-foreground',
      },
      {
        variant: 'destructive',
        loading: false,
        className:
          'disabled:bg-destructive-disabled disabled:text-destructive-disabled-foreground',
      },
      {
        size: 'default',
        loading: true,
        className: '[&_svg]:size-6',
      },
      {
        size: 'sm',
        loading: true,
        className: '[&_svg]:size-5',
      },
      {
        size: 'xs',
        loading: true,
        className: '[&_svg]:size-4',
      },
      // min-widths for standalone buttons (max-width handled by ButtonSet)
      {
        width: 'hug',
        size: 'default',
        className: 'min-w-[120px]',
      },
      {
        width: 'responsive',
        size: 'default',
        className: 'md:min-w-[120px]',
      },
      {
        width: 'hug',
        size: 'sm',
        className: 'min-w-[96px]',
      },
      {
        width: 'responsive',
        size: 'sm',
        className: 'md:min-w-[96px]',
      },
      {
        width: 'hug',
        size: 'xs',
        className: 'min-w-[72px]',
      },
      {
        width: 'responsive',
        size: 'xs',
        className: 'md:min-w-[72px]',
      },
      // size-based loading svg size variants
      {
        size: 'default',
        loading: true,
        className: '[&_svg]:size-6',
      },
      {
        size: 'sm',
        loading: true,
        className: '[&_svg]:size-5',
      },
      {
        size: 'xs',
        loading: true,
        className: '[&_svg]:size-4',
      },
    ],
    defaultVariants: {
      variant: 'primary',
      size: 'default',
      loading: false,
      width: 'responsive',
    },
  },
)
export type ButtonVariant = keyof typeof visualVariants
export type ButtonSize = Exclude<keyof typeof sizeVariants, 'none'>
export type ButtonWidth = keyof typeof widthVariants

export type ButtonStyleProps = {
  /** Controls the visual hierarchy of the button. */
  variant?: ButtonVariant
  /** Controls the height, padding, and typography density of the button. */
  size?: ButtonSize
  /**
   * Width behavior of the button.
   * - `responsive` (default): full width on mobile, hugs content on desktop.
   * - `hug`: always fits content width.
   * - `full`: always fills the container.
   */
  width?: ButtonWidth
}

/**
 * Button supports all native `<button>` props in addition to the Blocks visual
 * props defined here.
 */
export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    ButtonStyleProps {
  /** Renders the button styles onto the child element via Radix Slot. */
  asChild?: boolean
  /** Shows a loading indicator and disables interaction while the action is in progress. */
  loading?: boolean
  /** Prevents interaction and applies disabled visual styles. */
  disabled?: boolean
  ref?: React.Ref<HTMLButtonElement>
}

/** Interactive button for labeled actions. Supports all native `<button>` props. */
const Button = ({
  className,
  variant,
  size,
  width,
  asChild = false,
  loading = false,
  children,
  ref,
  ...props
}: ButtonProps) => {
  const Comp = asChild ? Slot : 'button'

  return (
    <Comp
      disabled={loading || props.disabled}
      data-slot="button"
      data-size={size}
      className={cn(
        buttonVariants({ variant, size, width, className, loading }),
        loading && 'relative',
      )}
      ref={ref}
      {...props}
    >
      {loading && <LoadingRing className="absolute inset-0 m-auto" />}
      {loading ? (
        // When loading, wrap children in a hidden flex container that preserves the width of the button
        <span className="invisible">{children}</span>
      ) : (
        children
      )}
    </Comp>
  )
}
Button.displayName = 'Button'
export { Button, buttonVariants }
