// @design-system: primitives/Slider
'use client';

import * as RadixSlider from '@radix-ui/react-slider';
import { useState } from 'react';
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '@/components/ui/overlays/Tooltip';
import { cn } from '@/lib/cn';

/** Map Arabic-Indic / Eastern Arabic digits to ASCII before parsing typed range values. */
function normalizeRangeDigits(raw: string): string {
  return raw
    .replace(/[٠-٩]/g, (d) => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d)))
    .replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)));
}

/** Strip currency/decoration and parse a whole-shekel range endpoint from formatted or raw text. */
export function parseRangeEndpoint(raw: string, bound: number): number | null {
  const trimmed = normalizeRangeDigits(raw.trim());
  const isOverflow = trimmed.endsWith('+');
  const withoutPlus = trimmed.replace(/\+$/, '');
  const digitParts = withoutPlus.replace(/[^\d.]/g, '').split('.');
  const digits =
    digitParts.length > 1
      ? `${digitParts[0] ?? ''}.${digitParts.slice(1).join('')}`
      : (digitParts[0] ?? '');
  if (isOverflow && digits.length === 0) return bound;
  if (digits.length === 0 || digits === '.') return null;
  const n = Math.round(parseFloat(digits));
  if (Number.isNaN(n)) return null;
  return isOverflow && n >= bound ? bound : n;
}

export interface SliderProps {
  min: number;
  max: number;
  value: [number, number];
  step?: number;
  onChange: (value: [number, number]) => void;
  formatLabel?: (v: number) => string;
  /** Label for the current min handle; defaults to formatLabel ?? String. */
  formatMinLabel?: (v: number) => string;
  /** Label for the current max handle; defaults to formatLabel ?? String. */
  formatMaxLabel?: (v: number) => string;
  /** Show an editable "min-max" text input between the range labels */
  showRangeInput?: boolean;
  /**
   * Availability bars rendered above the track, one per element, evenly spaced
   * left→right across [min,max]. Bucket i spans
   *   [min + i*w, min + (i+1)*w),  w = (max-min)/histogram.length.
   * Heights normalized to the max count; in-range buckets are brand-colored.
   * Decorative (aria-hidden). Omit → no bars.
   */
  histogram?: number[];
  /**
   * Builds the hover tooltip text for histogram bar i (count = histogram[i],
   * [lo, hi) = the bar's price-axis span). When provided AND histogram is present,
   * each bar gains a pointer-hover Radix tooltip. Bars are never controls: the
   * chart stays decorative (aria-hidden) and outside the tab order either way.
   */
  formatBarTooltip?: (count: number, lo: number, hi: number) => string;
  className?: string;
  'aria-label'?: string;
}

/**
 * Slider — dual-handle range slider primitive.
 * Math/price scales are always LTR (0 left, max right) regardless of page locale.
 *
 * @example
 * ```tsx
 * <Slider
 *   min={0} max={2000}
 *   value={priceRange}
 *   onChange={setPriceRange}
 *   formatLabel={formatShekelFloat}
 *   showRangeInput
 * />
 * ```
 */
