import React, { ReactElement, useEffect, useRef, useState } from 'react';
import css from '../../../utils/css';

import { CommonProps } from '../../common';
import {
  SliderTooltip,
  SliderRail,
  SliderTrack,
  SliderContainer,
  SliderThumb,
} from '../StyledSlider';
import {
  preventKeyboardScrolling,
  getValueByMouseEvent,
  getValueByKeyboard,
  useSliderData,
  removeListener,
  addListener,
} from '../utils';
import { useResizeObserver } from '../../../utils/hooks';

export interface SingleSliderProps extends CommonProps {
  /**
   * Whether the slider's tooltip is always shown.
   */
  alwaysShowTooltip?: boolean;
  /**
   * Custom text for tooltip. If defined, it will replace the auto generated tooltip.
   */
  customTooltipText?: string;
  /**
   * Whether the slider is disabled.
   */
  disabled?: boolean;
  /**
   * Slider max value.
   */
  max?: number;
  /**
   * Slider min value.
   */
  min?: number;
  /**
   * onChange event handler.
   */
  onChange?: (value: number) => void;
  /**
   * Slider step.
   */
  step?: number;
  /**
   * Current slider's value.
   */
  value: number;
}

const SingleSlider = ({
  min = 0,
  max = 100,
  step = 1,
  value,
  disabled,
  alwaysShowTooltip,
  customTooltipText,
  id,
  className,
  style,
  sx = {},
  'data-test-id': dataTestId,
  onChange,
}: SingleSliderProps): ReactElement => {
  const refSlider = useRef<HTMLDivElement>(null);

  const { valueInNumber, valueInPercent } = useSliderData({ min, max, value });

  const toolTip =
    customTooltipText !== undefined ? customTooltipText : valueInNumber;

  const [labelId] = useState<string>(`tooltip_${Date.now().toString()}`);
  const [railWidth, setRailWidth] = useState<number>(0);
  const [railOffsetLeft, setRailOffsetLeft] = useState<number>(0);

  const onKeyDown = (e: React.KeyboardEvent): void => {
    preventKeyboardScrolling(e);
    if (disabled !== true && onChange !== undefined) {
      const newValueInNumber = getValueByKeyboard(
        valueInNumber,
        step,
        min,
        max,
        e.key
      );
      if (onChange !== undefined) {
        onChange(newValueInNumber);
      }
    }
  };

  const onMouseMove = (e: Event): void => {
    e.stopPropagation();
    const newValueInNumber = getValueByMouseEvent(
      e,
      railWidth,
      railOffsetLeft,
      min,
      max,
      step
    );

    if (onChange !== undefined) {
      onChange(newValueInNumber);
    }
  };

  const onClickSlider = (e: React.MouseEvent | Event): void =>
    onMouseMove(e as Event);

  const onTouchStart = (e: React.TouchEvent): void => {
    e.stopPropagation();
    const onMouseUp = removeListener(onMouseMove);
    addListener(onMouseMove, onMouseUp);
  };

  const onMouseDown = (e: React.MouseEvent): void => {
    e.stopPropagation();
    const onMouseUp = removeListener(onMouseMove);
    addListener(onMouseMove, onMouseUp);
  };

  const updateSliderDimension = (): void => {
    const { current } = refSlider;
    if (current !== null) {
      const { width } = current.getBoundingClientRect();
      const offsetLeft = current.getBoundingClientRect().x;
      setRailWidth(width);
      setRailOffsetLeft(offsetLeft);
    }
  };

  useResizeObserver(updateSliderDimension, refSlider.current);

  useEffect(updateSliderDimension, [refSlider]);

  return (
    <SliderContainer
      ref={refSlider}
      id={id}
      style={{ ...style, ...css(sx) }}
      className={className}
      data-test-id={dataTestId}
      onKeyDown={onKeyDown}
      onMouseDown={onClickSlider}
      disabled={disabled === true}
    >
      <SliderRail />
      <SliderTrack style={{ width: `${valueInPercent}%` }} />
      <SliderThumb
        aria-valuemax={max}
        aria-valuemin={min}
        aria-valuenow={valueInNumber}
        aria-disabled={disabled === true}
        aria-orientation="horizontal"
        tabIndex={0}
        aria-labelledby={labelId}
        style={{ left: `${valueInPercent}%` }}
        onMouseDown={onMouseDown}
        onTouchStart={onTouchStart}
        data-test-id="slider-thumb"
      >
        <SliderTooltip
          id={labelId}
          role="tooltip"
          aria-label={`${toolTip}`}
          alwaysShowTooltip={alwaysShowTooltip === true}
        >
          {toolTip}
        </SliderTooltip>
      </SliderThumb>
    </SliderContainer>
  );
};

export default SingleSlider;
