import classNames from 'classnames';
import {
  Dispatch,
  MutableRefObject,
  ReactNode,
  SetStateAction,
  forwardRef,
  useEffect,
  useState,
} from 'react';
import BodyTr from './BodyTr';
import {Empty} from 'antd';
import {HeaderGroup, Row, RowModel} from '@tanstack/react-table';
import {
  DndContext,
  DragEndEvent,
  DragOverlay,
  DragStartEvent,
  PointerSensor,
  closestCenter,
  useSensor,
} from '@dnd-kit/core';
import {
  SortableContext,
  arrayMove,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import {debounce} from 'lodash-es';

type Props = {
  scrollProgess: 'start' | 'end' | 'process',
  getCenterTotalSize: () => number,
  getRowModel: () => RowModel<Record<string, any>>,
  highlightValue?: unknown,
  hightlightKey?: any,
  getHeaderGroups: () => HeaderGroup<Record<string, any>>[],
  enableDnd?: boolean,
  rawKey?: any,
  data: any[],
  setData: Dispatch<SetStateAction<any[]>>,
  isSecondLevel?: boolean,
  renderSubComponent?: (row: Row<Record<string, any>>) => ReactNode,
};

type ActiveState = {
  row: Row<Record<string, any>>,
} | null;

export default forwardRef<HTMLDivElement, Props>(function TableBody(
  {
    scrollProgess,
    getCenterTotalSize,
    getRowModel,
    highlightValue,
    hightlightKey,
    getHeaderGroups,
    data,
    enableDnd,
    rawKey,
    setData,
    isSecondLevel,
    renderSubComponent,
  },
  ref,
) {
  const sensor = useSensor(PointerSensor, {
    activationConstraint: {distance: 10},
  });

  const [active, setActive] = useState<ActiveState>(null);
  function onDragStart(event: DragStartEvent) {
    setActive(event.active.data.current as ActiveState);
  }
  function onDragEnd({over, active}: DragEndEvent) {
    setActive(null);
    if (!over)
      return;

    const {id: activeId} = active,
          {id: overId} = over;

    if (activeId === overId)
      return;

    setData(function(prev) {
      const fromIdx = prev.findIndex(val => val[rawKey ?? 'id'] === activeId),
            toIdx = prev.findIndex(val => val[rawKey ?? 'id'] === overId);

      return arrayMove(prev, fromIdx, toIdx);
    });
  }

  const [tableWidth, setTableWidth] = useState(0);

  useEffect(function() {
    if (!enableDnd) return;

    const dom = (ref as MutableRefObject<HTMLDivElement>).current;
    const obsever = new ResizeObserver(debounce(
      function([entrie]) {
        setTableWidth(entrie.contentRect.width);
      },
      300,
      {leading: false, trailing: true},
    ));

    obsever.observe(dom);

    return () => obsever.disconnect();
  }, [enableDnd, ref]);

  return (
    <DndContext
      sensors={[sensor]}
      collisionDetection={closestCenter}
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
    >
      <SortableContext
        items={data.map(val => val[rawKey ?? 'id'])}
        strategy={verticalListSortingStrategy}
      >
        <div className="ld-table-body-wrapper" ref={ref}>
          <table
            className={classNames('ld-table', {
              'ld-table-enabled-right-fixed-shadow': scrollProgess !== 'end',
              'ld-table-enabled-left-fixed-shadow': scrollProgess !== 'start',
            })}
            style={{width: getCenterTotalSize()}}
          >
            <tbody className="ld-table-body">
              {getRowModel().rows.length > 0 ? (
                getRowModel().rows.map(function(row) {
                  return (
                    <BodyTr
                      key={row.id}
                      highlight={row.original[hightlightKey as string ?? 'id'] === highlightValue}
                      enableDnd={enableDnd}
                      row={row}
                      renderSubComponent={renderSubComponent}
                    />
                  );
                })
              ) : (
                <tr>
                  <td
                    colSpan={getHeaderGroups()[0].headers.length}
                    style={{padding: 0}}
                  >
                    <Empty
                      image={Empty.PRESENTED_IMAGE_SIMPLE}
                      style={{
                        width: '400px',
                        position: 'sticky',
                        left: '50%',
                        transform: 'translateX(-50%)',
                        overflow: 'hidden',
                        margin: 0,
                        padding: '50px 10px',
                      }}
                    />
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
        <DragOverlay>
          {active && (
            <div
              className={classNames({
                'ld-table-preview-transform': isSecondLevel,
              })}
              style={{width: tableWidth, overflow: 'hidden'}}
            >
              <table
                className={classNames('ld-table', {
                  'ld-table-enabled-right-fixed-shadow': scrollProgess !== 'end',
                  'ld-table-enabled-left-fixed-shadow': scrollProgess !== 'start',
                })}
                style={{width: getCenterTotalSize()}}
              >
                <tbody>
                  <BodyTr {...active} preview />
                </tbody>
              </table>
            </div>
          )}
        </DragOverlay>
      </SortableContext>
    </DndContext>
  );
});
