"use client";

import classNames from "classnames/dedupe";
import type { ChangeEvent } from "react";
import { useRef, useState } from "react";
import type { DOMProps } from "../../types/dom";
import type { UseLabelProps } from "../../hooks";
import { useLabel } from "../../hooks";
import { Label } from "../Label";
import { numberFormatter } from "./helpers";
import "./Slider.css";

export interface SliderProps extends DOMProps, UseLabelProps {
  label?: string;
  variant?: "primary" | "secondary";
  className?: string;
  minValue?: number;
  maxValue?: number;
  defaultValue?: number;
  value?: number;
  step?: number;
  isDisabled?: boolean;
  onChange?: (value: number | undefined) => void;
  onChangeEnd?: (value: number | undefined) => void;
  minLabel?: string;
  maxLabel?: string;
  formatOptions?: Intl.NumberFormatOptions;
}

export const Slider = (props: SliderProps) => {
  const {
    id,
    className,
    label,
    "aria-label": ariaLabel,
    "aria-labelledby": ariaLabelledBy,
    minValue,
    maxValue,
    step = 1,
    defaultValue = 0,
    value,
    onChange,
    onChangeEnd,
    variant = "primary",
    minLabel,
    maxLabel,
    formatOptions,
    isDisabled = false,
  } = props;
  const trackRef = useRef(null);
  const [currentValue, setCurrentValue] = useState<number>(
    value || defaultValue || 0,
  );
  const [isDragging, setIsDraggging] = useState<boolean>(false);
  const { labelProps, fieldProps } = useLabel({
    id,
    label,
    "aria-label": ariaLabel,
    "aria-labelledby": ariaLabelledBy,
  });
  const formattedValue = numberFormatter(currentValue, formatOptions);

  const classes = classNames("mobius", "mobius-slider", className, {
    "--is-disabled": isDisabled,
    "--is-primary": variant === "primary",
    "--is-secondary": variant === "secondary",
    "--is-dragging": isDragging,
  });

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const val = +e.target.value;
    setCurrentValue(val);
    if (onChange) {
      onChange(val);
    }
  };

  const handleChangeStart = () => {
    setIsDraggging(true);
  };

  const handleChangeEnd = () => {
    setIsDraggging(false);
    if (onChangeEnd) {
      onChangeEnd(currentValue);
    }
  };

  return (
    <div className={classes}>
      <div className="mobius-slider__label-wrapper">
        {label && <Label {...labelProps}>{label}</Label>}
        {label && (
          <output style={{ flex: "1 0 auto", textAlign: "end" }}>
            {formattedValue}
          </output>
        )}
      </div>

      {/* The track element holds the visible track line and the thumb. */}
      <div ref={trackRef} className="mobius-slider__track-wrapper">
        <input
          className="mobius-slider__track"
          type="range"
          min={minValue}
          max={maxValue}
          value={currentValue}
          step={step}
          onChange={handleChange}
          autoComplete="off"
          disabled={isDisabled}
          onMouseDown={handleChangeStart}
          onMouseUp={handleChangeEnd}
          onKeyDown={handleChangeStart}
          onKeyUp={handleChangeEnd}
          {...fieldProps}
        />
      </div>

      <div className="mobius-slider__labels">
        <div className="mobius-slider__min-label" data-testid="min-label">
          {minLabel}
        </div>
        <div className="mobius-slider__max-label" data-testid="max-label">
          {maxLabel}
        </div>
      </div>
    </div>
  );
};
