import {fireEvent, render, screen} from 'sentry-test/reactTestingLibrary';

import {useTimelineZoom} from './timelineZoom';

interface TestProps {
  onSelect?: (startX: number, endX: number) => void;
}

function TestComponent({onSelect}: TestProps) {
  const {isActive, timelineSelector, selectionContainerRef} =
    useTimelineZoom<HTMLDivElement>({enabled: true, onSelect});

  return (
    <div data-test-id="body">
      {isActive && <div>Selection Active</div>}
      <div data-test-id="container" ref={selectionContainerRef}>
        {timelineSelector}
      </div>
    </div>
  );
}

beforeEach(() => {
  jest
    .spyOn(window, 'requestAnimationFrame')
    .mockImplementation((callback: FrameRequestCallback): number => {
      callback(0);
      return 0;
    });
});

afterEach(() => {
  jest.mocked(window.requestAnimationFrame).mockRestore();
});

function setupTestComponent() {
  const handleSelect = jest.fn();

  render(<TestComponent onSelect={handleSelect} />);

  const body = screen.getByTestId('body');
  const container = screen.getByTestId('container');

  container.getBoundingClientRect = jest.fn(() => ({
    x: 10,
    y: 10,
    width: 100,
    height: 100,
    left: 10,
    top: 10,
    right: 110,
    bottom: 110,
    toJSON: jest.fn(),
  }));

  return {handleSelect, body, container};
}

describe('TimelineZoom', function () {
  it('triggers onSelect', function () {
    const {handleSelect, body, container} = setupTestComponent();

    // Selector has not appeared
    expect(screen.queryByRole('presentation')).not.toBeInTheDocument();

    // Selection does not start when clicking outside the container
    fireEvent.mouseDown(body, {button: 0, clientX: 0, clientY: 0});
    expect(screen.queryByRole('presentation')).not.toBeInTheDocument();

    // Move cursor into the container, selection still not present
    fireEvent.mouseMove(body, {clientX: 20, clientY: 20});
    expect(screen.queryByRole('presentation')).not.toBeInTheDocument();

    // Left click starts selection
    fireEvent.mouseDown(body, {button: 0, clientX: 20, clientY: 20});

    const selection = screen.getByRole('presentation');
    expect(selection).toBeInTheDocument();
    expect(screen.getByText('Selection Active')).toBeInTheDocument();

    expect(container.style.getPropertyValue('--selectionStart')).toBe('10px');
    expect(container.style.getPropertyValue('--selectionWidth')).toBe('0px');

    // Body has disabled text selection
    expect(document.body).toHaveStyle({userSelect: 'none'});

    // Move right 15px
    fireEvent.mouseMove(body, {clientX: 35, clientY: 20});
    expect(container.style.getPropertyValue('--selectionWidth')).toBe('15px');

    // Move left 25px, at the edge of the container
    fireEvent.mouseMove(body, {clientX: 10, clientY: 20});
    expect(container.style.getPropertyValue('--selectionStart')).toBe('0px');
    expect(container.style.getPropertyValue('--selectionWidth')).toBe('10px');

    // Move left 5px more, selection does not move out of the container
    fireEvent.mouseMove(body, {clientX: 5, clientY: 20});
    expect(container.style.getPropertyValue('--selectionStart')).toBe('0px');
    expect(container.style.getPropertyValue('--selectionWidth')).toBe('10px');

    // Release to make selection
    fireEvent.mouseUp(body, {clientX: 5, clientY: 20});
    expect(handleSelect).toHaveBeenCalledWith(0, 10);
  });

  it('does not start selection with right click', function () {
    const {body} = setupTestComponent();

    // Move cursor into the container, selection still not present
    fireEvent.mouseMove(body, {clientX: 20, clientY: 20});

    // Right click does nothing
    fireEvent.mouseDown(body, {button: 1, clientX: 20, clientY: 20});
    expect(screen.queryByRole('presentation')).not.toBeInTheDocument();
  });

  it('does not select for very small regions', function () {
    const {handleSelect, body, container} = setupTestComponent();

    // Left click starts selection
    fireEvent.mouseMove(body, {clientX: 20, clientY: 20});
    fireEvent.mouseDown(body, {button: 0, clientX: 20, clientY: 20});
    fireEvent.mouseMove(body, {clientX: 22, clientY: 20});

    const selection = screen.getByRole('presentation');
    expect(selection).toBeInTheDocument();
    expect(screen.getByText('Selection Active')).toBeInTheDocument();

    expect(container.style.getPropertyValue('--selectionStart')).toBe('10px');
    expect(container.style.getPropertyValue('--selectionWidth')).toBe('2px');

    // Relase does not make selection for such a small range
    fireEvent.mouseUp(body, {clientX: 22, clientY: 20});
    expect(handleSelect).not.toHaveBeenCalledWith(0, 10);
  });
});
