"use client"

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

import { cn } from "@/lib/utils"

type SliderTone = "brand" | "neutral" | "success" | "warning" | "danger"
type SliderSize = "sm" | "default" | "lg"
type SliderVariant = "default" | "pill"
type SliderIndicator =
  | "none"
  | "lower-text"
  | "upper-text"
  | "lower-tooltip"
  | "upper-tooltip"
type SliderThumbLabel =
  | React.ReactNode
  | ((value: number, index: number, values: number[]) => React.ReactNode)

const sliderVariants = cva(
  [
    "group/slider relative flex touch-none select-none items-center",
    "data-[disabled]:pointer-events-none data-[disabled]:opacity-[var(--bh-opacity-60)]",
    "data-[orientation=horizontal]:w-[var(--bh-slider-width)] data-[orientation=horizontal]:max-w-full",
    "data-[orientation=vertical]:h-[var(--bh-slider-vertical-height)] data-[orientation=vertical]:w-fit data-[orientation=vertical]:flex-col",
    "[--bh-slider-track-size-current:var(--bh-slider-track-size-md)]",
    "[--bh-slider-thumb-size-current:var(--bh-slider-thumb-size-md)]",
    "[--bh-slider-thumb-ring-width-current:var(--bh-slider-thumb-ring-width-md)]",
    "[--bh-slider-track-bg:var(--bh-bg-neutral-soft)]",
    "[--bh-slider-range-bg:var(--bh-interactive-indicator-default)]",
    "[--bh-slider-thumb-bg:var(--bh-slider-range-bg)]",
    "[--bh-slider-thumb-ring-bg:var(--bh-bg-always-white)]",
    "[--bh-slider-thumb-pill-bg:var(--bh-bg-brand-subtle)]",
    "[--bh-slider-thumb-pill-border:var(--bh-border-brand-subtle)]",
    "[--bh-slider-thumb-grip-bg:var(--bh-border-brand-strong)]",
  ],
  {
    variants: {
      size: {
        sm: [
          "[--bh-slider-track-size-current:var(--bh-slider-track-size-sm)]",
          "[--bh-slider-thumb-size-current:var(--bh-slider-thumb-size-sm)]",
          "[--bh-slider-thumb-ring-width-current:var(--bh-slider-thumb-ring-width-sm)]",
        ],
        default: [
          "[--bh-slider-track-size-current:var(--bh-slider-track-size-md)]",
          "[--bh-slider-thumb-size-current:var(--bh-slider-thumb-size-md)]",
          "[--bh-slider-thumb-ring-width-current:var(--bh-slider-thumb-ring-width-md)]",
        ],
        lg: [
          "[--bh-slider-track-size-current:var(--bh-slider-track-size-lg)]",
          "[--bh-slider-thumb-size-current:var(--bh-slider-thumb-size-lg)]",
          "[--bh-slider-thumb-ring-width-current:var(--bh-slider-thumb-ring-width-lg)]",
        ],
      },
      tone: {
        brand: [
          "[--bh-slider-range-bg:var(--bh-interactive-indicator-default)]",
          "[--bh-slider-thumb-pill-bg:var(--bh-bg-brand-subtle)]",
          "[--bh-slider-thumb-pill-border:var(--bh-border-brand-subtle)]",
          "[--bh-slider-thumb-grip-bg:var(--bh-border-brand-strong)]",
          "[--bh-slider-pill-track-bg:var(--bh-bg-brand-soft)]",
        ],
        neutral: [
          "[--bh-slider-range-bg:var(--bh-bg-neutral-bold)]",
          "[--bh-slider-thumb-pill-bg:var(--bh-bg-neutral-subtle)]",
          "[--bh-slider-thumb-pill-border:var(--bh-border-subtle)]",
          "[--bh-slider-thumb-grip-bg:var(--bh-border-strong)]",
          "[--bh-slider-pill-track-bg:var(--bh-bg-neutral-soft)]",
        ],
        success: [
          "[--bh-slider-range-bg:var(--bh-bg-success-default)]",
          "[--bh-slider-thumb-pill-bg:var(--bh-bg-success-subtle)]",
          "[--bh-slider-thumb-pill-border:var(--bh-border-success-subtle)]",
          "[--bh-slider-thumb-grip-bg:var(--bh-border-success-strong)]",
          "[--bh-slider-pill-track-bg:var(--bh-bg-success-soft)]",
        ],
        warning: [
          "[--bh-slider-range-bg:var(--bh-bg-warning-default)]",
          "[--bh-slider-thumb-pill-bg:var(--bh-bg-warning-subtle)]",
          "[--bh-slider-thumb-pill-border:var(--bh-border-warning-subtle)]",
          "[--bh-slider-thumb-grip-bg:var(--bh-border-warning-strong)]",
          "[--bh-slider-pill-track-bg:var(--bh-bg-warning-soft)]",
        ],
        danger: [
          "[--bh-slider-range-bg:var(--bh-bg-danger-default)]",
          "[--bh-slider-thumb-pill-bg:var(--bh-bg-danger-subtle)]",
          "[--bh-slider-thumb-pill-border:var(--bh-border-danger-subtle)]",
          "[--bh-slider-thumb-grip-bg:var(--bh-border-danger-strong)]",
          "[--bh-slider-pill-track-bg:var(--bh-bg-danger-soft)]",
        ],
      },
      variant: {
        default: "",
        pill: [
          "[--bh-slider-track-size-current:var(--bh-slider-pill-height)]",
          "[--bh-slider-thumb-size-current:var(--bh-slider-pill-height)]",
          "[--bh-slider-track-bg:var(--bh-slider-pill-track-bg)]",
        ],
      },
    },
    defaultVariants: {
      size: "default",
      tone: "brand",
      variant: "default",
    },
  }
)

