import _styled from "styled-components";
import React, { useCallback, useState, useMemo, useEffect, useRef } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
import { handleKeyboardMovement } from "./HandleKeyboard";
import { StyledAutocompleteBody, StyledAutocompleteNewItem, StyledAutocompleteScrollShadow } from "./Styles";
import highlightText from "../../hooks/useHighlightText";
import { Option } from "../Select/Option";
import { StyledOption } from "../Select/Option/Styles";
import { OptionGroup } from "../Select/OptionGroup";
export function isOptionGroup(item) {
  return item.options !== undefined;
}
export const Autocomplete = _ref => {
  let {
    type,
    options = [],
    inputEl,
    selected = [],
    emptyValue,
    noResultText,
    renderOption = option => option == null ? void 0 : option.name,
    defaultValue,
    sortDirection = "asc",
    handleChange,
    optionClassName,
    handleEmptyAction,
    disabledInternalSort,
    AutocompleteClassName,
    handleDefaultOptionChange,
    preselectDefaultValue,
    keepSameOptionsOrder = false,
    autoHeightMax = 340,
    clearInputOnSelect,
    mixedOptions = [],
    filterCriteria,
    disableVirtualization
  } = _ref;
  const listContainerRef = useRef(null);
  const itemRef = useRef(null);
  const selectedOptions = useMemo(() => {
    if (Array.isArray(selected)) {
      return selected;
    }
    return [selected];
  }, [selected]);
  const handleSort = useCallback(opts => {
    var _opts$;
    if (keepSameOptionsOrder) {
      return opts;
    }
    const isGrouped = Array.isArray((_opts$ = opts[0]) == null ? void 0 : _opts$.options);
    const sortOptions = (a, b) => {
      const aSelected = selectedOptions.includes(a.id);
      const bSelected = selectedOptions.includes(b.id);
      const aMixed = mixedOptions.includes(a.id);
      const bMixed = mixedOptions.includes(b.id);
      if (aSelected && !bSelected) {
        return -1;
      }
      if (!aSelected && bSelected) {
        return 1;
      }
      if (aMixed && !bMixed) {
        return -1;
      }
      if (!aMixed && bMixed) {
        return 1;
      }
      return 0;
    };
    if (isGrouped) {
      return opts.map(group => {
        const sortedGroupOptions = group.options.sort(sortOptions);
        return {
          ...group,
          options: sortedGroupOptions
        };
      });
    } else {
      return opts.sort(sortOptions);
    }
  },
  // eslint-disable-next-line react-hooks/exhaustive-deps
  [mixedOptions, selectedOptions]);
  const sortList = useCallback(options => {
    var _options$;
    if (disabledInternalSort) {
      return handleSort([...options]);
    }
    const isGrouped = Array.isArray((_options$ = options[0]) == null ? void 0 : _options$.options);
    if (isGrouped) {
      const sortedOptions = options.map(group => {
        const sortedGroupOptions = group.options.sort((a, b) => sortDirection === "asc" ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name));
        return {
          ...group,
          options: handleSort(sortedGroupOptions)
        };
      });
      return handleSort(sortedOptions);
    } else {
      const sortedOptions = options.sort((a, b) => sortDirection === "asc" ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name));
      return handleSort(sortedOptions);
    }
  }, [sortDirection, disabledInternalSort, handleSort]);
  const [sortedList, setSortedList] = useState(() => sortList(options));
  useEffect(() => {
    setSortedList(sortList(options));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [options]);
  const [hover, setHover] = useState({
    item: undefined,
    by: undefined
  });
  const [filter, setFilter] = useState("");
  const handleEmpty = useCallback(e => {
    if (e && e.button !== 0) {
      return;
    }
    if (handleEmptyAction) {
      var _inputEl$current;
      inputEl == null || (_inputEl$current = inputEl.current) == null || _inputEl$current.focus();
      setFilter("");
      handleEmptyAction(filter);
    }
  }, [filter, handleEmptyAction, inputEl]);
  const onAddNewMouseEnter = useCallback(() => setHover({
    item: "addNew",
    by: "mouse"
  }), []);
  const showAddNew = useMemo(() => {
    return !!(emptyValue && filter.trim() && options.every(option => {
      if (isOptionGroup(option)) {
        return option.options.every(v => v.name.toLowerCase() !== filter.trim().toLowerCase());
      }
      return option.name.toLowerCase() !== filter.trim().toLowerCase();
    }));
  }, [emptyValue, filter, options]);
  const filterOptions = useCallback((options, filter) => {
    const trimmedFilter = filter.trim();
    const isGrouped = options[0] && isOptionGroup(options[0]);
    const matchesFilter = option => {
      const nameMatch = option.name.toLowerCase().includes(trimmedFilter.toLowerCase());
      if (filterCriteria && trimmedFilter) {
        return nameMatch || filterCriteria(option, trimmedFilter);
      }
      return nameMatch;
    };
    if (isGrouped) {
      let hovered = false;
      return options.reduce((acc, groupedOption) => {
        const filteredOptions = groupedOption.options.filter(matchesFilter);
        if (filteredOptions.length > 0) {
          if (!hovered && filter) {
            setHover({
              item: filteredOptions[0].id,
              by: "keyboard"
            });
          }
          hovered = true;
          return [...acc, {
            ...groupedOption,
            options: filteredOptions
          }];
        }
        return [...acc];
      }, []);
    } else {
      const filteredOptions = options.filter(matchesFilter);
      if (filter && filteredOptions.length > 0) {
        setHover({
          item: filteredOptions[0].id,
          by: "keyboard"
        });
      }
      if (filteredOptions.length === 0 && emptyValue) {
        setHover({
          item: "addNew",
          by: "keyboard"
        });
      }
      return filteredOptions;
    }
  }, [emptyValue, filterCriteria]);
  const list = useMemo(() => filterOptions(sortedList, filter), [filter, filterOptions, sortedList]);
  const showDefaultOption = useMemo(() => !!defaultValue && !filter, [defaultValue, filter]);
  const flatOptions = useMemo(() => {
    const options = list.reduce((acc, option) => {
      if (!isOptionGroup(option)) {
        return [...acc, option];
      }
      return [...acc, ...option.options];
    }, []);
    return filterOptions(options, filter);
  }, [filter, filterOptions, list]);
  const showNoResultCondition = useMemo(() => noResultText && !showAddNew && list.length < 1 && (defaultValue && filter || !defaultValue), [noResultText, showAddNew, list.length, defaultValue, filter]);
  const virtualItems = useMemo(() => {
    const items = [];
    if (showDefaultOption) {
      items.push({
        type: "default",
        id: "default"
      });
    }
    list.forEach((item, index) => {
      if (isOptionGroup(item)) {
        items.push({
          type: "group",
          id: item.id,
          item
        });
        item.options.forEach(option => {
          items.push({
            type: "option",
            id: option.id,
            item: option,
            index
          });
        });
      } else {
        items.push({
          type: "option",
          id: item.id,
          item,
          index
        });
      }
    });
    if (showNoResultCondition) {
      items.push({
        type: "noResult",
        id: "noResult"
      });
    }
    if (showAddNew) {
      items.push({
        type: "addNew",
        id: "addNew"
      });
    }
    return items;
  }, [list, showDefaultOption, showNoResultCondition, showAddNew]);
  const shouldUseVirtualization = !disableVirtualization && virtualItems.length > 40;
  const rowVirtualizer = useVirtualizer({
    count: virtualItems.length,
    getScrollElement: () => listContainerRef.current,
    estimateSize: () => 28,
    overscan: 5,
    gap: 4
  });
  const handleInputChange = useCallback(e => {
    if (e.target && !(e.key === "ArrowDown") && !(e.key === "ArrowUp") && !(e.key === "Enter")) {
      setFilter(e.target.value);
    }
  }, []);
  const handleHoverCallback = useCallback(e => {
    setHover({
      item: e,
      by: "mouse"
    });
  }, []);
  const toggleSelected = useCallback(id => {
    let result;
    if (id !== null) {
      if (type === "multiple") {
        if (selectedOptions.includes(id)) {
          result = selectedOptions.filter(_id => _id !== id);
        } else {
          result = [...selectedOptions, id];
        }
      } else {
        result = id;
      }
      if (clearInputOnSelect && inputEl != null && inputEl.current) {
        inputEl.current.value = "";
        inputEl.current.dispatchEvent(new Event("change", {
          bubbles: true
        }));
      }
      setFilter("");
    } else {
      if (typeof handleDefaultOptionChange === "function") {
        handleDefaultOptionChange();
        return;
      }
    }
    if (typeof handleChange === "function") {
      handleChange(result);
    }
  }, [clearInputOnSelect, handleChange, handleDefaultOptionChange, inputEl, selectedOptions, type]);
  const handleMouseEnter = useCallback(e => {
    if (e === undefined || e === null) {
      return setHover({
        item: null,
        by: "mouse"
      });
    }
    setHover({
      item: e,
      by: "mouse"
    });
  }, []);
  const handleClick = useCallback(e => {
    e.preventDefault();
    toggleSelected(hover.item);
  }, [toggleSelected, hover]);
  const handleRenderOption = useCallback((item, index) => {
    if (isOptionGroup(item)) {
      return /*#__PURE__*/React.createElement(OptionGroup, {
        checked: selectedOptions,
        name: item.name,
        tooltip: item.tooltip,
        key: item.id,
        setHover: handleHoverCallback,
        id: item.id,
        hover: hover.item,
        options: item.options,
        renderOptions: handleRenderOption,
        type: type,
        onChange: handleChange,
        filter: filter,
        mixedOptions: mixedOptions
      });
    }
    return /*#__PURE__*/React.createElement(Option, {
      name: item.name,
      ref: hover.item === item.id ? itemRef : null,
      key: index,
      tooltip: item.tooltip,
      onMouseEnter: handleMouseEnter,
      onClick: handleClick,
      id: item.id,
      hover: item.id === hover.item,
      className: optionClassName,
      renderOption: renderOption({
        ...item,
        name: highlightText(item.name, filter)
      }, {
        id: "option_" + item.id,
        checked: selectedOptions && selectedOptions.includes(item.id),
        hover: hover.item === item.id,
        onChange: () => null
      })
    });
  }, [handleClick, handleMouseEnter, hover.item, optionClassName, renderOption, filter, selectedOptions, handleHoverCallback, type, handleChange, mixedOptions]);
  const handleOnMouseLeave = useCallback(() => {
    setHover({
      item: undefined,
      by: "mouse"
    });
  }, []);
  useEffect(() => {
    if (hover.by === "keyboard" && hover.item !== undefined) {
      const index = virtualItems.findIndex(item => item.id === hover.item);
      if (index !== -1) {
        rowVirtualizer.scrollToIndex(index, {
          align: "center"
        });
      }
    }
  }, [hover, rowVirtualizer, virtualItems]);
  const handleOnKeyDown = useCallback(e => {
    if (e.key === "Enter") {
      e.preventDefault();
      if (hover.item === undefined && filter === "") {
        return;
      }
      if (hover.item === "addNew") {
        handleEmpty(null);
        return;
      }
      if (hover.item === null && handleDefaultOptionChange) {
        handleDefaultOptionChange();
        return;
      }
      if (typeof hover.item !== "undefined" || hover.item !== null) {
        toggleSelected(hover.item);
        setFilter("");
      }
      return;
    }
    setHover({
      item: handleKeyboardMovement(e, hover.item, flatOptions, showAddNew, showDefaultOption),
      by: "keyboard"
    });
  }, [filter, flatOptions, toggleSelected, handleDefaultOptionChange, handleEmpty, hover, showAddNew, showDefaultOption]);
  useEffect(() => {
    var _inputEl$current2;
    const listenerTarget = (_inputEl$current2 = inputEl == null ? void 0 : inputEl.current) != null ? _inputEl$current2 : document;
    listenerTarget.addEventListener("input", handleInputChange);
    listenerTarget.addEventListener("keydown", handleOnKeyDown);
    return () => {
      listenerTarget.removeEventListener("input", handleInputChange);
      listenerTarget.removeEventListener("keydown", handleOnKeyDown);
    };
  }, [handleInputChange, handleOnKeyDown, inputEl]);
  const isDefaultOptionSelected = useCallback(() => {
    if (preselectDefaultValue) {
      return selectedOptions.length < 1 || selectedOptions[0] === "";
    } else {
      return selectedOptions[0] === null || selectedOptions.length === flatOptions.length;
    }
  }, [flatOptions.length, preselectDefaultValue, selectedOptions]);
  const renderVirtualItem = useCallback((virtualItem, index) => {
    switch (virtualItem.type) {
      case "default":
        return /*#__PURE__*/React.createElement(Option, {
          name: String(defaultValue),
          ref: hover.item === null ? itemRef : null,
          hover: hover.item === null,
          onMouseEnter: handleMouseEnter,
          onClick: e => {
            e.preventDefault();
            toggleSelected(null);
          },
          renderOption: renderOption({
            name: defaultValue,
            id: null
          }, {
            checked: isDefaultOptionSelected(),
            hover: hover.item === null,
            onChange: () => null
          })
        });
      case "group":
        if (!virtualItem.item || !isOptionGroup(virtualItem.item)) return null;
        return /*#__PURE__*/React.createElement(OptionGroup, {
          checked: selectedOptions,
          name: virtualItem.item.name,
          tooltip: virtualItem.item.tooltip,
          key: virtualItem.item.id,
          setHover: handleHoverCallback,
          id: virtualItem.item.id,
          hover: hover.item,
          options: virtualItem.item.options,
          renderOptions: handleRenderOption,
          type: type,
          onChange: handleChange,
          filter: filter,
          mixedOptions: mixedOptions
        });
      case "option":
        if (!virtualItem.item || isOptionGroup(virtualItem.item)) return null;
        return /*#__PURE__*/React.createElement(Option, {
          name: virtualItem.item.name,
          ref: hover.item === virtualItem.id ? itemRef : null,
          key: index,
          tooltip: virtualItem.item.tooltip,
          onMouseEnter: () => handleMouseEnter(virtualItem.id),
          onClick: handleClick,
          id: virtualItem.id,
          hover: virtualItem.id === hover.item,
          className: optionClassName,
          renderOption: renderOption({
            ...virtualItem.item,
            name: highlightText(virtualItem.item.name, filter)
          }, {
            id: "option_" + virtualItem.id,
            checked: selectedOptions && selectedOptions.includes(virtualItem.id),
            hover: hover.item === virtualItem.id,
            onChange: () => null
          })
        });
      case "noResult":
        return /*#__PURE__*/React.createElement(_StyledStyledOption, null, noResultText);
      case "addNew":
        return /*#__PURE__*/React.createElement(StyledAutocompleteNewItem, {
          ref: hover.item === "addNew" ? itemRef : null,
          key: "emptyValue",
          hover: hover.item === "addNew",
          onMouseDown: handleEmpty,
          onMouseEnter: onAddNewMouseEnter
        }, emptyValue);
      default:
        return null;
    }
  }, [defaultValue, hover.item, handleMouseEnter, renderOption, isDefaultOptionSelected, selectedOptions, handleHoverCallback, handleRenderOption, type, handleChange, filter, mixedOptions, handleClick, optionClassName, noResultText, handleEmpty, onAddNewMouseEnter, emptyValue, toggleSelected]);
  return /*#__PURE__*/React.createElement(StyledAutocompleteScrollShadow, {
    className: AutocompleteClassName,
    $isHidden: !defaultValue && !emptyValue && !noResultText && list.length < 1
  }, _ref2 => {
    let {
      onScroll
    } = _ref2;
    return /*#__PURE__*/React.createElement(StyledAutocompleteBody, {
      key: "body",
      onMouseLeave: handleOnMouseLeave
    }, /*#__PURE__*/React.createElement("div", {
      ref: listContainerRef,
      style: {
        height: Math.min(rowVirtualizer.getTotalSize() + 12, autoHeightMax) + "px",
        overflow: "auto"
      },
      onScroll: e => {
        if (onScroll) onScroll(e);
      }
    }, /*#__PURE__*/React.createElement("div", {
      style: {
        height: rowVirtualizer.getTotalSize() + "px",
        width: "100%",
        position: "relative"
      }
    }, shouldUseVirtualization ? rowVirtualizer.getVirtualItems().map(virtualRow => {
      const item = virtualItems[virtualRow.index];
      return /*#__PURE__*/React.createElement("div", {
        key: virtualRow.index,
        "data-index": virtualRow.index,
        style: {
          position: "absolute",
          top: 0,
          left: 0,
          width: "100%",
          transform: "translateY(" + virtualRow.start + "px)"
        }
      }, renderVirtualItem(item, virtualRow.index));
    }) : virtualItems.map((item, index) => /*#__PURE__*/React.createElement(React.Fragment, {
      key: index
    }, renderVirtualItem(item, index))))));
  });
};
Autocomplete.displayName = "Autocomplete";
var _StyledStyledOption = _styled(StyledOption).withConfig({
  displayName: "Autocomplete___StyledStyledOption",
  componentId: "sc-9x4q7e-0"
})(["cursor:auto"]);
//# sourceMappingURL=Autocomplete.js.map