import { forwardRef } from 'react'
import type { ButtonHTMLAttributes, ReactNode } from 'react'
import { Slot } from 'radix-ui'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
import { Spinner } from './spinner'

const buttonVariants = cva(
  'inline-flex items-center justify-center gap-1.5 font-medium transition-[color,background-color,border-color,box-shadow,transform] duration-200 active:scale-[0.98] focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer',
  {
    variants: {
      variant: {
        primary: 'bg-primary text-text-on-primary hover:bg-primary-hover',
        secondary: 'bg-bg-highlight text-text-on-primary hover:bg-bg-highlight-hover',
        tertiary: 'bg-fill-secondary text-text hover:bg-fill',
        outline: 'border border-border-tertiary bg-bg-container text-text hover:bg-fill-secondary',
        ghost: 'bg-transparent text-text-secondary hover:bg-fill-secondary hover:text-text',
        text: 'bg-transparent text-text-secondary hover:text-text',
        destructive: 'bg-error text-text-on-primary hover:bg-error-hover',
      },
      size: {
        sm: 'h-[var(--control-height-sm)] px-[var(--control-padding-x-sm)] text-[length:var(--control-font-size-sm)] leading-[var(--control-line-height-sm)]',
        md: 'h-[var(--control-height-md)] px-[var(--control-padding-x-md)] text-[length:var(--control-font-size-md)] leading-[var(--control-line-height-md)]',
        lg: 'h-[var(--control-height-lg)] px-[var(--control-padding-x-lg)] text-[length:var(--control-font-size-lg)] leading-[var(--control-line-height-lg)]',
      },
      rounded: {
        square: 'rounded',
        pill: 'rounded-full',
      },
      textOnly: {
        true: 'h-auto px-[var(--control-text-padding-x)] py-[var(--control-text-padding-y)]',
        false: '',
      },
    },
    compoundVariants: [
      { variant: 'text', className: 'px-[var(--control-text-padding-x)]' },
    ],
    defaultVariants: {
      variant: 'ghost',
      size: 'md',
      rounded: 'square',
      textOnly: false,
    },
  }
)

export interface ButtonProps
  extends ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  children: ReactNode
  textButton?: boolean
  prefixIcon?: ReactNode
  suffixIcon?: ReactNode
  loading?: boolean
  asChild?: boolean
}

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      variant = 'ghost',
      children,
      size = 'md',
      textButton = false,
      prefixIcon,
      suffixIcon,
      rounded = 'square',
      disabled = false,
      loading = false,
      asChild = false,
      className,
      ...props
    },
    ref
  ) => {
    const Comp = asChild ? Slot.Root : 'button'
    const isDisabled = disabled || loading
    return (
      <Comp
        ref={ref}
        className={cn(buttonVariants({
          variant: textButton ? 'text' : variant,
          size,
          rounded,
          textOnly: textButton,
        }), className)}
        disabled={isDisabled}
        aria-busy={loading || undefined}
        {...props}
      >
        {asChild ? (
          children
        ) : (
          <>
            {loading ? (
              <Spinner className="shrink-0" />
            ) : prefixIcon ? (
              <span className="inline-flex shrink-0 items-center justify-center [&>*]:block [&>*]:leading-none">
                {prefixIcon}
              </span>
            ) : null}
            {children}
            {suffixIcon && (
              <span className="inline-flex shrink-0 items-center justify-center [&>*]:block [&>*]:leading-none">
                {suffixIcon}
              </span>
            )}
          </>
        )}
      </Comp>
    )
  }
)
Button.displayName = 'Button'

export { Button, buttonVariants }