type SliderProps = Omit<
  React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,
  "children"
> &
  VariantProps<typeof sliderVariants> & {
    ariaLabels?: string[]
    ariaValueText?:
      | string
      | string[]
      | ((value: number, index: number, values: number[]) => string)
    indicator?: SliderIndicator
    showThumbGrip?: boolean
    showTicks?: boolean
    thumbLabel?: SliderThumbLabel
    thumbLabels?: React.ReactNode[]
    tickCount?: number
  }

function Slider({
  ariaLabels,
  ariaValueText,
  className,
  defaultValue,
  indicator,
  max = 100,
  min = 0,
  onValueChange,
  orientation = "horizontal",
  showThumbGrip,
  showTicks = false,
  size,
  thumbLabel,
  thumbLabels,
  tickCount = 8,
  tone,
  value,
  variant,
  ...props
}: SliderProps) {
  const isControlled = value !== undefined
  const initialValueRef = React.useRef(
    normalizeSliderValue(value ?? defaultValue ?? [min])
  )
  const [uncontrolledValue, setUncontrolledValue] = React.useState(
    initialValueRef.current
  )
  const values = normalizeSliderValue(isControlled ? value : uncontrolledValue)
  const resolvedVariant = variant ?? "default"
  const hasExplicitThumbLabel =
    thumbLabel !== undefined || thumbLabels !== undefined
  const resolvedIndicator =
    indicator ?? (hasExplicitThumbLabel && resolvedVariant !== "pill" ? "lower-text" : "none")
  const shouldShowGrip = showThumbGrip ?? resolvedVariant === "pill"

  return (
    <SliderPrimitive.Root
      data-indicator={resolvedIndicator}
      data-slot="slider"
      data-variant={resolvedVariant}
      defaultValue={isControlled ? undefined : initialValueRef.current}
      max={max}
      min={min}
      onValueChange={(nextValue) => {
        if (!isControlled) {
          setUncontrolledValue(nextValue)
        }
        onValueChange?.(nextValue)
      }}
      orientation={orientation}
      value={value}
      className={cn(sliderVariants({ size, tone, variant }), className)}
      {...props}
    >
      <SliderPrimitive.Track
        data-slot="slider-track"
        onPointerDown={
          resolvedVariant === "pill" ? (event) => event.stopPropagation() : undefined
        }
        className="relative grow overflow-hidden rounded-[var(--bh-slider-track-radius)] bg-[var(--bh-slider-track-bg)] data-[orientation=horizontal]:h-[var(--bh-slider-track-size-current)] data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-[var(--bh-slider-track-size-current)] group-data-[variant=pill]/slider:rounded-[var(--bh-slider-pill-radius)]"
      >
        {showTicks ? (
          <span
            aria-hidden="true"
            data-orientation={orientation}
            data-slot="slider-ticks"
            className="pointer-events-none absolute inset-0 z-[var(--bh-z-base)] flex items-center justify-evenly gap-[var(--bh-slider-tick-gap)] px-[var(--bh-space-6xl-32)] data-[orientation=vertical]:flex-col data-[orientation=vertical]:px-0 data-[orientation=vertical]:py-[var(--bh-space-xl-12)]"
          >
            {Array.from({ length: tickCount }).map((_, index) => (
              <span
                key={index}
                className="block size-[var(--bh-slider-tick-size)] rounded-[var(--bh-radius-full)] bg-[var(--bh-slider-tick-bg)]"
              />
            ))}
          </span>
        ) : null}
        <SliderPrimitive.Range
          data-slot="slider-range"
          className="absolute z-[var(--bh-z-raised)] bg-[var(--bh-slider-range-bg)] data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full group-data-[variant=pill]/slider:bg-transparent"
        />
      </SliderPrimitive.Track>

      {values.map((thumbValue, index) => {
        const label = getThumbLabel({
          index,
          thumbLabel,
          thumbLabels,
          value: thumbValue,
          values,
        })
        const indicatorLabel = hasRenderableContent(label)
          ? label
          : formatSliderValue(thumbValue)
        const showIndicator =
          resolvedVariant !== "pill" && resolvedIndicator !== "none"
        const showInlineLabel =
          resolvedVariant === "pill" && hasRenderableContent(label)

        return (
          <SliderPrimitive.Thumb
            aria-label={getThumbAriaLabel({
              ariaLabels,
              index,
              values,
              fallback: props["aria-label"],
            })}
            aria-valuetext={getThumbAriaValueText({
              ariaValueText,
              index,
              value: thumbValue,
              values,
            })}
            data-has-indicator={showIndicator ? "true" : undefined}
            data-has-label={showInlineLabel ? "true" : undefined}
            data-slot="slider-thumb"
            key={index}
            className="relative z-[var(--bh-z-popover)] flex size-[var(--bh-slider-thumb-size-current)] shrink-0 items-center justify-center rounded-[var(--bh-slider-thumb-radius)] border-[var(--bh-slider-thumb-ring-width-current)] border-[var(--bh-slider-thumb-ring-bg)] bg-[var(--bh-slider-thumb-bg)] text-[var(--bh-content-default)] shadow-[var(--shadow-slider-thumb)] outline-none transition-[background-color,border-color,box-shadow] focus-visible:shadow-[var(--shadow-slider-thumb-focus)] group-data-[variant=pill]/slider:h-[var(--bh-slider-pill-height)] group-data-[variant=pill]/slider:w-[var(--bh-slider-pill-thumb-width)] group-data-[variant=pill]/slider:justify-end group-data-[variant=pill]/slider:rounded-[var(--bh-slider-pill-radius)] group-data-[variant=pill]/slider:border-[length:var(--bh-slider-pill-thumb-border-width)] group-data-[variant=pill]/slider:border-[var(--bh-slider-thumb-pill-border)] group-data-[variant=pill]/slider:bg-[var(--bh-slider-thumb-pill-bg)] group-data-[variant=pill]/slider:px-[var(--bh-space-lg-10)] group-data-[disabled]/slider:border-[var(--bh-bg-default)] group-data-[disabled]/slider:bg-[var(--bh-content-disabled)] group-data-[disabled]/slider:text-[var(--bh-content-disabled)] group-data-[variant=pill]/slider:group-data-[disabled]/slider:border-[var(--bh-border-disabled)] group-data-[variant=pill]/slider:group-data-[disabled]/slider:bg-[var(--bh-bg-disabled)]"
          >
            {showIndicator ? (
              <SliderThumbIndicator
                indicator={resolvedIndicator as Exclude<SliderIndicator, "none">}
              >
                {indicatorLabel}
              </SliderThumbIndicator>
            ) : null}
            {showInlineLabel ? (
              <span
                data-slot="slider-thumb-label"
                className="pointer-events-none me-auto min-w-0 truncate pe-[var(--bh-space-sm-6)] text-[length:var(--bh-text-body-sm-medium-font-size)] leading-[var(--bh-text-body-sm-medium-line-height)] font-[var(--bh-text-body-sm-medium-font-weight)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)]"
              >
                {label}
              </span>
            ) : null}
            {shouldShowGrip ? (
              <span
                aria-hidden="true"
                data-slot="slider-thumb-grip"
                className="pointer-events-none block h-[var(--bh-space-6xl-32)] w-[var(--bh-slider-thumb-grip-width)] shrink-0 rounded-[var(--bh-radius-full)] bg-[var(--bh-slider-thumb-grip-bg)]"
              />
            ) : null}
          </SliderPrimitive.Thumb>
        )
      })}
    </SliderPrimitive.Root>
  )
}

