import { useEffect, type RefObject } from 'react';
import type { FiltersMap } from '@wix/bex-core';
import type { EditableTableState } from '../../state/EditableTable/EditableTableState';

const EDGE_ZONE = 60;
const MAX_SCROLL_SPEED = 32;
const DRAG_THRESHOLD = 5;

function computeEdgeDelta(
  clientPos: number,
  rectStart: number,
  rectEnd: number,
): number {
  if (clientPos > rectEnd - EDGE_ZONE) {
    const ratio = Math.min(1, (clientPos - (rectEnd - EDGE_ZONE)) / EDGE_ZONE);
    return ratio * MAX_SCROLL_SPEED;
  }
  if (clientPos < rectStart + EDGE_ZONE) {
    const ratio = Math.min(1, (rectStart + EDGE_ZONE - clientPos) / EDGE_ZONE);
    return -ratio * MAX_SCROLL_SPEED;
  }
  return 0;
}

export function useMouseSweepSelection<T, F extends FiltersMap>(
  state: EditableTableState<T, F>,
  containerRef: RefObject<HTMLElement>,
) {
  useEffect(() => {
    const container = containerRef.current;
    if (!container) {
      return;
    }

    const { cellInteraction } = state;
    const getVerticalScroller = () =>
      state.collection.scrollableContent as HTMLElement | null;

    let rafId = 0;
    let currentDx = 0;
    let currentDy = 0;
    // Cached because cellEl is null when the pointer is in the edge zone
    // (outside cells), but we still need to scroll horizontally.
    let lastHorizontalScroller: HTMLElement | null = null;

    // Recorded on pointerdown — sweep only activates after drag threshold.
    let sweepStart: {
      x: number;
      y: number;
      rowKey: string;
      columnId: string;
    } | null = null;

    const stopAutoScroll = () => {
      cancelAnimationFrame(rafId);
      rafId = 0;
      currentDx = 0;
      currentDy = 0;
    };

    // Capture phase + stopPropagation blocks dnd-kit's bubble-phase
    // listener on <tr>. Only record the start here — no startSweep
    // or focusCell, so existing click/edit/popover handlers are untouched.
    const onPointerDown = (e: PointerEvent) => {
      if (e.button !== 0 || e.shiftKey || e.pointerType !== 'mouse') {
        return;
      }

      const target = (e.target as HTMLElement).closest<HTMLElement>(
        '[data-row-key][data-column-id]',
      );
      if (!target) {
        return;
      }

      const rowKey = target.getAttribute('data-row-key')!;
      const columnId = target.getAttribute('data-column-id')!;

      if (cellInteraction.isCellEditing(rowKey, columnId)) {
        return;
      }

      e.stopPropagation();
      sweepStart = { x: e.clientX, y: e.clientY, rowKey, columnId };
    };

    const onPointerUp = () => {
      sweepStart = null;
      cellInteraction.stopSweep();
      stopAutoScroll();
    };

    const onPointerMove = (e: PointerEvent) => {
      // Before sweep is active: check if drag threshold is exceeded.
      if (sweepStart && !cellInteraction.isSelecting) {
        const dx = e.clientX - sweepStart.x;
        const dy = e.clientY - sweepStart.y;
        if (Math.abs(dx) < DRAG_THRESHOLD && Math.abs(dy) < DRAG_THRESHOLD) {
          return;
        }
        cellInteraction.startSweep(sweepStart.rowKey, sweepStart.columnId);
        container.setPointerCapture(e.pointerId);
      }

      if (!cellInteraction.isSelecting) {
        return;
      }

      // Resolve the cell under the pointer — handles fast mouse movement
      const el = document.elementFromPoint(e.clientX, e.clientY);
      const cellEl = el?.closest<HTMLElement>('[data-row-key][data-column-id]');
      if (cellEl) {
        const rowKey = cellEl.getAttribute('data-row-key')!;
        const columnId = cellEl.getAttribute('data-column-id')!;
        cellInteraction.sweepTo(rowKey, columnId);
      }

      updateAutoScroll(e, cellEl ?? null);
    };

    // Auto-scroll when near container edges.
    // Vertical and horizontal scroll are on separate elements:
    // - Vertical: collection.scrollableContent (overflowY: auto)
    // - Horizontal: the table's scrollWrapper from WDS (overflowX: auto)
    const updateAutoScroll = (e: PointerEvent, cellEl: HTMLElement | null) => {
      const verticalScroller = getVerticalScroller();
      const hScroller = cellEl?.closest<HTMLElement>('table')?.parentElement;
      if (hScroller) {
        lastHorizontalScroller = hScroller;
      }

      const vRect = verticalScroller?.getBoundingClientRect();
      const hRect = lastHorizontalScroller?.getBoundingClientRect();

      const vRectTop = vRect?.top ?? 0;
      const headerBottom =
        state.tableState.headerElement?.getBoundingClientRect().bottom ?? 0;
      const vTopRef =
        headerBottom > vRectTop ? headerBottom - EDGE_ZONE : vRectTop;
      currentDy = vRect
        ? computeEdgeDelta(e.clientY, vTopRef, vRect.bottom)
        : 0;
      currentDx = hRect
        ? computeEdgeDelta(e.clientX, hRect.left, hRect.right)
        : 0;

      if (currentDx === 0 && currentDy === 0) {
        cancelAnimationFrame(rafId);
        rafId = 0;
      } else if (rafId === 0) {
        const scroll = () => {
          if (rafId === 0) {
            return;
          }
          if (currentDy !== 0) {
            getVerticalScroller()?.scrollBy(0, currentDy);
          }
          if (currentDx !== 0) {
            lastHorizontalScroller?.scrollBy(currentDx, 0);
          }
          rafId = requestAnimationFrame(scroll);
        };
        rafId = requestAnimationFrame(scroll);
      }
    };

    container.addEventListener('pointerdown', onPointerDown, true);
    container.addEventListener('pointermove', onPointerMove);
    container.addEventListener('pointerup', onPointerUp);
    container.addEventListener('lostpointercapture', onPointerUp);
    document.addEventListener('pointerup', onPointerUp);

    return () => {
      container.removeEventListener('pointerdown', onPointerDown, true);
      container.removeEventListener('pointermove', onPointerMove);
      container.removeEventListener('pointerup', onPointerUp);
      container.removeEventListener('lostpointercapture', onPointerUp);
      document.removeEventListener('pointerup', onPointerUp);
      stopAutoScroll();
      cellInteraction.stopSweep();
    };
  }, [state, containerRef]);
}
