import React from 'react';
import { fireEvent, createEvent } from '@testing-library/react';
import renderWithTheme from '../../../testUtils/renderWithTheme';

import Slider from '..';

const mockGetBoundingClientRect = () => {
  const mockedRef = {
    mockElement: { getBoundingClientRect: jest.fn() },

    get current() {
      return this.mockElement;
    },

    set current(newCurrent) {
      if (newCurrent !== null) {
        jest.spyOn(newCurrent, 'getBoundingClientRect').mockReturnValue({
          x: 0,
          width: 100,
        });
      }
      this.mockElement = newCurrent;
    },
  };

  jest.spyOn(React, 'useRef').mockReturnValue(mockedRef);
};

describe('Slider', () => {
  it('can slide on mouse move', () => {
    mockGetBoundingClientRect();
    const mockOnChange = jest.fn();
    const { getByTestId } = renderWithTheme(
      <Slider min={50} max={100} value={50} onChange={mockOnChange} />
    );

    const thumb = getByTestId('slider-thumb');
    expect(thumb).toBeInTheDocument();

    const mouseDown = createEvent.mouseDown(thumb);
    thumb.dispatchEvent(mouseDown);

    const mouseMove = createEvent.mouseMove(document, {
      clientX: 40,
      clientY: 0,
    });
    const mouseUp = createEvent.mouseUp(document);
    fireEvent(document, mouseMove);
    fireEvent(document, mouseUp);

    expect(mockOnChange).toHaveBeenCalledWith(70);
  });

  it('can slide on touch move', async () => {
    mockGetBoundingClientRect();
    const mockOnChange = jest.fn();
    const { getByTestId } = renderWithTheme(
      <Slider value={35} onChange={mockOnChange} />
    );

    const thumb = getByTestId('slider-thumb');
    expect(thumb).toBeInTheDocument();

    fireEvent.touchStart(thumb);
    fireEvent.touchMove(thumb, {
      changedTouches: [{ pageX: 30, pageY: 0 }],
    });
    expect(mockOnChange).toHaveBeenNthCalledWith(1, 30);

    fireEvent.touchMove(thumb, {
      touches: [{ pageX: 75, pageY: 0 }],
    });
    expect(mockOnChange).toHaveBeenNthCalledWith(2, 75);
  });

  it('shows tooltip', () => {
    const { getByText } = renderWithTheme(
      <Slider value={10} customTooltipText="my tooltip" alwaysShowTooltip />
    );

    expect(getByText('my tooltip')).toBeInTheDocument();
  });

  it.each`
    key             | keyCode | expectedValue | shouldPreventScrolling
    ${'PageUp'}     | ${33}   | ${52}         | ${true}
    ${'PageDown'}   | ${34}   | ${48}         | ${true}
    ${'ArrowLeft'}  | ${37}   | ${48}         | ${true}
    ${'ArrowUp'}    | ${38}   | ${52}         | ${true}
    ${'ArrowRight'} | ${39}   | ${52}         | ${true}
    ${'ArrowDown'}  | ${40}   | ${48}         | ${true}
    ${'KeyA'}       | ${65}   | ${50}         | ${false}
  `(
    'prevents scrolling and slides when pressing $key key',
    ({
      key,
      keyCode,
      expectedValue,
      shouldPreventScrolling,
    }: {
      expectedValue: number;
      key: string;
      keyCode: number;
      shouldPreventScrolling: boolean;
    }) => {
      const mockOnChange = jest.fn();
      const { getByTestId } = renderWithTheme(
        <Slider value={50} step={2} onChange={mockOnChange} />
      );

      const thumb = getByTestId('slider-thumb');
      expect(thumb).toBeInTheDocument();

      const keyDownEvent = createEvent.keyDown(thumb, {
        key,
        keyCode,
      });
      fireEvent(thumb, keyDownEvent);

      expect(keyDownEvent.defaultPrevented).toBe(shouldPreventScrolling);
      expect(mockOnChange).toHaveBeenCalledWith(expectedValue);
    }
  );
});