function SliderThumbIndicator({
  children,
  indicator,
}: {
  children: React.ReactNode
  indicator: Exclude<SliderIndicator, "none">
}) {
  const isTooltip = indicator.endsWith("tooltip")
  const isUpper = indicator.startsWith("upper")

  return (
    <span
      data-indicator={indicator}
      data-slot="slider-thumb-indicator"
      dir="auto"
      className={cn(
        "pointer-events-none absolute left-1/2 z-[var(--bh-z-overlay)] -translate-x-1/2 whitespace-nowrap text-center",
        isUpper
          ? "bottom-[calc(100%+var(--bh-slider-indicator-gap))]"
          : "top-[calc(100%+var(--bh-slider-indicator-gap))]",
        isTooltip
          ? "rounded-[var(--bh-radius-md-6)] border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-raised)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] text-[length:var(--bh-text-body-2xs-medium-font-size)] leading-[var(--bh-text-body-2xs-medium-line-height)] font-[var(--bh-text-body-2xs-medium-font-weight)] tracking-[var(--bh-text-body-2xs-medium-letter-spacing)] text-[var(--bh-content-default)] shadow-[var(--shadow-slider-tooltip)]"
          : "text-[length:var(--bh-text-body-2xs-regular-font-size)] leading-[var(--bh-text-body-2xs-regular-line-height)] font-[var(--bh-text-body-2xs-regular-font-weight)] tracking-[var(--bh-text-body-2xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
      )}
    >
      {children}
    </span>
  )
}

