import { sendMouse } from '@web/test-runner-commands';

function determineMousePosition(el: Element, position: string, offsetX: number, offsetY: number) {
  const { x, y, width, height } = el.getBoundingClientRect();
  const centerX = Math.floor(x + window.scrollX + width / 2);
  const centerY = Math.floor(y + window.scrollY + height / 2);
  let clickX: number;
  let clickY: number;

  switch (position) {
    case 'top':
      clickX = centerX;
      clickY = y;
      break;
    case 'right':
      clickX = x + width - 1;
      clickY = centerY;
      break;
    case 'bottom':
      clickX = centerX;
      clickY = y + height - 1;
      break;
    case 'left':
      clickX = x;
      clickY = centerY;
      break;
    default:
      clickX = centerX;
      clickY = centerY;
  }

  clickX += offsetX;
  clickY += offsetY;
  return { clickX, clickY };
}

/** A testing utility that measures an element's position and clicks on it. */
export async function clickOnElement(
  /** The element to click */
  el: Element,
  /** The location of the element to click */
  position: 'top' | 'right' | 'bottom' | 'left' | 'center' = 'center',
  /** The horizontal offset to apply to the position when clicking */
  offsetX = 0,
  /** The vertical offset to apply to the position when clicking */
  offsetY = 0
) {
  const { clickX, clickY } = determineMousePosition(el, position, offsetX, offsetY);

  await sendMouse({ type: 'click', position: [clickX, clickY] });
}

/** A testing utility that moves the mouse onto an element. */
export async function moveMouseOnElement(
  /** The element to click */
  el: Element,
  /** The location of the element to click */
  position: 'top' | 'right' | 'bottom' | 'left' | 'center' = 'center',
  /** The horizontal offset to apply to the position when clicking */
  offsetX = 0,
  /** The vertical offset to apply to the position when clicking */
  offsetY = 0
) {
  const { clickX, clickY } = determineMousePosition(el, position, offsetX, offsetY);

  await sendMouse({ type: 'move', position: [clickX, clickY] });
}

/** A testing utility that drags an element with the mouse. */
export async function dragElement(
  /** The element to drag */
  el: Element,
  /** The horizontal distance to drag in pixels */
  deltaX = 0,
  /** The vertical distance to drag in pixels */
  deltaY = 0,
  callbacks: {
    afterMouseDown?: () => void | Promise<void>;
    afterMouseMove?: () => void | Promise<void>;
  } = {}
): Promise<void> {
  await moveMouseOnElement(el);
  await sendMouse({ type: 'down' });

  await callbacks.afterMouseDown?.();

  const { clickX, clickY } = determineMousePosition(el, 'center', deltaX, deltaY);
  await sendMouse({ type: 'move', position: [clickX, clickY] });

  await callbacks.afterMouseMove?.();

  await sendMouse({ type: 'up' });
}
