import * as React from 'react'

type Variant = 'primary' | 'secondary' | 'soft'
type Size = 'xs' | 'sm' | 'md' | 'lg'
type Color = 'indigo' | 'blue' | 'emerald' | 'red' | 'amber'

export type ButtonProps = Omit<
  React.ButtonHTMLAttributes<HTMLButtonElement>,
  'children'
> & {
  children: React.ReactNode
  variant?: Variant
  size?: Size
  color?: Color
  rounded?: 'sm' | 'md' | 'full'
  leadingIcon?: React.ReactNode
  trailingIcon?: React.ReactNode
  isLoading?: boolean
  className?: string
}

function cn(...parts: Array<string | false | null | undefined>) {
  return parts.filter(Boolean).join(' ')
}

const base =
  'inline-flex items-center justify-center font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed'

const roundedMap = {
  sm: 'rounded-sm',
  md: 'rounded-md',
  full: 'rounded-full',
} as const

const sizeMap: Record<Size, string> = {
  xs: 'px-2 py-1 text-xs',
  sm: 'px-2.5 py-1.5 text-sm',
  md: 'px-3 py-2 text-sm',
  lg: 'px-3.5 py-2.5 text-sm',
}

const colorMap: Record<Color, Record<Variant, string>> = {
  indigo: {
    primary:
      '!bg-indigo-600 !text-white shadow-sm hover:!bg-indigo-700 focus-visible:outline-indigo-600 ' +
      'dark:!bg-indigo-500 dark:hover:!bg-indigo-400 dark:focus-visible:outline-indigo-500',
    secondary:
      'bg-white text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50 ' +
      'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20',
    soft:
      'bg-indigo-50 text-indigo-600 shadow-xs hover:bg-indigo-100 ' +
      'dark:bg-indigo-500/20 dark:text-indigo-400 dark:shadow-none dark:hover:bg-indigo-500/30',
  },

  blue: {
    primary:
      '!bg-blue-600 !text-white shadow-sm hover:!bg-blue-700 focus-visible:outline-blue-600 ' +
      'dark:!bg-blue-500 dark:hover:!bg-blue-400 dark:focus-visible:outline-blue-500',
    secondary:
      'bg-white text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50 ' +
      'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20',
    soft:
      'bg-blue-50 text-blue-600 shadow-xs hover:bg-blue-100 ' +
      'dark:bg-blue-500/20 dark:text-blue-400 dark:shadow-none dark:hover:bg-blue-500/30',
  },

  emerald: {
    primary:
      '!bg-emerald-600 !text-white shadow-sm hover:!bg-emerald-700 focus-visible:outline-emerald-600 ' +
      'dark:!bg-emerald-500 dark:hover:!bg-emerald-400 dark:focus-visible:outline-emerald-500',
    secondary:
      'bg-white text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50 ' +
      'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20',
    soft:
      'bg-emerald-50 text-emerald-700 shadow-xs hover:bg-emerald-100 ' +
      'dark:bg-emerald-500/20 dark:text-emerald-400 dark:shadow-none dark:hover:bg-emerald-500/30',
  },

  red: {
    primary:
      '!bg-red-600 !text-white shadow-sm hover:!bg-red-700 focus-visible:outline-red-600 ' +
      'dark:!bg-red-500 dark:hover:!bg-red-400 dark:focus-visible:outline-red-500',
    secondary:
      'bg-white text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50 ' +
      'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20',
    soft:
      'bg-red-50 text-red-700 shadow-xs hover:bg-red-100 ' +
      'dark:bg-red-500/20 dark:text-red-400 dark:shadow-none dark:hover:bg-red-500/30',
  },

  amber: {
    primary:
      '!bg-amber-500 !text-white shadow-sm hover:!bg-amber-600 focus-visible:outline-amber-500 ' +
      'dark:!bg-amber-500 dark:hover:!bg-amber-400 dark:focus-visible:outline-amber-500',
    secondary:
      'bg-white text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50 ' +
      'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20',
    soft:
      'bg-amber-50 text-amber-800 shadow-xs hover:bg-amber-100 ' +
      'dark:bg-amber-500/20 dark:text-amber-300 dark:shadow-none dark:hover:bg-amber-500/30',
  },
}

function Spinner() {
  return (
    <svg viewBox="0 0 24 24" className="size-4 animate-spin" aria-hidden="true">
      <circle
        cx="12"
        cy="12"
        r="10"
        fill="none"
        stroke="currentColor"
        strokeWidth="4"
        opacity="0.25"
      />
      <path
        d="M22 12a10 10 0 0 1-10 10"
        fill="none"
        stroke="currentColor"
        strokeWidth="4"
        opacity="0.9"
      />
    </svg>
  )
}

export default function Button({
  children,
  variant = 'primary',
  color = 'indigo',
  size = 'md',
  rounded = 'md',
  leadingIcon,
  trailingIcon,
  isLoading = false,
  disabled,
  className,
  type = 'button',
  ...props
}: ButtonProps) {
  const iconGap = leadingIcon || trailingIcon || isLoading ? 'gap-x-1.5' : ''

  return (
    <button
      type={type}
      disabled={disabled || isLoading}
      className={cn(
        base,
        roundedMap[rounded],
        sizeMap[size],
        colorMap[color][variant],
        iconGap,
        className
      )}
      {...props}
    >
      {isLoading ? (
        <span className="-ml-0.5">
          <Spinner />
        </span>
      ) : (
        leadingIcon && <span className="-ml-0.5">{leadingIcon}</span>
      )}

      <span>{children}</span>

      {trailingIcon && !isLoading && <span className="-mr-0.5">{trailingIcon}</span>}
    </button>
  )
}