function normalizeSliderValue(value: number[] | readonly number[] | undefined) {
  return Array.isArray(value) && value.length > 0 ? [...value] : [0]
}

function formatSliderValue(value: number) {
  return `${Number.isInteger(value) ? value : value.toFixed(1)}%`
}

function hasRenderableContent(value: React.ReactNode) {
  return value !== undefined && value !== null && value !== false && value !== ""
}

function getThumbLabel({
  index,
  thumbLabel,
  thumbLabels,
  value,
  values,
}: {
  index: number
  thumbLabel?: SliderThumbLabel
  thumbLabels?: React.ReactNode[]
  value: number
  values: number[]
}) {
  if (thumbLabels?.[index] !== undefined) return thumbLabels[index]
  if (typeof thumbLabel === "function") return thumbLabel(value, index, values)
  return thumbLabel
}

function getThumbAriaLabel({
  ariaLabels,
  fallback,
  index,
  values,
}: {
  ariaLabels?: string[]
  fallback?: string
  index: number
  values: number[]
}) {
  if (ariaLabels?.[index]) return ariaLabels[index]
  if (fallback && values.length > 1) return `${fallback} ${index + 1}`
  return fallback
}

function getThumbAriaValueText({
  ariaValueText,
  index,
  value,
  values,
}: {
  ariaValueText?: SliderProps["ariaValueText"]
  index: number
  value: number
  values: number[]
}) {
  if (typeof ariaValueText === "function") {
    return ariaValueText(value, index, values)
  }
  if (Array.isArray(ariaValueText)) return ariaValueText[index]
  return ariaValueText
}

export { Slider, sliderVariants }
export type {
  SliderIndicator,
  SliderProps,
  SliderSize,
  SliderThumbLabel,
  SliderTone,
  SliderVariant,
}
