import { useCallback, useEffect, useRef, useState } from 'react';
import type { FiltersMap } from '@wix/bex-core';
import type { CellInteractionState } from '../../state/EditableTable/CellInteractionState';
import type { Cell } from '../../state/EditableTable';

const MOUSE_LEAVE_PADDING = 30;

const isInsidePaddedRect = (e: MouseEvent, rect: DOMRect, padding: number) =>
  e.clientX >= rect.left - padding &&
  e.clientX <= rect.right + padding &&
  e.clientY >= rect.top - padding &&
  e.clientY <= rect.bottom + padding;

export function useEditableTableContextMenu<T, F extends FiltersMap>(
  cellInteraction: CellInteractionState<T, F>,
) {
  const [position, setPosition] = useState<{ x: number; y: number } | null>(
    null,
  );
  const [cell, setCell] = useState<Cell | null>(null);
  const menuRef = useRef<HTMLDivElement>(null);
  const cellElRef = useRef<Element | null>(null);

  const handleContextMenu = useCallback(
    (e: React.MouseEvent) => {
      const cellEl = (e.target as HTMLElement).closest(
        '[data-row-key][data-column-id]',
      );
      if (!cellEl) {
        return;
      }

      e.preventDefault();
      const rowKey = cellEl.getAttribute('data-row-key')!;
      const columnId = cellEl.getAttribute('data-column-id')!;
      if (!cellInteraction.isCellSelected(rowKey, columnId)) {
        cellInteraction.focusCell(rowKey, columnId);
      }
      cellElRef.current = cellEl;
      setPosition({ x: e.clientX, y: e.clientY });
      setCell({ rowKey, columnId });
    },
    [cellInteraction],
  );

  const handleClose = useCallback(() => {
    setPosition(null);
    setCell(null);
    cellElRef.current = null;
  }, []);

  useEffect(() => {
    if (!position) {
      return;
    }

    const isInside = (target: EventTarget | null) =>
      menuRef.current?.contains(target as Node);

    const onMouseDown = (e: MouseEvent) => {
      if (!isInside(e.target)) {
        handleClose();
      }
    };

    const onScroll = (e: Event) => {
      if (!isInside(e.target)) {
        handleClose();
      }
    };

    const onMouseMove = (e: MouseEvent) => {
      if (!menuRef.current) {
        return;
      }
      const menuRect = menuRef.current.getBoundingClientRect();
      // Skip until the menu has been laid out
      if (menuRect.width === 0 || menuRect.height === 0) {
        return;
      }
      // Stay open if cursor is near the menu
      if (isInsidePaddedRect(e, menuRect, MOUSE_LEAVE_PADDING)) {
        return;
      }
      // Stay open if cursor is on the cell that triggered the menu
      if (cellElRef.current) {
        const cellRect = cellElRef.current.getBoundingClientRect();
        if (isInsidePaddedRect(e, cellRect, 0)) {
          return;
        }
      }
      handleClose();
    };

    document.addEventListener('mousedown', onMouseDown);
    document.addEventListener('scroll', onScroll, true);
    document.addEventListener('mousemove', onMouseMove);

    return () => {
      document.removeEventListener('mousedown', onMouseDown);
      document.removeEventListener('scroll', onScroll, true);
      document.removeEventListener('mousemove', onMouseMove);
    };
  }, [position, handleClose]);

  return { position, cell, menuRef, handleContextMenu, handleClose };
}
