import type { InputHTMLAttributes } from 'react';
import { forwardRef, useId, useMemo } from 'react';
import './range-slider.css';

export type RangeSliderProps = {
  label?: string;
  helperText?: string;
  error?: boolean;
  errorMessage?: string;
  /** Emits the numeric value on each change. */
  onValueChange?: (value: number) => void;
  /** Show current value next to the label. */
  showValue?: boolean;
  /** Optional formatting for the displayed value. */
  formatValue?: (value: number) => string;
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange'> & {
    onChange?: InputHTMLAttributes<HTMLInputElement>['onChange'];
  };

export const RangeSlider = forwardRef<HTMLInputElement, RangeSliderProps>(function RangeSlider(
  {
    label = '',
    helperText = '',
    error = false,
    errorMessage = '',
    disabled = false,
    required,
    id: idProp,
    value,
    defaultValue,
    onChange,
    onValueChange,
    showValue = true,
    formatValue,
    className,
    'aria-label': ariaLabelProp,
    'aria-describedby': ariaDescribedByProp,
    min = 0,
    max = 100,
    step = 1,
    ...rest
  },
  ref
) {
  const uid = useId();
  const inputId = idProp ?? `ccl-range-${uid.replace(/:/g, '')}`;
  const hintId = `${inputId}-hint`;
  const errorId = `${inputId}-error`;

  const describedBy = useMemo(() => {
    const ids: string[] = [];
    if (ariaDescribedByProp) ids.push(ariaDescribedByProp);
    if (helperText) ids.push(hintId);
    if (error && errorMessage) ids.push(errorId);
    return ids.length ? ids.join(' ') : undefined;
  }, [ariaDescribedByProp, error, errorId, errorMessage, helperText, hintId]);

  const numericValue = useMemo(() => {
    const raw = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : undefined;
    if (raw != null && !Number.isNaN(raw)) return raw;
    const dv = typeof defaultValue === 'number' ? defaultValue : typeof defaultValue === 'string' ? Number(defaultValue) : undefined;
    if (dv != null && !Number.isNaN(dv)) return dv;
    return undefined;
  }, [defaultValue, value]);

  const displayValue = useMemo(() => {
    if (numericValue == null) return '';
    return formatValue ? formatValue(numericValue) : String(numericValue);
  }, [formatValue, numericValue]);

  const classes = ['ccl-range', error ? 'ccl-range--error' : '', disabled ? 'ccl-range--disabled' : '', className ?? '']
    .filter(Boolean)
    .join(' ');

  return (
    <div className="ccl-range-wrapper">
      {label ? (
        <label className="ccl-range__label" htmlFor={inputId}>
          <span className="ccl-range__label-text">
            {label}
            {required ? (
              <span className="ccl-range__required" aria-label="required">
                *
              </span>
            ) : null}
          </span>
          {showValue && numericValue != null ? <span className="ccl-range__value">{displayValue}</span> : null}
        </label>
      ) : null}

      <input
        ref={ref}
        id={inputId}
        className={classes}
        type="range"
        disabled={disabled}
        required={required}
        value={value}
        defaultValue={defaultValue}
        min={min}
        max={max}
        step={step}
        aria-label={ariaLabelProp}
        aria-describedby={describedBy}
        aria-invalid={error}
        onChange={(e) => {
          onChange?.(e);
          onValueChange?.(Number(e.currentTarget.value));
        }}
        {...rest}
      />

      {helperText ? (
        <span id={hintId} className="ccl-range__helper">
          {helperText}
        </span>
      ) : null}
      {error && errorMessage ? (
        <span id={errorId} className="ccl-range__error" role="alert">
          {errorMessage}
        </span>
      ) : null}
    </div>
  );
});

