import React, { useState, useMemo, useRef } from "react";
import { ModuleId, ModuleLookup, NavLeftHandSideItemParent } from "./types";
import TopListItemInner from "./TopListItemInner";
import UnpinnedDropZone from "./UnpinnedDropZone";
import rbd, {
  DragDropContext,
  Droppable,
  Draggable
} from "react-beautiful-dnd";
import DragAndDropGripper from "./DragAndDropGripper";
import { Dropzones } from "./types";
import UnpinnedMenu from "./UnpinnedMenu";

const makeParentItemWithChildren = (
  lookup: ModuleLookup,
  id: ModuleId
): NavLeftHandSideItemParent => {
  const parentItem = lookup[id];
  const subItems =
    parentItem &&
    parentItem.subItems &&
    parentItem.subItems.map(subId => makeParentItemWithChildren(lookup, subId));
  return {
    ...parentItem,
    id,
    subItems
  };
};

export interface Props {
  selectedModuleId?: ModuleId | null;
  moduleLookup: ModuleLookup;
  pinnedIds: ModuleId[];
  onModuleClick: (moduleId: ModuleId) => void;
  onPinnedItemsChange?: (moduleIds: ModuleId[]) => void;
  children: React.ReactNode;
  BottomComponent?: React.ReactNode;
}

export default function NavLeftHandSide({
  selectedModuleId,
  moduleLookup,
  pinnedIds,
  onPinnedItemsChange,
  children,
  onModuleClick,
  BottomComponent
}: Props) {
  const [isDraggingAny, setIsDraggingAny] = useState(false);
  const navRef = useRef();

  const unpinnedIds = useMemo(() => {
    const childIds = Object.values(moduleLookup).reduce(
      (acc: ModuleId[], { subItems }) => acc.concat(subItems || []),
      []
    );
    return Object.keys(moduleLookup).filter(
      (id: ModuleId) => !(pinnedIds.includes(id) || childIds.includes(id))
    ) as ModuleId[];
  }, [moduleLookup, pinnedIds]);

  function reorder(list: any[], startIndex: number, endIndex: number) {
    const result = list;
    const [removed] = result.splice(startIndex, 1);
    result.splice(endIndex, 0, removed);
    return result;
  }

  const onDragStart: rbd.OnDragStartResponder = () => {
    setIsDraggingAny(true);
  };

  const onDragEnd: rbd.OnDragEndResponder = ({ destination, source }) => {
    setIsDraggingAny(false);
    if (
      !destination ||
      (destination.index === source.index &&
        destination.droppableId === source.droppableId)
    ) {
      return;
    }
    let newPinnedIds;
    if (destination.droppableId === Dropzones.MODULES_DROP_ZONE) {
      newPinnedIds = reorder(pinnedIds, source.index, destination.index);
    } else {
      newPinnedIds = [...pinnedIds];
      newPinnedIds.splice(source.index, 1);
    }
    onPinnedItemsChange(newPinnedIds);
  };

  return (
    <nav
      className="auik-fixed auik-w-16 auik-z-20 auik-bg-blue-900 auik-h-screen auik-flex auik-flex-col"
      ref={navRef}
    >
      <div className="auik-absolute auik-left-full auik-z-10">{children}</div>
      <DragDropContext onDragEnd={onDragEnd} onDragStart={onDragStart}>
        <Droppable droppableId={Dropzones.MODULES_DROP_ZONE}>
          {provided => (
            <ul
              ref={provided.innerRef}
              {...provided.droppableProps}
              className="auik-flex-grow auik-flex-shrink auik-flex auik-flex-col auik-mb-4 auik-overflow-hidden auik-z-20 auik-bg-blue-900 auik-overflow-y-auto"
            >
              {pinnedIds.map((parentId, index) => (
                <Draggable key={parentId} draggableId={parentId} index={index}>
                  {(provided, snapshot) => (
                    <li ref={provided.innerRef} {...provided.draggableProps}>
                      <TopListItemInner
                        parentItem={makeParentItemWithChildren(
                          moduleLookup,
                          parentId
                        )}
                        selectedItem={selectedModuleId}
                        isDraggingAny={isDraggingAny}
                        isDragging={snapshot.isDragging}
                        isDraggingOverUnpinned={
                          snapshot.draggingOver === Dropzones.UNPINNED_DROP_ZONE
                        }
                        onClickItem={id => {
                          onModuleClick(id);
                        }}
                      >
                        <DragAndDropGripper
                          snapshot={snapshot}
                          provided={provided}
                          isDraggingAny={isDraggingAny}
                        />
                      </TopListItemInner>
                    </li>
                  )}
                </Draggable>
              ))}
              {provided.placeholder}
              <li className="auik-flex auik-items-center auik-justify-center auik-mt-8">
                {!!unpinnedIds.length && (
                  <UnpinnedMenu
                    moduleLookup={moduleLookup}
                    unpinnedIds={unpinnedIds}
                    onMenuItemClick={id => {
                      onModuleClick(id);
                      onPinnedItemsChange([...pinnedIds, id]);
                    }}
                  />
                )}
                {/* TODO style popover border and shadow to match design. auik classes have lower specificity */}
              </li>
            </ul>
          )}
        </Droppable>
        {!!BottomComponent && (
          <div className="auik-mt-auto">{BottomComponent}</div>
        )}
        <UnpinnedDropZone isDraggingAny={isDraggingAny} />
      </DragDropContext>
    </nav>
  );
}
