"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 ProgressSliderTone = "brand" | "neutral" | "success" | "warning" | "danger"
type ProgressSliderSize = "default" | "lg"

const progressSliderVariants = cva(
  [
    "group/progress-slider relative flex touch-none select-none items-center",
    "w-[var(--bh-progress-slider-width-current)] max-w-full",
    "data-[disabled]:pointer-events-none data-[disabled]:opacity-[var(--bh-opacity-60)]",
    "[--bh-progress-slider-width-current:var(--bh-progress-slider-width-md)]",
    "[--bh-progress-slider-height-current:var(--bh-progress-slider-height-md)]",
    "[--bh-progress-slider-handle-width-current:var(--bh-progress-slider-handle-width-md)]",
    "[--bh-progress-slider-border-width-current:var(--bh-progress-slider-border-width-md)]",
    "[--bh-progress-slider-grip-height-current:var(--bh-progress-slider-grip-height-md)]",
    "[--bh-progress-slider-track-bg:var(--bh-bg-brand-soft)]",
    "[--bh-progress-slider-fill-bg:var(--bh-bg-brand-subtle)]",
    "[--bh-progress-slider-fill-border:var(--bh-border-brand-subtle)]",
    "[--bh-progress-slider-grip-bg:var(--bh-border-brand-strong)]",
  ],
  {
    variants: {
      size: {
        default: [
          "[--bh-progress-slider-width-current:var(--bh-progress-slider-width-md)]",
          "[--bh-progress-slider-height-current:var(--bh-progress-slider-height-md)]",
          "[--bh-progress-slider-handle-width-current:var(--bh-progress-slider-handle-width-md)]",
          "[--bh-progress-slider-border-width-current:var(--bh-progress-slider-border-width-md)]",
          "[--bh-progress-slider-grip-height-current:var(--bh-progress-slider-grip-height-md)]",
        ],
        lg: [
          "[--bh-progress-slider-width-current:var(--bh-progress-slider-width-lg)]",
          "[--bh-progress-slider-height-current:var(--bh-progress-slider-height-lg)]",
          "[--bh-progress-slider-handle-width-current:var(--bh-progress-slider-handle-width-lg)]",
          "[--bh-progress-slider-border-width-current:var(--bh-progress-slider-border-width-lg)]",
          "[--bh-progress-slider-grip-height-current:var(--bh-progress-slider-grip-height-lg)]",
        ],
      },
      tone: {
        brand: [
          "[--bh-progress-slider-track-bg:var(--bh-bg-brand-soft)]",
          "[--bh-progress-slider-fill-bg:var(--bh-bg-brand-subtle)]",
          "[--bh-progress-slider-fill-border:var(--bh-border-brand-subtle)]",
          "[--bh-progress-slider-grip-bg:var(--bh-border-brand-strong)]",
        ],
        neutral: [
          "[--bh-progress-slider-track-bg:var(--bh-bg-neutral-soft)]",
          "[--bh-progress-slider-fill-bg:var(--bh-bg-neutral-subtle)]",
          "[--bh-progress-slider-fill-border:var(--bh-border-subtle)]",
          "[--bh-progress-slider-grip-bg:var(--bh-border-strong)]",
        ],
        success: [
          "[--bh-progress-slider-track-bg:var(--bh-bg-success-soft)]",
          "[--bh-progress-slider-fill-bg:var(--bh-bg-success-subtle)]",
          "[--bh-progress-slider-fill-border:var(--bh-border-success-subtle)]",
          "[--bh-progress-slider-grip-bg:var(--bh-border-success-strong)]",
        ],
        warning: [
          "[--bh-progress-slider-track-bg:var(--bh-bg-warning-soft)]",
          "[--bh-progress-slider-fill-bg:var(--bh-bg-warning-subtle)]",
          "[--bh-progress-slider-fill-border:var(--bh-border-warning-subtle)]",
          "[--bh-progress-slider-grip-bg:var(--bh-border-warning-strong)]",
        ],
        danger: [
          "[--bh-progress-slider-track-bg:var(--bh-bg-danger-soft)]",
          "[--bh-progress-slider-fill-bg:var(--bh-bg-danger-subtle)]",
          "[--bh-progress-slider-fill-border:var(--bh-border-danger-subtle)]",
          "[--bh-progress-slider-grip-bg:var(--bh-border-danger-strong)]",
        ],
      },
    },
    defaultVariants: {
      size: "default",
      tone: "brand",
    },
  }
)

type ProgressSliderProps = Omit<
  React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,
  | "children"
  | "defaultValue"
  | "onValueChange"
  | "onValueCommit"
  | "orientation"
  | "value"
