import React, { FC, useState } from 'react';
import {
  DndContext,
  DragOverlay,
  DragEndEvent,
  DragMoveEvent,
} from '@dnd-kit/core';
import dayjs from 'dayjs';
import { Draggable } from '../dndComponents/Draggable';
import {
  ECalendarMode,
  ECalendarView,
  IDndProviderProps,
  ISlot,
} from '../types';
import { calculateNewDate, getMinutesDifferenceByStep } from '../utils';
import { oneHourHeight, oneHourWidth } from '../utils/timeUtils';
import { useMiddlewareContext } from '../hooks/useMiddlewareContext';

export const DndProvider: FC<IDndProviderProps> = ({
  children,
  DragOverlayComponent,
  onChange,
}) => {
  const { options, handleDrop } = useMiddlewareContext();
  const { view = ECalendarView.VERTICAL, mode = ECalendarMode.DAILY } = options;

  const [draggableItem, setDraggableItem] = useState<ISlot | undefined>();
  const [deltas, setDeltas] = useState(0);
  const [didMove, setDidMove] = useState(false);

  const [movingDelta, setMovingDelta] = useState(0);
  const debouncedDelta = movingDelta - deltas;

  const handleDragEnd = ({ delta, active, over }: DragEndEvent) => {
    if (!draggableItem) return;

    let startDate: string | undefined;
    let endDate: string | undefined;
    let overId = over?.id || 0;

    if (mode === ECalendarMode.DAILY) {
      const currentDelta = view === ECalendarView.VERTICAL ? delta.y : delta.x;
      const size =
        view === ECalendarView.VERTICAL ? oneHourHeight : oneHourWidth;

      let minutesDifference = Math.round(
        Number(currentDelta - deltas) * (60 / size)
      );

      minutesDifference = getMinutesDifferenceByStep(
        draggableItem.startDate,
        minutesDifference,
        options.dndStepLength
      );

      startDate = calculateNewDate(draggableItem.startDate, minutesDifference);
      endDate = calculateNewDate(draggableItem.endDate, minutesDifference);
    } else {
      const newDate = over?.id.split('_')[1];
      const date = dayjs.utc(newDate);

      startDate = dayjs
        .utc(draggableItem.startDate)
        .set('date', date.get('date'))
        .set('month', date.get('month'))
        .set('year', date.get('year'))
        .toISOString();

      endDate = dayjs
        .utc(draggableItem.endDate)
        .set('date', date.get('date'))
        .set('month', date.get('month'))
        .set('year', date.get('year'))
        .toISOString();

      overId = over?.id.split('_')[0] || 0;
    }

    let result = true;

    if (onChange) {
      const onChangeData = {
        type: 'SLOT_DROP',
        data: {
          startDate,
          endDate,
          startTime: dayjs.utc(startDate).format('HH:mm'),
          endTime: dayjs.utc(endDate).format('HH:mm'),
          overId: String(overId),
          parentId: String(draggableItem.spaceId),
          slotId: String(draggableItem.id),
        },
      };

      const answer = onChange(onChangeData);
      result = typeof answer === 'boolean' ? answer : true;
    }

    if (result) {
      handleDrop(
        active.id,
        active.data.current?.weeklyView
          ? +active.data.current.parentId.split('_')[0]
          : active.data.current?.parentId,
        active.data.current?.weeklyView && over
          ? +over?.id.split('_')[0] || 0
          : over?.id || 0,
        over?.id.split('_')[1] || startDate || '',
        endDate
      );
    }

    setDidMove(false);
    setDraggableItem(undefined);
  };

  const handleDragMove = ({ delta }: DragMoveEvent) => {
    const isVertical = view === ECalendarView.VERTICAL;

    if (!didMove && mode === ECalendarMode.DAILY) {
      setDeltas(isVertical ? delta.y : delta.x);
      setDidMove(true);
    }

    setMovingDelta(isVertical ? delta.y : delta.x);
  };

  return (
    <DndContext
      onDragStart={({ active }) =>
        setDraggableItem(active?.data?.current?.event)
      }
      onDragEnd={handleDragEnd}
      onDragMove={handleDragMove}
      onDragCancel={() => setDraggableItem(undefined)}
    >
      {children}
      <DragOverlay>
        {draggableItem && (
          <Draggable
            item={draggableItem}
            delta={debouncedDelta}
            Component={DragOverlayComponent}
          />
        )}
      </DragOverlay>
    </DndContext>
  );
};
