import React from 'react';
import { NonEmptyArray } from '../../fp/NonEmptyArray';

const KEYBOARD_KEY = {
  ArrowRight: 'ArrowRight',
  ArrowLeft: 'ArrowLeft',
  ArrowUp: 'ArrowUp',
  ArrowDown: 'ArrowDown',
  PageUp: 'PageUp',
  PageDown: 'PageDown',
  Escape: 'Escape',
};

const preventKeyboardScrolling = (e: React.KeyboardEvent): void => {
  if (
    [
      KEYBOARD_KEY.ArrowLeft,
      KEYBOARD_KEY.ArrowRight,
      KEYBOARD_KEY.ArrowUp,
      KEYBOARD_KEY.ArrowDown,
      KEYBOARD_KEY.PageUp,
      KEYBOARD_KEY.PageDown,
    ].includes(e.key)
  ) {
    e.stopPropagation();
    e.preventDefault();
  }
};

interface SlidingEventData {
  changedTouches?: NonEmptyArray<{ pageX: number; pageY: number }>;
  clientX: number;
  clientY: number;
  touches?: NonEmptyArray<{ pageX: number; pageY: number }>;
  type: string;
}

const getPointerPosition = (
  event: Event | SlidingEventData
): { x: number; y: number } => {
  const e = event as SlidingEventData;
  const isTouchEvent = e.type === 'touchmove';
  const isMouseEvent = e.type === 'mousemove' || e.type === 'mousedown';

  let x = 0;
  let y = 0;
  if (
    isTouchEvent === true &&
    e.touches !== undefined &&
    e.changedTouches !== undefined
  ) {
    const isTouches = e.touches[0] !== null && e.touches[0] !== undefined;
    const touch = isTouches ? e.touches[0] : e.changedTouches[0];
    x = touch.pageX;
    y = touch.pageY;
  } else if (isMouseEvent === true) {
    x = e.clientX;
    y = e.clientY;
  }
  return { x, y };
};

const normalizeValue = (min: number, max: number, value: number): number =>
  Math.min(Math.max(value, min), max);

const getValueByMouseEvent = (
  e: Event | SlidingEventData,
  railWidth: number,
  railOffsetLeft: number,
  min: number,
  max: number,
  step: number
): number => {
  const draggedWidth = getPointerPosition(e).x - railOffsetLeft;
  const draggedPercent = (draggedWidth / railWidth) * 100;
  const valueFromDraggedPercent = Math.round(
    min + Math.round((((max - min) / 100) * draggedPercent) / step) * step
  );
  const result = normalizeValue(min, max, valueFromDraggedPercent);
  return result;
};

const getValueByKeyboard = (
  value: number,
  step: number,
  min: number,
  max: number,
  keyboardKey: string
): number => {
  if (
    [
      KEYBOARD_KEY.ArrowLeft,
      KEYBOARD_KEY.ArrowDown,
      KEYBOARD_KEY.PageDown,
    ].includes(keyboardKey)
  ) {
    return Math.max(value - step, min);
  }
  if (
    [
      KEYBOARD_KEY.ArrowRight,
      KEYBOARD_KEY.ArrowUp,
      KEYBOARD_KEY.PageUp,
    ].includes(keyboardKey)
  ) {
    return Math.min(value + step, max);
  }
  return value;
};

const removeListener = (onMouseMove: EventListener) => {
  return (): void => {
    document.removeEventListener('mousemove', onMouseMove);
    document.removeEventListener('touchmove', onMouseMove);
  };
};

const addListener = (
  onMouseMove: EventListener,
  onMouseUp: EventListener
): void => {
  document.addEventListener('mousemove', onMouseMove);
  document.addEventListener('touchmove', onMouseMove);

  document.addEventListener('mouseup', onMouseUp);
  document.addEventListener('touchend', onMouseUp);
};

export interface SliderData {
  valueInNumber: number;
  valueInPercent: number;
}

interface SliderInput {
  max: number;
  min: number;
  value: number;
}

export const useSliderData = ({ min, max, value }: SliderInput): SliderData => {
  const valueInNumber =
    value === undefined ? (min + max) / 2 : normalizeValue(min, max, value);
  const valueInPercent = normalizeValue(
    0,
    100,
    ((valueInNumber - min) / (max - min)) * 100
  );
  return {
    valueInNumber,
    valueInPercent,
  };
};

export {
  preventKeyboardScrolling,
  getValueByMouseEvent,
  getValueByKeyboard,
  addListener,
  removeListener,
};
