import React, { forwardRef, HTMLAttributes, useEffect, useState } from 'react';
import { FiltersMap } from '@wix/bex-core';
import { DragAndDropBulkSubmit, DragAndDropSubmit } from '../DragAndDrop';
import { NestedTableNestedModeState } from '../../state';
import { observer } from 'mobx-react-lite';
import { 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 { addEventListener } from '@wix/bex-core/util';
import { NestedTableDragAndDropState } from './NestedTableDragAndDropState';
import { minimalRequiredWdsRuntimeCheck } from '../../minimalRequiredWdsRuntimeCheck';
import { validateNestedTableDragAndDropProps } from './validateNestedTableDragAndDropProps';
import { DragOverlayTestComponent } from '../DragAndDrop/DragOverlayTestComponent';
import { DragOverlayProps } from '@wix/wix-style-react-incubator/dnd-kit/core';
import { NestedTableDragAndDropOverlay } from './NestedTableDragAndDropOverlay';
import { createUseIsDropDisabled } from './createUseIsDropDisabled';
import { usePageContext } from '@wix/bex-core/react';

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

function _NestedTableDragAndDropContext<
  C extends string,
  T,
  F extends FiltersMap,
>(props: NestedTableDragAndDropContextProps<C, T, F>) {
  const { state: nestedState, onSubmit, onBulkSubmit, children } = props;
  const { type: Table, props: tableProps } = children;

  if (process.env.NODE_ENV !== 'production') {
    minimalRequiredWdsRuntimeCheck({
      required: '^1.109.2',
      requiredBy: 'WixPatterns/NestedTable',
    });
    validateNestedTableDragAndDropProps(nestedState);
  }

  const state =
    nestedState.nestedTableDragAndDrop as NestedTableDragAndDropState<C>;
  const useIsDropDisabled = createUseIsDropDisabled(state);

  const dragAndDropState = state.dnd;

  const { modalsContainerRef } = usePageContext();

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

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

  const [dragAndDropContext] = useState(
    (): IDragAndDropContext => ({
      useIsDropDisabled,
      closestCenterOnViewPort: true,
      onDragAndDropDisabledRowPointerDown: (id, e) => {
        dragAndDropState.attachDisabledDragAttempt(id as string, e, 'y');
      },
      cancelDrop: async (ev) => {
        return 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,
            );
          },
        );
      },
      overlayWrapperElement: forwardRef<
        HTMLDivElement,
        HTMLAttributes<HTMLDivElement>
      >((props, ref) => (
        <NestedTableDragAndDropOverlay
          innerRef={ref}
          state={state}
          {...props}
        />
      )),
      ...(process.env.NODE_ENV === 'test' && {
        DragOverlayComponentType: (props: DragOverlayProps) => (
          <DragOverlayTestComponent {...props} state={dragAndDropState} />
        ),
      }),
      keyboardCodes: dragAndDropState.keyboardCodes,
      autoScrollOptions: {
        acceleration: 6,
      },
      hideOverlayBottomShadow: true,
      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,
            })),
        }),
      },
    }),
  );

  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 NestedTableDragAndDropContext = observer(
  _NestedTableDragAndDropContext,
);
