import styled from '@emotion/styled';
import { useMemo } from 'react';

import MultiSelectContext from '@/components/clipBrowser/MultiSelectContext';
import { useContextSelector } from '@/hooks/useContextSelector';

import DragAndDropContext, {
  AnyDraggedObject,
  DraggedClip,
  DraggedMultiClip,
} from './DragAndDropContext';

const Wrapper = styled.div<{ dragging: boolean; draggable: boolean }>`
  cursor: ${({ draggable }) => (draggable ? 'grab' : 'default')};
  ${({ draggable }) =>
    draggable &&
    `
    &:active {
      cursor: grabbing;
    }
    `}
  opacity: ${({ dragging }) => (dragging ? 0.5 : 1)};
`;

export const useClipDraggedObject = (
  clipId: string
): DraggedClip | DraggedMultiClip => {
  const selectedClipIds = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.selectedClipIds
  );

  return useMemo(() => {
    if (selectedClipIds.includes(clipId)) {
      return {
        type: 'multi-clip' as const,
        payload: { clipIds: selectedClipIds },
      };
    }
    return {
      type: 'clip' as const,
      payload: { clipId },
    };
  }, [clipId, selectedClipIds]);
};

export default function Draggable<T extends AnyDraggedObject>({
  children,
  draggedObject,
  isInCurrentDraggedObject,
  onMouseDown,
}: {
  children: React.ReactNode;
  draggedObject: T | null;
  isInCurrentDraggedObject?: (
    currentDraggedObject: AnyDraggedObject
  ) => boolean;
  onMouseDown?: (draggedObject: T | null) => void;
}) {
  const handleDragStart = useContextSelector(
    DragAndDropContext,
    (ctx) => ctx.onDragStart
  );
  const onDragEnd = useContextSelector(
    DragAndDropContext,
    (ctx) => ctx.onDragEnd
  );
  const currentDraggedObject = useContextSelector(
    DragAndDropContext,
    (ctx) => ctx.draggedObject
  );
  const inCurrentDraggedObject = useMemo(
    () =>
      !!currentDraggedObject &&
      (isInCurrentDraggedObject
        ? isInCurrentDraggedObject(currentDraggedObject)
        : currentDraggedObject === draggedObject),
    [draggedObject, currentDraggedObject, isInCurrentDraggedObject]
  );
  return (
    <Wrapper
      dragging={inCurrentDraggedObject}
      draggable={!!draggedObject}
      onMouseDown={() => {
        onMouseDown?.(draggedObject as T | null);
      }}
      onDragStart={() => {
        if (draggedObject) {
          handleDragStart(draggedObject);
        }
      }}
      onDragEnd={() => {
        onDragEnd();
      }}
    >
      {children}
    </Wrapper>
  );
}
