import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { cx } from '@emotion/css';
import { useMultipleSelection, useCombobox } from 'downshift';
import { useState, useMemo, useCallback } from 'react';
import { t } from '@grafana/i18n';
import { useStyles2 } from '../../themes/ThemeContext.mjs';
import { Icon } from '../Icon/Icon.mjs';
import { Box } from '../Layout/Box/Box.mjs';
import { Portal } from '../Portal/Portal.mjs';
import { Text } from '../Text/Text.mjs';
import { Tooltip } from '../Tooltip/Tooltip.mjs';
import { ComboboxList } from './ComboboxList.mjs';
import { SuffixIcon } from './SuffixIcon.mjs';
import { ValuePill } from './ValuePill.mjs';
import { itemToString } from './filter.mjs';
import { getComboboxStyles } from './getComboboxStyles.mjs';
import { getMultiComboboxStyles } from './getMultiComboboxStyles.mjs';
import { ALL_OPTION_VALUE } from './types.mjs';
import { useComboboxFloat } from './useComboboxFloat.mjs';
import { useMeasureMulti, MAX_SHOWN_ITEMS } from './useMeasureMulti.mjs';
import { useMultiInputAutoSize } from './useMultiInputAutoSize.mjs';
import { useOptions } from './useOptions.mjs';