export function Slider({
  min,
  max,
  value,
  step = 1,
  onChange,
  formatLabel,
  formatMinLabel,
  formatMaxLabel,
  showRangeInput = false,
  histogram,
  formatBarTooltip,
  className,
  'aria-label': ariaLabel,
}: SliderProps) {
  const fmt = formatLabel ?? String;
  const fmtMin = formatMinLabel ?? fmt;
  const fmtMax = formatMaxLabel ?? fmt;

  const rangeInputDisplay = `${fmtMin(value[0])}-${fmtMax(value[1])}`;

  const [inputVal, setInputVal] = useState(rangeInputDisplay);
  const [prevValue, setPrevValue] = useState(value);

  // Sync inputVal when the controlled value changes from outside (prev-renders pattern).
  if (prevValue !== value) {
    setPrevValue(value);
    setInputVal(rangeInputDisplay);
  }

  function commitInput(raw: string) {
    const parts = raw.split('-');
    if (parts.length !== 2) return;
    const loParsed = parseRangeEndpoint(parts[0] ?? '', max);
    const hiParsed = parseRangeEndpoint(parts[1] ?? '', max);
    if (loParsed === null || hiParsed === null) return;
    const lo = Math.min(Math.max(loParsed, min), max);
    const hi = Math.min(Math.max(hiParsed, min), max);
    onChange([Math.min(lo, hi), Math.max(lo, hi)]);
  }

  return (
    <div className={cn('flex flex-col gap-2', className)}>
      {histogram && histogram.length > 0 && (
        <div
          data-slot="histogram"
          aria-hidden="true"
          dir="ltr"
          className={cn(
            'flex h-10 items-end gap-px px-2',
            !formatBarTooltip && 'pointer-events-none',
          )}
        >
          {(() => {
            const maxCount = Math.max(...histogram, 1);
            const width = (max - min) / histogram.length;
            const bars = histogram.map((count, i) => {
              const lo = min + i * width;
              const hi = min + (i + 1) * width;
              const inRange = hi > value[0] && lo < value[1];
              const isEmpty = count === 0;
              const barHeight = isEmpty ? '3px' : `${Math.max(8, (count / maxCount) * 100)}%`;
              const barClass = cn(
                'flex-1 transition-colors',
                isEmpty ? 'rounded-full' : 'rounded-sm',
                inRange ? 'bg-brand-primary-400' : 'bg-neutral-200',
              );
              if (!formatBarTooltip) {
                return (
                  <div
                    key={i}
                    data-slot="histogram-bar"
                    data-in-range={inRange ? 'true' : 'false'}
                    style={{ height: barHeight }}
                    className={barClass}
                  />
                );
              }
              const label = formatBarTooltip(count, lo, hi);
              return (
                <Tooltip key={i}>
                  <TooltipTrigger asChild>
                    <div
                      data-slot="histogram-bar"
                      data-in-range={inRange ? 'true' : 'false'}
                      style={{ height: barHeight }}
                      className={cn(barClass, isEmpty ? 'min-h-0' : 'min-h-2')}
                    />
                  </TooltipTrigger>
                  <TooltipContent>{label}</TooltipContent>
                </Tooltip>
              );
            });
            return formatBarTooltip ? (
              <TooltipProvider delayDuration={150} skipDelayDuration={300}>
                {bars}
              </TooltipProvider>
            ) : (
              bars
            );
          })()}
        </div>
      )}
      <RadixSlider.Root
        dir="ltr"
        min={min}
        max={max}
        step={step}
        value={value}
        onValueChange={(v) => onChange(v as [number, number])}
        className="relative flex h-5 w-full touch-none items-center select-none"
        aria-label={ariaLabel}
      >
        <RadixSlider.Track className="relative h-1 w-full grow overflow-hidden rounded-full bg-neutral-200">
          <RadixSlider.Range className="bg-brand-primary-600 absolute h-full" />
        </RadixSlider.Track>
        {value.map((_, i) => (
          <RadixSlider.Thumb
            key={i}
            className={cn(
              'block h-4 w-4 rounded-full bg-white',
              'border-brand-primary-600 border-2',
              'shadow-sm transition-colors',
              'focus-visible:ring-brand-primary-500 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
              'disabled:pointer-events-none disabled:opacity-50',
            )}
          />
        ))}
      </RadixSlider.Root>
      <div className="flex items-center justify-between text-xs" dir="ltr">
        <span className="text-text-secondary">{fmtMin(value[0])}</span>
        {showRangeInput && (
          <input
            type="text"
            value={inputVal}
            onChange={(e) => setInputVal(e.target.value)}
            onBlur={(e) => commitInput(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === 'Enter') commitInput(inputVal);
            }}
            aria-label={ariaLabel ? `${ariaLabel} range` : 'price range'}
            className={cn(
              'text-brand-primary-600 w-20 bg-transparent text-center font-medium',
              'border-brand-primary-300 rounded border px-1 py-0.5 tabular-nums',
              'focus-visible:ring-brand-primary-400 focus-visible:ring-1 focus-visible:outline-none',
            )}
          />
        )}
        <span className="text-text-secondary">{fmtMax(value[1])}</span>
      </div>
    </div>
  );
}
