import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

import { BUTTON_INTERACTION_CLASS_NAME } from "@/shared/ui/button-interaction"
import { cn } from "@/shared/ui/cn"
import { Spinner } from "@/shared/ui/spinner"

const buttonVariants = cva(
  cn(
    BUTTON_INTERACTION_CLASS_NAME,
    "inline-flex items-center justify-center gap-2 whitespace-nowrap border border-transparent text-sm font-semibold [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0"
  ),
  {
    variants: {
      variant: {
        default:
          "border-[var(--action-blue)] bg-[var(--action-blue)] text-white shadow-[var(--action-shadow-primary)] [@media(hover:hover)]:hover:border-[var(--action-blue-hover)] [@media(hover:hover)]:hover:bg-[var(--action-blue-hover)] [&:active:not(:disabled)]:border-[var(--action-blue-pressed)] [&:active:not(:disabled)]:bg-[var(--action-blue-pressed)] [&:active:not(:disabled)]:shadow-[var(--action-shadow-primary-pressed)]",
        destructive:
          "border-[var(--action-danger)] bg-[var(--action-danger)] text-white shadow-[var(--action-shadow-secondary)] [@media(hover:hover)]:hover:border-[var(--action-danger-hover)] [@media(hover:hover)]:hover:bg-[var(--action-danger-hover)] [&:active:not(:disabled)]:border-[var(--action-danger-pressed)] [&:active:not(:disabled)]:bg-[var(--action-danger-pressed)] [&:active:not(:disabled)]:shadow-[var(--action-shadow-secondary-pressed)]",
        outline:
          "border-[var(--action-border)] bg-[var(--surface-reading)] text-slate-700 shadow-[var(--action-shadow-secondary)] [@media(hover:hover)]:hover:border-[var(--action-border-hover)] [@media(hover:hover)]:hover:bg-[var(--action-soft)] [@media(hover:hover)]:hover:text-[var(--action-blue-hover)] [&:active:not(:disabled)]:border-[var(--action-border-pressed)] [&:active:not(:disabled)]:bg-[var(--action-soft-pressed)] [&:active:not(:disabled)]:text-[var(--action-blue-pressed)] [&:active:not(:disabled)]:shadow-[var(--action-shadow-secondary-pressed)]",
        secondary:
          "border-[var(--action-border)] bg-[var(--surface-control)] text-slate-700 shadow-[var(--action-shadow-secondary)] [@media(hover:hover)]:hover:border-[var(--action-border-hover)] [@media(hover:hover)]:hover:bg-[var(--action-soft)] [@media(hover:hover)]:hover:text-[var(--action-blue-hover)] [&:active:not(:disabled)]:border-[var(--action-border-pressed)] [&:active:not(:disabled)]:bg-[var(--action-soft-pressed)] [&:active:not(:disabled)]:text-[var(--action-blue-pressed)] [&:active:not(:disabled)]:shadow-[var(--action-shadow-secondary-pressed)]",
        ghost:
          "bg-transparent text-slate-600 shadow-none [@media(hover:hover)]:hover:bg-[var(--action-soft)] [@media(hover:hover)]:hover:text-[var(--action-blue-hover)] [&:active:not(:disabled)]:bg-[var(--action-soft-pressed)] [&:active:not(:disabled)]:text-[var(--action-blue-pressed)] [&:active:not(:disabled)]:shadow-none",
        link:
          "h-auto border-0 bg-transparent p-0 text-[var(--action-blue)] shadow-none underline-offset-4 [@media(hover:hover)]:hover:text-[var(--action-blue-hover)] [@media(hover:hover)]:hover:underline [&:active:not(:disabled)]:text-[var(--action-blue-pressed)] [&:active:not(:disabled)]:shadow-none"
      },
      size: {
        default: "h-10 rounded-xl px-4 py-2",
        sm: "h-8 rounded-lg px-3 text-xs after:absolute after:-inset-1 after:content-['']",
        lg: "h-11 rounded-xl px-6",
        icon: "h-10 w-10 rounded-xl",
        "icon-sm":
          "h-8 w-8 rounded-lg after:absolute after:-inset-1 after:content-['']"
      }
    },
    defaultVariants: {
      variant: "default",
      size: "default"
    }
  }
)

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

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      children,
      className,
      variant,
      size,
      asChild = false,
      disabled,
      loading = false,
      loadingLabel = "处理中",
      onClick,
      tabIndex,
      ...props
    },
    ref
  ) => {
    const Comp = asChild ? Slot : "button"
    const blocked = disabled || loading
    const childContent =
      asChild && React.isValidElement<{ children?: React.ReactNode }>(children)
        ? children.props.children
        : children
    const loadingContent = loading ? (
      <>
        <span aria-hidden="true" className="invisible inline-flex items-center gap-2">
          {childContent}
        </span>
        <Spinner
          aria-hidden="true"
          className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
        />
        <span className="sr-only">{loadingLabel}</span>
      </>
    ) : null
    const renderedChildren =
      loading && asChild && React.isValidElement(children)
        ? React.cloneElement(children, undefined, loadingContent)
        : loadingContent ?? children

    return (
      <Comp
        aria-busy={loading || undefined}
        aria-disabled={asChild && blocked ? true : undefined}
        className={cn(buttonVariants({ variant, size, className }))}
        data-loading={loading || undefined}
        data-slot="button"
        disabled={asChild ? undefined : blocked}
        onClick={(event) => {
          if (asChild && blocked) {
            event.preventDefault()
            event.stopPropagation()
            return
          }
          onClick?.(event)
        }}
        ref={ref}
        tabIndex={asChild && blocked ? -1 : tabIndex}
        {...props}
      >
        {renderedChildren}
      </Comp>
    )
  }
)
Button.displayName = "Button"

export { Button }
