---
import { Icon } from 'astro-icon/components'
import { tv, type VariantProps } from 'tailwind-variants'

import type { HTMLAttributes, Polymorphic } from 'astro/types'

type Props<Tag extends 'a' | 'button' | 'label' = 'button'> = Polymorphic<
  Required<Pick<HTMLAttributes<'label'>, Tag extends 'label' ? 'for' : never>> &
    VariantProps<typeof button> & {
      as: Tag
      label?: string
      icon?: string
      endIcon?: string
      classNames?: {
        label?: string
        icon?: string
        endIcon?: string
      }
      dataAstroReload?: boolean
      children?: never
      disabled?: boolean
      inlineIcon?: boolean
    }
>

export type ButtonProps<Tag extends 'a' | 'button' | 'label' = 'button'> = Props<Tag>

const button = tv({
  slots: {
    base: 'inline-flex items-center justify-center gap-2 rounded-lg border transition-colors duration-100 focus-visible:ring-2 focus-visible:ring-current focus-visible:ring-offset-2 focus-visible:ring-offset-black focus-visible:outline-hidden',
    icon: 'size-4 shrink-0',
    label: 'text-left whitespace-nowrap',
    endIcon: 'size-4 shrink-0',
  },
  variants: {
    size: {
      sm: {
        base: 'h-8 px-3 text-sm',
        icon: 'size-4',
        endIcon: 'size-4',
      },
      md: {
        base: 'h-9 px-4 text-sm',
        icon: 'size-4',
        endIcon: 'size-4',
        label: 'font-medium',
      },
      lg: {
        base: 'h-10 px-5 text-base',
        icon: 'size-5',
        endIcon: 'size-5',
        label: 'font-bold tracking-wider uppercase',
      },
    },
    color: {
      black: {
        base: 'border-night-500 bg-night-800 hover:bg-night-900 hover:text-day-200 focus-visible:bg-night-500 text-white/50 focus-visible:text-white focus-visible:ring-white',
      },
      white: {
        base: 'border-day-300 bg-day-100 hover:bg-day-200 text-black focus-visible:ring-green-500',
      },
      gray: {
        base: 'border-day-500 bg-day-400 hover:bg-day-500 text-black focus-visible:ring-white',
      },
      success: {
        base: 'border-green-600 bg-green-500 text-black hover:bg-green-600',
      },
      error: {
        base: 'border-red-600 bg-red-500 text-white hover:bg-red-600',
      },
      warning: {
        base: 'border-yellow-600 bg-yellow-500 text-white hover:bg-yellow-600',
      },
      info: {
        base: 'border-blue-600 bg-blue-500 text-white hover:bg-blue-600',
      },
    },
    shadow: {
      true: {
        base: 'shadow-lg',
      },
    },
    disabled: {
      true: {
        base: 'cursor-not-allowed',
      },
    },
  },
  compoundVariants: [
    {
      color: 'black',
      shadow: true,
      class: 'shadow-black/30',
    },
    {
      color: 'white',
      shadow: true,
      class: 'shadow-white/30',
    },
    {
      color: 'gray',
      shadow: true,
      class: 'shadow-day-500/30',
    },
    {
      color: 'success',
      shadow: true,
      class: 'shadow-green-500/30',
    },
    {
      color: 'error',
      shadow: true,
      class: 'shadow-red-500/30',
    },
    {
      color: 'warning',
      shadow: true,
      class: 'shadow-yellow-500/30',
    },
    {
      color: 'info',
      shadow: true,
      class: 'shadow-blue-500/30',
    },
  ],
  defaultVariants: {
    size: 'md',
    color: 'black',
    shadow: false,
    disabled: false,
  },
})

const {
  as: Tag = 'button' as 'a' | 'button' | 'label',
  label,
  icon,
  endIcon,
  size,
  color,
  shadow,
  class: className,
  classNames,
  role,
  dataAstroReload,
  disabled,
  inlineIcon,
  ...htmlProps
} = Astro.props

const {
  base,
  icon: iconSlot,
  label: labelSlot,
  endIcon: endIconSlot,
} = button({ size, color, shadow, disabled })

const ActualTag = disabled && Tag === 'a' ? 'span' : Tag
---

<ActualTag
  class={base({ class: className })}
  role={role ??
    (ActualTag === 'button' || ActualTag === 'label' || ActualTag === 'span' ? undefined : 'button')}
  aria-disabled={disabled}
  {...dataAstroReload && { 'data-astro-reload': dataAstroReload }}
  {...htmlProps}
>
  {!!icon && <Icon name={icon} class={iconSlot({ class: classNames?.icon })} is:inline={inlineIcon} />}
  {!!label && <span class={labelSlot({ class: classNames?.label })}>{label}</span>}
  {
    !!endIcon && (
      <Icon name={endIcon} class={endIconSlot({ class: classNames?.endIcon })} is:inline={inlineIcon}>
        {endIcon}
      </Icon>
    )
  }
</ActualTag>