"use strict";
const MultiCombobox = (props) => {
  const {
    placeholder,
    onChange,
    value,
    width,
    enableAllOption,
    invalid,
    disabled,
    minWidth,
    maxWidth,
    isClearable,
    createCustomValue = false,
    "aria-labelledby": ariaLabelledBy,
    "data-testid": dataTestId,
    portalContainer,
    prefixIcon
  } = props;
  const styles = useStyles2(getComboboxStyles);
  const [inputValue, setInputValue] = useState("");
  const allOptionItem = useMemo(() => {
    return {
      label: inputValue === "" ? t("multicombobox.all.title", "All") : t("multicombobox.all.title-filtered", "All (filtered)"),
      // Type casting needed to make this work when T is a number
      value: ALL_OPTION_VALUE
    };
  }, [inputValue]);
  const {
    options: baseOptions,
    updateOptions,
    asyncLoading,
    asyncError
  } = useOptions(props.options, createCustomValue);
  const options = useMemo(() => {
    const addAllOption = enableAllOption && baseOptions.length > 1;
    return addAllOption ? [allOptionItem, ...baseOptions] : baseOptions;
  }, [baseOptions, enableAllOption, allOptionItem]);
  const loading = props.loading || asyncLoading;
  const selectedItems = useMemo(() => {
    if (!value) {
      return [];
    }
    return getSelectedItemsFromValue(value, typeof props.options !== "function" ? props.options : baseOptions);
  }, [value, props.options, baseOptions]);
  const { measureRef, counterMeasureRef, suffixMeasureRef, shownItems } = useMeasureMulti(
    selectedItems,
    width,
    disabled
  );
  const isOptionSelected = useCallback(
    (item) => selectedItems.some((opt) => opt.value === item.value),
    [selectedItems]
  );
  const { getSelectedItemProps, getDropdownProps, setSelectedItems, addSelectedItem, removeSelectedItem, reset } = useMultipleSelection({
    selectedItems,
    // initially selected items,
    onStateChange: ({ type, selectedItems: newSelectedItems }) => {
      switch (type) {
        case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownBackspace:
        case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:
        case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace:
        case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:
        case useMultipleSelection.stateChangeTypes.FunctionAddSelectedItem:
        case useMultipleSelection.stateChangeTypes.FunctionSetSelectedItems:
        case useMultipleSelection.stateChangeTypes.FunctionReset:
          onChange(newSelectedItems != null ? newSelectedItems : []);
          break;
        default:
          break;
      }
    },
    stateReducer: (_state, actionAndChanges) => {
      const { changes } = actionAndChanges;
      return {
        ...changes,
        /**
         * TODO: Fix Hack!
         * This prevents the menu from closing when the user unselects an item in the dropdown at the expense
         * of breaking keyboard navigation.
         *
         * Downshift isn't really designed to keep selected items in the dropdown menu, so when you unselect an item
         * in a multiselect, the stateReducer tries to move focus onto another item which causes the menu to be closed.
         * This only seems to happen when you deselect the last item in the selectedItems list.
         *
         * Check out:
         *  - FunctionRemoveSelectedItem in the useMultipleSelection reducer https://github.com/downshift-js/downshift/blob/master/src/hooks/useMultipleSelection/reducer.js#L75
         *  - The activeIndex useEffect in useMultipleSelection https://github.com/downshift-js/downshift/blob/master/src/hooks/useMultipleSelection/index.js#L68-L72
         *
         * Forcing the activeIndex to -999 both prevents the useEffect that changes the focus from triggering (value never changes)
         * and prevents the if statement in useMultipleSelection from focusing anything.
         */
        activeIndex: -999
      };
    }
  });
  const {
    getToggleButtonProps,
    //getLabelProps,
    isOpen,
    highlightedIndex,
    getMenuProps,
    getInputProps,
    getItemProps
  } = useCombobox({
    items: options,
    itemToString,
    inputValue,
    selectedItem: null,
    stateReducer: (state, actionAndChanges) => {
      const { type } = actionAndChanges;
      let { changes } = actionAndChanges;
      const menuBeingOpened = state.isOpen === false && changes.isOpen === true;
      if (menuBeingOpened && changes.inputValue === state.inputValue) {
        changes = {
          ...changes,
          inputValue: ""
        };
      }
      switch (type) {
        case useCombobox.stateChangeTypes.InputKeyDownEnter:
        case useCombobox.stateChangeTypes.ItemClick:
          return {
            ...changes,
            isOpen: true,
            highlightedIndex: state.highlightedIndex
          };
        case useCombobox.stateChangeTypes.InputBlur:
          setInputValue("");
        default:
          return changes;
      }
    },
    onIsOpenChange: ({ isOpen: isOpen2, inputValue: inputValue2 }) => {
      if (isOpen2 && inputValue2 === "") {
        updateOptions(inputValue2);
      }
    },
    onStateChange: ({ inputValue: newInputValue, type, selectedItem: newSelectedItem }) => {
      switch (type) {
        case useCombobox.stateChangeTypes.InputKeyDownEnter:
        case useCombobox.stateChangeTypes.ItemClick:
          if (newSelectedItem == null ? void 0 : newSelectedItem.infoOption) {
            break;
          }
          if ((newSelectedItem == null ? void 0 : newSelectedItem.value) === ALL_OPTION_VALUE) {
            const isAllFilteredSelected = selectedItems.length === options.length - 1;
            const realOptions = options.slice(1).filter((option) => !option.infoOption);
            let newSelectedItems = isAllFilteredSelected && inputValue === "" ? [] : realOptions;
            if (!isAllFilteredSelected && inputValue !== "") {
              newSelectedItems = [.../* @__PURE__ */ new Set([...selectedItems, ...realOptions])];
            }
            if (isAllFilteredSelected && inputValue !== "") {
              const filteredSet = new Set(realOptions.map((item) => item.value));
              newSelectedItems = selectedItems.filter((item) => !filteredSet.has(item.value));
            }
            setSelectedItems(newSelectedItems);
          } else if (newSelectedItem && isOptionSelected(newSelectedItem)) {
            const itemToRemove = selectedItems.find((item) => item.value === newSelectedItem.value);
            if (itemToRemove) {
              removeSelectedItem(itemToRemove);
            }
          } else if (newSelectedItem) {
            addSelectedItem(newSelectedItem);
          }
          break;
        case useCombobox.stateChangeTypes.InputChange:
          setInputValue(newInputValue != null ? newInputValue : "");
          updateOptions(newInputValue != null ? newInputValue : "");
          break;
        default:
          break;
      }
    }
  });
  const { inputRef: containerRef, floatingRef, floatStyles, scrollRef } = useComboboxFloat(options, isOpen);
  const multiStyles = useStyles2(
    getMultiComboboxStyles,
    isOpen,
    invalid,
    disabled,
    width,
    minWidth,
    maxWidth,
    isClearable
  );
  const visibleItems = isOpen ? selectedItems.slice(0, MAX_SHOWN_ITEMS) : selectedItems.slice(0, shownItems);
  const { inputRef, inputWidth } = useMultiInputAutoSize(inputValue);
  return /* @__PURE__ */ jsxs("div", { className: multiStyles.container, ref: containerRef, children: [
    /* @__PURE__ */ jsxs("div", { className: cx(multiStyles.wrapper, { [multiStyles.disabled]: disabled }), ref: measureRef, children: [
      prefixIcon && /* @__PURE__ */ jsx(Box, { marginLeft: 0.5, children: /* @__PURE__ */ jsx(Text, { color: "secondary", children: /* @__PURE__ */ jsx(Icon, { name: prefixIcon }) }) }),
      /* @__PURE__ */ jsxs("span", { className: multiStyles.pillWrapper, children: [
        visibleItems.map((item, index) => /* @__PURE__ */ jsx(
          ValuePill,
          {
            disabled,
            onRemove: () => {
              removeSelectedItem(item);
            },
            ...getSelectedItemProps({ selectedItem: item, index }),
            children: itemToString(item)
          },
          `${item.value}${index}`
        )),
        selectedItems.length > visibleItems.length && /* @__PURE__ */ jsxs(Box, { display: "flex", direction: "row", marginLeft: 0.5, gap: 1, ref: counterMeasureRef, children: [
          /* @__PURE__ */ jsx(Text, { children: "..." }),
          /* @__PURE__ */ jsx(
            Tooltip,
            {
              interactive: true,
              content: /* @__PURE__ */ jsx(Fragment, { children: selectedItems.slice(visibleItems.length).map((item) => /* @__PURE__ */ jsx("div", { children: itemToString(item) }, item.value)) }),
              children: /* @__PURE__ */ jsx("div", { className: multiStyles.restNumber, children: selectedItems.length - shownItems })
            }
          )
        ] }),
        /* @__PURE__ */ jsx(
          "input",
          {
            className: multiStyles.input,
            ...getInputProps({
              ...getDropdownProps({
                disabled,
                preventKeyAction: isOpen,
                placeholder: visibleItems.length === 0 ? placeholder : "",
                ref: inputRef,
                style: { width: inputWidth }
              }),
              "aria-labelledby": ariaLabelledBy,
              // Label should be handled with the Field component
              "data-testid": dataTestId
            })
          }
        ),
        /* @__PURE__ */ jsxs("div", { className: multiStyles.suffix, ref: suffixMeasureRef, ...getToggleButtonProps(), children: [
          isClearable && selectedItems.length > 0 && /* @__PURE__ */ jsx(
            Icon,
            {
              name: "times",
              className: styles.clear,
              title: t("multicombobox.clear.title", "Clear all"),
              tabIndex: 0,
              role: "button",
              onClick: (e) => {
                e.stopPropagation();
                reset();
              },
              onKeyDown: (e) => {
                if (e.key === "Enter" || e.key === " ") {
                  reset();
                }
              }
            }
          ),
          /* @__PURE__ */ jsx(SuffixIcon, { isLoading: loading || false, isOpen })
        ] })
      ] })
    ] }),
    /* @__PURE__ */ jsx(Portal, { root: portalContainer, children: /* @__PURE__ */ jsx(
      "div",
      {
        className: cx(styles.menu, !isOpen && styles.menuClosed),
        style: {
          ...floatStyles,
          width: floatStyles.width + 24,
          // account for checkbox
          pointerEvents: "auto"
          // Override container's pointer-events: none
        },
        ...getMenuProps({ ref: floatingRef }),
        children: isOpen && /* @__PURE__ */ jsx(
          ComboboxList,
          {
            loading,
            options,
            highlightedIndex,
            selectedItems,
            scrollRef,
            getItemProps,
            enableAllOption,
            isMultiSelect: true,
            error: asyncError
          }
        )
      }
    ) })
  ] });
};
function getSelectedItemsFromValue(value, options) {
  if (isComboboxOptions(value)) {
    return value;
  }
  const valueMap = new Map(value.map((val, index) => [val, index]));
  const resultingItems = [];
  for (const option of options) {
    const index = valueMap.get(option.value);
    if (index !== void 0) {
      resultingItems[index] = option;
      valueMap.delete(option.value);
    }
    if (valueMap.size === 0) {
      break;
    }
  }
  for (const [val, index] of valueMap) {
    resultingItems[index] = { value: val };
  }
  return resultingItems;
}
function isComboboxOptions(value) {
  return typeof value[0] === "object";
}

export { MultiCombobox };
//# sourceMappingURL=MultiCombobox.mjs.map
