import React, { useEffect, useState } from 'react';
import { FiltersMap } from '@wix/bex-core';
import { DragAndDropBulkSubmit, DragAndDropSubmit } from '../DragAndDrop';
import { ToolbarCollectionState } from '../../state';
import { observer } from 'mobx-react-lite';
import type { TableProps } from '@wix/design-system';
import {
  DragAndDropContext,
  IDragAndDropContext,
} from '@wix/wix-style-react-incubator/drag-and-drop';
import { DragAndDropAnnouncements } from '../DragAndDrop/DragAndDropAnnouncements';
import {
  dragHandleTestEvents,
  sortableItemTestEvents,
} from '../GridDragAndDropDndKit/dndTestEventHandlers';
import { useTableContext } from '../../providers';
import { addEventListener } from '@wix/bex-core/util';
import { TableDragAndDropState } from '../DragAndDrop/TableDragAndDropState';
import { createUseIsDropDisabled } from './createUseIsDropDisabled';
import { usePageContext } from '@wix/bex-core/react';

export interface TableDragAndDropContextProps<T, F extends FiltersMap> {
  state: ToolbarCollectionState<T, F>;
  onSubmit?: DragAndDropSubmit<T, F>;
  onBulkSubmit?: DragAndDropBulkSubmit<T, F>;
  children: React.ReactElement<
    TableProps<T>,
    React.ComponentType<TableProps<T>>
  >;
}

function _TableDragAndDropContext<T, F extends FiltersMap = FiltersMap>(
  props: TableDragAndDropContextProps<T, F>,
) {
  const { onSubmit, onBulkSubmit, children } = props;
  const { type: Table, props: tableProps } = children;

  const tableState = useTableContext<T, F>();
  const { modalsContainerRef } = usePageContext();

  const state = tableState.tableDragAndDropState as TableDragAndDropState<T, F>;

  const dragAndDropState = state.dragAndDrop.dnd;

  const useIsDropDisabled = createUseIsDropDisabled(dragAndDropState);

  useEffect(() => state.init(), []);

  dragAndDropState.onSubmit = onSubmit;
  dragAndDropState.onBulkSubmit = onBulkSubmit;

  const [dragAndDropContext] = useState<IDragAndDropContext>(() => ({
    useIsDropDisabled,
    onDragAndDropDisabledRowPointerDown: (id, e) => {
      dragAndDropState.attachDisabledDragAttempt(id as string, e, 'y');
    },
    cancelDrop: (ev) => dragAndDropState.beforeDrop(ev),
    dropAnimation: {
      duration: dragAndDropState.dropAnimation.duration,
      easing: dragAndDropState.dropAnimation.easing,
    },
    announcements: dragAndDropState.nullAnnouncements,
    a11yContainer: modalsContainerRef.current,
    onDragOver: ({ over }) => dragAndDropState.onDragOver({ over }),
    onDragMove: (event) => dragAndDropState.onDragMove(event),
    restoreFocus: false,
    onDropAnimationEnd: (id, dragHandleRef) => {
      return addEventListener(
        dragAndDropState.events,
        'dropAnimationEnd',
        () => {
          dragAndDropState.focusDragHandleIfDropped(id, dragHandleRef.current);
        },
      );
    },
    eventHandlers: {
      row: ({ id, disabled }) => {
        const testEvents =
          process.env.NODE_ENV === 'test'
            ? sortableItemTestEvents({
                state: dragAndDropState,
                id,
                disabled,
              })
            : undefined;

        return {
          ...testEvents,
          onPointerDown: (e) => {
            dragAndDropState.containerEvents.onPointerDown?.(e);
            testEvents?.onPointerDown?.(e);
          },
        };
      },
      handle: ({ id }) => ({
        onPointerDown: (e) => {
          dragAndDropState.handleEvents.onPointerDown?.(e);
        },
        onKeyDown: (e) => {
          dragAndDropState.handleEvents.onKeyDown?.(e);
        },
        ...(process.env.NODE_ENV === 'test' &&
          dragHandleTestEvents({
            state: dragAndDropState,
            id,
          })),
      }),
      keyboardCodes: dragAndDropState.keyboardCodes,
    },
  }));

  dragAndDropContext.a11yContainer = modalsContainerRef.current;

  return (
    <DragAndDropContext.Provider value={dragAndDropContext}>
      <Table
        {...tableProps}
        isDragAndDropDisabled={dragAndDropState.isDisabled}
        onDragStart={(event) => dragAndDropState.onDragStart(event)}
        onDragEnd={(event) => dragAndDropState.onDragEnd(event)}
        onDragCancel={() => dragAndDropState.onDragCancel()}
      />

      <DragAndDropAnnouncements
        state={dragAndDropState}
        dataHook="collection-table-dnd-live-region"
      />
    </DragAndDropContext.Provider>
  );
}

export const TableDragAndDropContext = observer(_TableDragAndDropContext);