> &
  VariantProps<typeof progressSliderVariants> & {
    ariaValueText?: string | ((value: number) => string)
    defaultValue?: number
    onValueChange?: (value: number) => void
    onValueCommit?: (value: number) => void
    showTicks?: boolean
    tickCount?: number
    value?: number
  }

function ProgressSlider({
  ariaValueText,
  className,
  defaultValue,
  max = 100,
  min = 0,
  onValueChange,
  onValueCommit,
  showTicks = false,
  size,
  step,
  tickCount = 8,
  tone,
  value,
  ...props
}: ProgressSliderProps) {
  const isControlled = value !== undefined
  const initialValueRef = React.useRef(
    normalizeProgressValue(value ?? defaultValue ?? min, min, max)
  )
  const [uncontrolledValue, setUncontrolledValue] = React.useState(
    initialValueRef.current
  )
  const currentValue = normalizeProgressValue(
    isControlled ? value : uncontrolledValue,
    min,
    max
  )
  const sliderValue = [currentValue]

  return (
    <SliderPrimitive.Root
      className={cn(progressSliderVariants({ size, tone }), className)}
      data-slot="progress-slider"
      defaultValue={isControlled ? undefined : [initialValueRef.current]}
      max={max}
      min={min}
      onValueChange={(nextValue) => {
        const nextProgressValue = normalizeProgressValue(
          nextValue[0] ?? min,
          min,
          max
        )

        if (!isControlled) {
          setUncontrolledValue(nextProgressValue)
        }

        onValueChange?.(nextProgressValue)
      }}
      onValueCommit={(nextValue) => {
        onValueCommit?.(normalizeProgressValue(nextValue[0] ?? min, min, max))
      }}
      orientation="horizontal"
      step={step}
      value={isControlled ? sliderValue : undefined}
      {...props}
    >
      <SliderPrimitive.Track
        className="relative h-[var(--bh-progress-slider-height-current)] w-full grow overflow-hidden rounded-[var(--bh-progress-slider-radius)] bg-[var(--bh-progress-slider-track-bg)]"
        data-slot="progress-slider-track"
      >
        <SliderPrimitive.Range
          className="absolute inset-y-0 z-[var(--bh-z-raised)] rounded-[var(--bh-progress-slider-radius)] border-[length:var(--bh-progress-slider-border-width-current)] border-[var(--bh-progress-slider-fill-border)] bg-[var(--bh-progress-slider-fill-bg)]"
          data-slot="progress-slider-range"
        />
        {showTicks ? (
          <span
            aria-hidden="true"
            className="pointer-events-none absolute inset-0 z-[var(--bh-z-popover)] flex items-center justify-evenly gap-[var(--bh-progress-slider-tick-gap)] px-[var(--bh-progress-slider-tick-padding)]"
            data-slot="progress-slider-ticks"
          >
            {Array.from({ length: tickCount }).map((_, index) => (
              <span
                className="block size-[var(--bh-progress-slider-tick-size)] rounded-[var(--bh-radius-full)] bg-[var(--bh-progress-slider-tick-bg)]"
                key={index}
              />
            ))}
          </span>
        ) : null}
      </SliderPrimitive.Track>

      <SliderPrimitive.Thumb
        aria-label={props["aria-label"]}
        aria-valuetext={getProgressAriaValueText(ariaValueText, currentValue)}
        className="relative z-[var(--bh-z-overlay)] flex h-[var(--bh-progress-slider-height-current)] w-[var(--bh-progress-slider-handle-width-current)] shrink-0 items-center justify-center rounded-[var(--bh-progress-slider-radius)] bg-transparent outline-none transition-[box-shadow] focus-visible:shadow-[var(--shadow-progress-slider-thumb-focus)]"
        data-slot="progress-slider-thumb"
      >
        <span
          aria-hidden="true"
          className="pointer-events-none block h-[var(--bh-progress-slider-grip-height-current)] w-[var(--bh-progress-slider-grip-width)] rounded-[var(--bh-radius-full)] bg-[var(--bh-progress-slider-grip-bg)] group-data-[disabled]/progress-slider:bg-[var(--bh-content-disabled)]"
          data-slot="progress-slider-grip"
        />
      </SliderPrimitive.Thumb>
    </SliderPrimitive.Root>
  )
}

function normalizeProgressValue(value: number | undefined, min: number, max: number) {
  const nextValue = Number.isFinite(value) ? Number(value) : min
  return Math.min(Math.max(nextValue, min), max)
}

function getProgressAriaValueText(
  ariaValueText: ProgressSliderProps["ariaValueText"],
  value: number
) {
  if (typeof ariaValueText === "function") return ariaValueText(value)
  return ariaValueText
}

export { ProgressSlider, progressSliderVariants }
export type { ProgressSliderProps, ProgressSliderSize, ProgressSliderTone }
