import React, { useEffect, useLayoutEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import {
   closestCenter,
   DragOverlay,
   DndContext,
   KeyboardSensor,
   MouseSensor,
   TouchSensor,
   useSensor,
   useSensors,
} from '@dnd-kit/core';
import { restrictToWindowEdges } from '@dnd-kit/modifiers';
import {
   arrayMove,
   useSortable,
   SortableContext,
   sortableKeyboardCoordinates,
   rectSortingStrategy,
} from '@dnd-kit/sortable';
import { IImagesSortableContainerProps, IImagesSortableProps } from './ImagesSortable.interface';
import * as S from './ImagesSortable.style';

export const Item = React.memo(
   React.forwardRef<HTMLDivElement, any>(function Item(
      {
         color,
         dragOverlay,
         dragging,
         handle,
         height,
         index,
         listeners,
         onRemove,
         sorting,
         transition,
         transform,
         value,
         wrapperStyle,
         disabled,
         renderItem,
         ...props
      },
      ref,
   ) {
      useEffect(() => {
         if (!dragOverlay) {
            return;
         }

         document.body.style.cursor = 'grabbing';

         return () => {
            document.body.style.cursor = '';
         };
      }, [dragOverlay]);

      return (
         <S.ItemContainer
            style={
               {
                  // transition: dragging ? 'transform 200ms ease' : undefined,
                  '--translate-x': transform ? `${Math.round(transform.x)}px` : undefined,
                  '--translate-y': transform ? `${Math.round(transform.y)}px` : undefined,
                  '--scale-x': transform?.scaleX ? `${transform.scaleX}` : undefined,
                  '--scale-y': transform?.scaleY ? `${transform.scaleY}` : undefined,
                  '--index': index,
                  // '--dragging': dragging ? 'true' : 'false',
                  '--color': color,
               } as React.CSSProperties
            }
            ref={ref}
         >
            {renderItem({ value, dragging, dragOverlay, disabled, listeners, index })}
         </S.ItemContainer>
      );
   }),
);

export function ImagesSortable({ images, onChange, renderItem }: IImagesSortableProps): any {
   const items = images.map((el, i) => el?.id.toString() ?? i);

   const [activeId, setActiveId] = useState<string | null>(null);
   const sensors = useSensors(
      useSensor(MouseSensor, {}),
      useSensor(TouchSensor, {}),
      useSensor(KeyboardSensor, {
         coordinateGetter: sortableKeyboardCoordinates,
      }),
   );

   const getIndex = items.indexOf.bind(items);
   const activeIndex = activeId ? getIndex(activeId) : -1;

   return (
      <DndContext
         sensors={sensors}
         collisionDetection={closestCenter}
         onDragStart={({ active }) => {
            if (!active) {
               return;
            }

            setActiveId(active.id);
         }}
         onDragEnd={({ over }) => {
            setActiveId(null);

            if (over) {
               const overIndex = getIndex(over.id);
               if (activeIndex !== overIndex) {
                  onChange(arrayMove(images, activeIndex, overIndex));
               }
            }
         }}
         onDragCancel={() => setActiveId(null)}
         modifiers={[restrictToWindowEdges]}
      >
         <S.Wrapper>
            <SortableContext items={items} strategy={rectSortingStrategy}>
               <S.GridContainer>
                  {items.map((value, index) => {
                     if (!images[Number(index)]) return null;

                     return (
                        <SortableItem
                           key={images[Number(index)]?.id}
                           id={value}
                           value={images[Number(index)]}
                           index={index}
                           useDragOverlay
                           renderItem={renderItem}
                        />
                     );
                  })}
               </S.GridContainer>
            </SortableContext>
         </S.Wrapper>
         {createPortal(
            <DragOverlay adjustScale>
               {activeId ? (
                  <Item value={images[activeIndex]} handle={false} dragOverlay renderItem={renderItem} />
               ) : null}
            </DragOverlay>,
            document.body,
         )}
      </DndContext>
   );
}

export function SortableItem({ id, index, useDragOverlay, value, renderItem }: IImagesSortableContainerProps): any {
   const { isDragging, isSorting, listeners, setNodeRef, transform, transition } = useSortable({
      id,
      disabled: !!value?.disabled,
   });

   return (
      <Item
         ref={setNodeRef}
         value={value}
         dragging={isDragging}
         sorting={isSorting}
         index={index}
         transform={transform}
         transition={!useDragOverlay && isDragging ? 'none' : transition}
         listeners={listeners}
         dragOverlay={!useDragOverlay && isDragging}
         renderItem={renderItem}
         disabled={!!value?.disabled}
      />
   );
}
