import React, { useState, useRef, useCallback, useEffect } from 'react';

export interface SliderProps {
  /** Current value (controlled) */
  value?: number;
  /** Default value */
  defaultValue?: number;
  /** Minimum value */
  min?: number;
  /** Maximum value */
  max?: number;
  /** Step increment */
  step?: number;
  /** Callback when value changes */
  onChange?: (value: number) => void;
  /** Callback when sliding ends */
  onChangeEnd?: (value: number) => void;
  /** Show current value tooltip */
  showTooltip?: boolean | 'always' | 'hover' | 'drag';
  /** Format tooltip value */
  formatTooltip?: (value: number) => string;
  /** Show tick marks */
  showTicks?: boolean;
  /** Custom tick values */
  ticks?: number[];
  /** Show tick labels */
  showTickLabels?: boolean;
  /** Format tick labels */
  formatTickLabel?: (value: number) => string;
  /** Color variant */
  variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
  /** Size variant */
  size?: 'sm' | 'md' | 'lg';
  /** Disabled state */
  disabled?: boolean;
  /** Orientation */
  orientation?: 'horizontal' | 'vertical';
  /** Additional CSS classes */
  className?: string;
}

export const Slider: React.FC<SliderProps> = ({
  value: controlledValue,
  defaultValue = 0,
  min = 0,
  max = 100,
  step = 1,
  onChange,
  onChangeEnd,
  showTooltip = 'drag',
  formatTooltip = (v) => String(v),
  showTicks = false,
  ticks,
  showTickLabels = false,
  formatTickLabel = (v) => String(v),
  variant = 'primary',
  size = 'md',
  disabled = false,
  orientation = 'horizontal',
  className = '',
}) => {
  const [internalValue, setInternalValue] = useState(defaultValue);
  const [isDragging, setIsDragging] = useState(false);
  const [isHovering, setIsHovering] = useState(false);
  const trackRef = useRef<HTMLDivElement>(null);

  const value = controlledValue ?? internalValue;

  // Calculate percentage
  const percentage = ((value - min) / (max - min)) * 100;

  // Get value from position
  const getValueFromPosition = useCallback((clientX: number, clientY: number): number => {
    if (!trackRef.current) return value;

    const rect = trackRef.current.getBoundingClientRect();
    let ratio: number;

    if (orientation === 'horizontal') {
      ratio = (clientX - rect.left) / rect.width;
    } else {
      ratio = 1 - (clientY - rect.top) / rect.height;
    }

    ratio = Math.max(0, Math.min(1, ratio));
    let newValue = min + ratio * (max - min);

    // Snap to step
    newValue = Math.round(newValue / step) * step;
    newValue = Math.max(min, Math.min(max, newValue));

    return newValue;
  }, [min, max, step, value, orientation]);

  // Handle mouse/touch move
  const handleMove = useCallback((clientX: number, clientY: number) => {
    if (disabled) return;

    const newValue = getValueFromPosition(clientX, clientY);

    if (controlledValue === undefined) {
      setInternalValue(newValue);
    }
    onChange?.(newValue);
  }, [disabled, getValueFromPosition, controlledValue, onChange]);

  // Handle mouse down
  const handleMouseDown = (e: React.MouseEvent) => {
    if (disabled) return;
    e.preventDefault();

    setIsDragging(true);
    handleMove(e.clientX, e.clientY);
  };

  // Handle touch start
  const handleTouchStart = (e: React.TouchEvent) => {
    if (disabled) return;

    setIsDragging(true);
    const touch = e.touches[0];
    handleMove(touch.clientX, touch.clientY);
  };

  // Handle mouse/touch events
  useEffect(() => {
    if (!isDragging) return;

    const handleMouseMove = (e: MouseEvent) => {
      handleMove(e.clientX, e.clientY);
    };

    const handleTouchMove = (e: TouchEvent) => {
      const touch = e.touches[0];
      handleMove(touch.clientX, touch.clientY);
    };

    const handleEnd = () => {
      setIsDragging(false);
      onChangeEnd?.(value);
    };

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleEnd);
    document.addEventListener('touchmove', handleTouchMove);
    document.addEventListener('touchend', handleEnd);

    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleEnd);
      document.removeEventListener('touchmove', handleTouchMove);
      document.removeEventListener('touchend', handleEnd);
    };
  }, [isDragging, handleMove, onChangeEnd, value]);

  // Handle keyboard
  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (disabled) return;

    let newValue = value;
    const largeStep = step * 10;

    switch (e.key) {
      case 'ArrowRight':
      case 'ArrowUp':
        newValue = Math.min(max, value + step);
        break;
      case 'ArrowLeft':
      case 'ArrowDown':
        newValue = Math.max(min, value - step);
        break;
      case 'PageUp':
        newValue = Math.min(max, value + largeStep);
        break;
      case 'PageDown':
        newValue = Math.max(min, value - largeStep);
        break;
      case 'Home':
        newValue = min;
        break;
      case 'End':
        newValue = max;
        break;
      default:
        return;
    }

    e.preventDefault();

    if (controlledValue === undefined) {
      setInternalValue(newValue);
    }
    onChange?.(newValue);
    onChangeEnd?.(newValue);
  };

  // Generate tick marks
  const tickMarks = ticks || (showTicks ?
    Array.from({ length: Math.floor((max - min) / step) + 1 }, (_, i) => min + i * step) :
    []
  );

  // Determine tooltip visibility
  const shouldShowTooltip =
    showTooltip === 'always' ||
    (showTooltip === 'hover' && (isHovering || isDragging)) ||
    (showTooltip === 'drag' && isDragging) ||
    (showTooltip === true && (isHovering || isDragging));

  const classes = [
    'll-slider',
    `ll-slider-${orientation}`,
    `ll-slider-${variant}`,
    `ll-slider-${size}`,
    disabled && 'll-slider-disabled',
    className,
  ].filter(Boolean).join(' ');

  const trackStyle = orientation === 'horizontal'
    ? { width: `${percentage}%` }
    : { height: `${percentage}%` };

  const thumbStyle = orientation === 'horizontal'
    ? { left: `${percentage}%` }
    : { bottom: `${percentage}%` };

  return (
    <div className={classes}>
      <div
        ref={trackRef}
        className="ll-slider-track-container"
        onMouseDown={handleMouseDown}
        onTouchStart={handleTouchStart}
        onMouseEnter={() => setIsHovering(true)}
        onMouseLeave={() => setIsHovering(false)}
      >
        <div className="ll-slider-track">
          <div className="ll-slider-track-fill" style={trackStyle} />
        </div>

        {/* Tick marks */}
        {tickMarks.length > 0 && (
          <div className="ll-slider-ticks">
            {tickMarks.map((tick) => {
              const tickPercent = ((tick - min) / (max - min)) * 100;
              const tickStyle = orientation === 'horizontal'
                ? { left: `${tickPercent}%` }
                : { bottom: `${tickPercent}%` };

              return (
                <div key={tick} className="ll-slider-tick" style={tickStyle}>
                  <div className="ll-slider-tick-mark" />
                  {showTickLabels && (
                    <div className="ll-slider-tick-label">
                      {formatTickLabel(tick)}
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        )}

        {/* Thumb */}
        <div
          className="ll-slider-thumb"
          style={thumbStyle}
          tabIndex={disabled ? -1 : 0}
          role="slider"
          aria-valuemin={min}
          aria-valuemax={max}
          aria-valuenow={value}
          aria-orientation={orientation}
          aria-disabled={disabled}
          onKeyDown={handleKeyDown}
        >
          {shouldShowTooltip && (
            <div className="ll-slider-tooltip">
              {formatTooltip(value)}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// Range Slider
export interface RangeSliderProps extends Omit<SliderProps, 'value' | 'defaultValue' | 'onChange' | 'onChangeEnd'> {
  /** Current values [min, max] (controlled) */
  value?: [number, number];
  /** Default values [min, max] */
  defaultValue?: [number, number];
  /** Callback when values change */
  onChange?: (value: [number, number]) => void;
  /** Callback when sliding ends */
  onChangeEnd?: (value: [number, number]) => void;
  /** Minimum gap between handles */
  minGap?: number;
}

export const RangeSlider: React.FC<RangeSliderProps> = ({
  value: controlledValue,
  defaultValue = [25, 75],
  min = 0,
  max = 100,
  step = 1,
  onChange,
  onChangeEnd,
  minGap = 0,
  showTooltip = 'drag',
  formatTooltip = (v) => String(v),
  showTicks = false,
  ticks,
  showTickLabels = false,
  formatTickLabel = (v) => String(v),
  variant = 'primary',
  size = 'md',
  disabled = false,
  orientation = 'horizontal',
  className = '',
}) => {
  const [internalValue, setInternalValue] = useState(defaultValue);
  const [activeHandle, setActiveHandle] = useState<0 | 1 | null>(null);
  const [isHovering, setIsHovering] = useState<0 | 1 | null>(null);
  const trackRef = useRef<HTMLDivElement>(null);

  const value = controlledValue ?? internalValue;

  // Calculate percentages
  const lowPercent = ((value[0] - min) / (max - min)) * 100;
  const highPercent = ((value[1] - min) / (max - min)) * 100;

  // Get value from position
  const getValueFromPosition = useCallback((clientX: number, clientY: number): number => {
    if (!trackRef.current) return 0;

    const rect = trackRef.current.getBoundingClientRect();
    let ratio: number;

    if (orientation === 'horizontal') {
      ratio = (clientX - rect.left) / rect.width;
    } else {
      ratio = 1 - (clientY - rect.top) / rect.height;
    }

    ratio = Math.max(0, Math.min(1, ratio));
    let newValue = min + ratio * (max - min);
    newValue = Math.round(newValue / step) * step;

    return newValue;
  }, [min, max, step, orientation]);

  // Handle move
  const handleMove = useCallback((clientX: number, clientY: number) => {
    if (disabled || activeHandle === null) return;

    const newValue = getValueFromPosition(clientX, clientY);
    let newValues: [number, number] = [...value];

    if (activeHandle === 0) {
      newValues[0] = Math.min(newValue, value[1] - minGap);
      newValues[0] = Math.max(min, newValues[0]);
    } else {
      newValues[1] = Math.max(newValue, value[0] + minGap);
      newValues[1] = Math.min(max, newValues[1]);
    }

    if (controlledValue === undefined) {
      setInternalValue(newValues);
    }
    onChange?.(newValues);
  }, [disabled, activeHandle, getValueFromPosition, value, minGap, min, max, controlledValue, onChange]);

  // Handle track click
  const handleTrackClick = (e: React.MouseEvent) => {
    if (disabled) return;

    const clickValue = getValueFromPosition(e.clientX, e.clientY);
    const distToLow = Math.abs(clickValue - value[0]);
    const distToHigh = Math.abs(clickValue - value[1]);

    const handle = distToLow <= distToHigh ? 0 : 1;
    setActiveHandle(handle);

    let newValues: [number, number] = [...value];
    if (handle === 0) {
      newValues[0] = Math.min(clickValue, value[1] - minGap);
      newValues[0] = Math.max(min, newValues[0]);
    } else {
      newValues[1] = Math.max(clickValue, value[0] + minGap);
      newValues[1] = Math.min(max, newValues[1]);
    }

    if (controlledValue === undefined) {
      setInternalValue(newValues);
    }
    onChange?.(newValues);
  };

  // Handle mouse/touch events
  useEffect(() => {
    if (activeHandle === null) return;

    const handleMouseMove = (e: MouseEvent) => handleMove(e.clientX, e.clientY);
    const handleTouchMove = (e: TouchEvent) => handleMove(e.touches[0].clientX, e.touches[0].clientY);
    const handleEnd = () => {
      setActiveHandle(null);
      onChangeEnd?.(value);
    };

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleEnd);
    document.addEventListener('touchmove', handleTouchMove);
    document.addEventListener('touchend', handleEnd);

    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleEnd);
      document.removeEventListener('touchmove', handleTouchMove);
      document.removeEventListener('touchend', handleEnd);
    };
  }, [activeHandle, handleMove, onChangeEnd, value]);

  // Tick marks
  const tickMarks = ticks || (showTicks ?
    Array.from({ length: Math.floor((max - min) / step) + 1 }, (_, i) => min + i * step) :
    []
  );

  const classes = [
    'll-slider',
    'll-range-slider',
    `ll-slider-${orientation}`,
    `ll-slider-${variant}`,
    `ll-slider-${size}`,
    disabled && 'll-slider-disabled',
    className,
  ].filter(Boolean).join(' ');

  const fillStyle = orientation === 'horizontal'
    ? { left: `${lowPercent}%`, width: `${highPercent - lowPercent}%` }
    : { bottom: `${lowPercent}%`, height: `${highPercent - lowPercent}%` };

  const thumbStyles = [
    orientation === 'horizontal' ? { left: `${lowPercent}%` } : { bottom: `${lowPercent}%` },
    orientation === 'horizontal' ? { left: `${highPercent}%` } : { bottom: `${highPercent}%` },
  ];

  const shouldShowTooltip = (handle: 0 | 1) =>
    showTooltip === 'always' ||
    (showTooltip === 'hover' && (isHovering === handle || activeHandle === handle)) ||
    (showTooltip === 'drag' && activeHandle === handle) ||
    (showTooltip === true && (isHovering === handle || activeHandle === handle));

  return (
    <div className={classes}>
      <div
        ref={trackRef}
        className="ll-slider-track-container"
        onClick={handleTrackClick}
      >
        <div className="ll-slider-track">
          <div className="ll-slider-track-fill" style={fillStyle} />
        </div>

        {/* Tick marks */}
        {tickMarks.length > 0 && (
          <div className="ll-slider-ticks">
            {tickMarks.map((tick) => {
              const tickPercent = ((tick - min) / (max - min)) * 100;
              const tickStyle = orientation === 'horizontal'
                ? { left: `${tickPercent}%` }
                : { bottom: `${tickPercent}%` };

              return (
                <div key={tick} className="ll-slider-tick" style={tickStyle}>
                  <div className="ll-slider-tick-mark" />
                  {showTickLabels && (
                    <div className="ll-slider-tick-label">
                      {formatTickLabel(tick)}
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        )}

        {/* Thumbs */}
        {[0, 1].map((handle) => (
          <div
            key={handle}
            className={`ll-slider-thumb ${activeHandle === handle ? 'active' : ''}`}
            style={thumbStyles[handle]}
            tabIndex={disabled ? -1 : 0}
            role="slider"
            aria-valuemin={handle === 0 ? min : value[0]}
            aria-valuemax={handle === 0 ? value[1] : max}
            aria-valuenow={value[handle as 0 | 1]}
            aria-orientation={orientation}
            aria-disabled={disabled}
            onMouseDown={(e) => { e.stopPropagation(); setActiveHandle(handle as 0 | 1); }}
            onTouchStart={() => setActiveHandle(handle as 0 | 1)}
            onMouseEnter={() => setIsHovering(handle as 0 | 1)}
            onMouseLeave={() => setIsHovering(null)}
          >
            {shouldShowTooltip(handle as 0 | 1) && (
              <div className="ll-slider-tooltip">
                {formatTooltip(value[handle as 0 | 1])}
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
};
