import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { bem } from 'utils/bem';
import { SRangeSlider } from 'common/components/form/range-slider/s-range-slider';
import { Range } from 'react-range';
import { useMeasure } from 'react-use';
import { AnimatePresence, motion } from 'framer-motion';
import { PrimaryLabel } from 'common/components/typography';
import { colorPalette } from 'app/theme/colors';

interface Props {
  min: number;
  max: number;
  points: number[];
  onChange(selected: number[]);
  value: number[];
  disabled?: boolean;
  step?: number;
}

const classes = bem('range-slider');

const COLORS = [colorPalette.periwinkleGray, '#FF3C81'];
const POINT_WIDTH = 15;

const handleAnimationVariants = {
  hover: { scale: 1.2 },
  pressed: { scale: 0.9 },
  initial: { scale: 1 },
};

const tooltipAnimationVariants = {
  shown: { scale: 1, y: 0 },
  hidden: { scale: 0, y: 20 },
  exit: { scale: 0, y: 20 },
};

const getTrackBackground = (values: number[], min: number, max: number): string => {
  if (!values) return '';
  const progress = values.map(value => ((value - min) / (max - min)) * 100);

  return `linear-gradient(90deg, ${COLORS[0]} ${progress[0]}%, ${COLORS[1]} ${progress[0]}%, ${COLORS[1]} ${progress[1]}%, ${COLORS[0]} ${progress[1]}%)`;
};

export const RangeSlider = ({ min, max, points, value = [min, max], onChange, disabled, step = 1 }: Props) => {
  const normalizedValue = useMemo(() => {
    let normalizedValue = [...value];
    if (value[0] < min) normalizedValue = [min, ...normalizedValue.slice(1, value.length)];
    if (value[value.length - 1] > max) normalizedValue = [...normalizedValue.slice(0, value.length), max];
    return normalizedValue.sort((current, next) => current - next);
  }, [value, max, min]);

  const [innerValues, setValues] = useState(normalizedValue);
  const [pointContainer, { width }] = useMeasure();
  const rangeRef = useRef(null);

  useEffect(() => {
    setValues(normalizedValue);
  }, [normalizedValue]);

  const onRangeChange = (newValues: number[]) => {
    if (newValues[0] === newValues[1]) return;
    setValues(newValues);
  };

  const onFinalChange = (newValues: number[]) => {
    if (newValues[0] === newValues[1]) return;
    onChange(newValues);
  };

  const renderTrack = useCallback(
    ({ props, children }) => (
      <div onMouseDown={props.onMouseDown} onTouchStart={props.onTouchStart} className={classes('track-container')}>
        <div
          className={classes('track')}
          ref={props.ref}
          style={{ background: getTrackBackground(innerValues, min, max) }}
        >
          {children}
        </div>
      </div>
    ),
    [innerValues, min, max]
  );

  const renderThumb = useCallback(
    ({ props, isDragged }) => {
      const plusSign = innerValues[1] === max ? '+' : '';
      return (
        <div {...props}>
          <AnimatePresence>
            {isDragged && (
              <motion.div
                initial="hidden"
                animate="shown"
                exit="exit"
                variants={tooltipAnimationVariants}
                className={classes('tooltip')}
              >{`${innerValues[0]}-${innerValues[1]}${plusSign}`}</motion.div>
            )}
          </AnimatePresence>
          <motion.div
            variants={handleAnimationVariants}
            initial="initial"
            whileHover={disabled ? undefined : 'hover'}
            whileTap={disabled ? undefined : 'pressed'}
            className={classes('handle')}
          />
        </div>
      );
    },
    [innerValues, max, disabled]
  );

  const renderPoints = useMemo(
    () =>
      points.map(point => {
        const percent = ((point - min) / (max - min)) * width;
        return (
          <div
            key={point}
            className={classes('point')}
            style={{ left: percent - POINT_WIDTH / 2, position: 'absolute', width: POINT_WIDTH }}
          >
            <div className={classes('indicator')} />
            <PrimaryLabel className={classes('point-value')}>{point}</PrimaryLabel>
          </div>
        );
      }),
    [points, min, max, width]
  );

  return (
    <SRangeSlider disabled={disabled}>
      <Range
        values={innerValues || []}
        ref={rangeRef}
        step={step}
        min={min}
        max={max}
        disabled={disabled}
        onChange={onRangeChange}
        onFinalChange={onFinalChange}
        renderTrack={renderTrack}
        renderThumb={renderThumb}
      />
      <div className={classes('points-container')} ref={pointContainer}>
        {renderPoints}
      </div>
    </SRangeSlider>
  );
};
