import { useTreeViewContext } from "../../internals/TreeViewProvider/index.js";
import { useTreeViewLabel } from "../../internals/plugins/useTreeViewLabel/index.js";
import { hasPlugin } from "../../internals/utils/plugins.js";

/**
 * Plugins that need to be present in the Tree View in order for `useTreeItem2Utils` to work correctly.
 */

/**
 * Plugins that `useTreeItem2Utils` can use if they are present, but are not required.
 */

const isItemExpandable = reactChildren => {
  if (Array.isArray(reactChildren)) {
    return reactChildren.length > 0 && reactChildren.some(isItemExpandable);
  }
  return Boolean(reactChildren);
};
export const useTreeItem2Utils = ({
  itemId,
  children
}) => {
  const {
    instance,
    selection: {
      multiSelect
    },
    publicAPI
  } = useTreeViewContext();
  const status = {
    expandable: isItemExpandable(children),
    expanded: instance.isItemExpanded(itemId),
    focused: instance.isItemFocused(itemId),
    selected: instance.isItemSelected(itemId),
    disabled: instance.isItemDisabled(itemId),
    editing: instance?.isItemBeingEdited ? instance?.isItemBeingEdited(itemId) : false,
    editable: instance.isItemEditable ? instance.isItemEditable(itemId) : false
  };
  const handleExpansion = event => {
    if (status.disabled) {
      return;
    }
    if (!status.focused) {
      instance.focusItem(event, itemId);
    }
    const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey);

    // If already expanded and trying to toggle selection don't close
    if (status.expandable && !(multiple && instance.isItemExpanded(itemId))) {
      instance.toggleItemExpansion(event, itemId);
    }
  };
  const handleSelection = event => {
    if (status.disabled) {
      return;
    }
    if (!status.focused && !status.editing) {
      instance.focusItem(event, itemId);
    }
    const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey);
    if (multiple) {
      if (event.shiftKey) {
        instance.expandSelectionRange(event, itemId);
      } else {
        instance.selectItem({
          event,
          itemId,
          keepExistingSelection: true
        });
      }
    } else {
      instance.selectItem({
        event,
        itemId,
        shouldBeSelected: true
      });
    }
  };
  const handleCheckboxSelection = event => {
    const hasShift = event.nativeEvent.shiftKey;
    if (multiSelect && hasShift) {
      instance.expandSelectionRange(event, itemId);
    } else {
      instance.selectItem({
        event,
        itemId,
        keepExistingSelection: multiSelect,
        shouldBeSelected: event.target.checked
      });
    }
  };
  const toggleItemEditing = () => {
    if (!hasPlugin(instance, useTreeViewLabel)) {
      return;
    }
    if (instance.isItemEditable(itemId)) {
      if (instance.isItemBeingEdited(itemId)) {
        instance.setEditedItemId(null);
      } else {
        instance.setEditedItemId(itemId);
      }
    }
  };
  const handleSaveItemLabel = (event, label) => {
    if (!hasPlugin(instance, useTreeViewLabel)) {
      return;
    }

    // As a side effect of `instance.focusItem` called here and in `handleCancelItemLabelEditing` the `labelInput` is blurred
    // The `onBlur` event is triggered, which calls `handleSaveItemLabel` again.
    // To avoid creating an unwanted behavior we need to check if the item is being edited before calling `updateItemLabel`
    // using `instance.isItemBeingEditedRef` instead of `instance.isItemBeingEdited` since the state is not yet updated in this point
    if (instance.isItemBeingEditedRef(itemId)) {
      instance.updateItemLabel(itemId, label);
      toggleItemEditing();
      instance.focusItem(event, itemId);
    }
  };
  const handleCancelItemLabelEditing = event => {
    if (!hasPlugin(instance, useTreeViewLabel)) {
      return;
    }
    if (instance.isItemBeingEditedRef(itemId)) {
      toggleItemEditing();
      instance.focusItem(event, itemId);
    }
  };
  const interactions = {
    handleExpansion,
    handleSelection,
    handleCheckboxSelection,
    toggleItemEditing,
    handleSaveItemLabel,
    handleCancelItemLabelEditing
  };
  return {
    interactions,
    status,
    publicAPI
  };
};