import React, { useState, useEffect, useMemo } from 'react';
import classNames from 'classnames';
import { uniqueId } from 'lodash-es';
import { Dropdown } from '../dropdown';
import DropdownMenuContent, { SEARCH_ID, getMenuTreeNavigationAdapter } from './dropdown-menu-content';
import DropdownTrigger from './dropdown-trigger';
import { ACCENT_TYPES } from '../button';
import { DROPDOWN_PLACEMENT_TYPES } from '../dropdown/types';
import { TOOLTIP_PLACEMENT_TYPES, TOOLTIP_DELAY_TYPES } from '../tooltip';
import { TreeNavigationProvider } from '../hooks/use-tree-navigation';
import { useReturnFocusOnClose } from '../hooks/use-return-focus-on-close';
import { useTranslation } from '../hooks/use-translation';
import { getFlattenedSubsectionOptions, getOptionsWithPrivateKey, getFooterOptionWithPrivateKey } from './utils';
import { DefaultLinkComponent } from './default-link-component';
import { jsx as _jsx } from "react/jsx-runtime";
const SEARCHABLE_FIELDS = {
  title: 'title',
  subtext: 'subtext',
  label: 'label' // for radios
};
const MIN_SEARCHABLE_ITEMS = 10;
let optionsId = 0;
function getSelectedItem(collection, selectedKey) {
  return collection.find(option => option.key === selectedKey);
}
function getFlattenedMultilevelOptions(collection) {
  return collection.flatMap(option => option.subItems ? option.subItems : option);
}
function getTotalNumberOfSearchableItems(options, footerOption, isSectioned) {
  function countSearchableItems(collection) {
    return collection.reduce((memo, option) => {
      const searchableFieldsCount = Object.values(SEARCHABLE_FIELDS).reduce((acc, value) => {
        if (option[value]) {
          return acc + 1;
        }
        return acc;
      }, 0);
      return memo + searchableFieldsCount + countSearchableItems(option.radioOptions || []);
    }, 0);
  }
  let numberOfSearchableItems = isSectioned ? countSearchableItems(getFlattenedSubsectionOptions(options)) : countSearchableItems(options);
  if (footerOption) {
    // footer option should only have title
    numberOfSearchableItems++;
  }
  return numberOfSearchableItems;
}
function matchBy(param, query) {
  return param?.toLowerCase().includes(query.trim().toLowerCase());
}
function filterBy(option, query) {
  // we should be filtering by visible items
  return option.isShown !== false && (matchBy(String(option[SEARCHABLE_FIELDS.title]), query) || matchBy(String(option[SEARCHABLE_FIELDS.subtext]), query) || matchBy(String(option[SEARCHABLE_FIELDS.label]), query));
}
function getFilteredOptions(options, query) {
  return options.reduce((memo, option) => {
    let filteredOption;
    if (option.subOptions) {
      const filteredSubOptions = getFilteredOptions(option.subOptions, query);
      filteredOption = filteredSubOptions.length && {
        ...option,
        subOptions: filteredSubOptions
      };
    } else if (option.radioOptions) {
      const filteredRadioOptions = option.radioOptions.filter(radioOption => filterBy(radioOption, query));
      filteredOption = filteredRadioOptions.length && {
        ...option,
        radioOptions: filteredRadioOptions
      };
    } else if (typeof option.title !== 'string' && option.searchableValue) {
      // title can be "node", in this case 'searchableValue' is being passed along with 'title'
      filteredOption = matchBy(option.searchableValue, query) && option;
    } else if (typeof option.title !== 'string' && !option.searchableValue && !option.radioOptions) {
      // eslint-disable-next-line no-console
      console.error('for search to work properly with React nodes, searchableValue must be provided');
    } else if (option.title || option.subtext) {
      // sectioned options also have titles
      // but we are only searching by their children
      filteredOption = filterBy(option, query) && option;
    }
    if (filteredOption) {
      memo.push(filteredOption);
    }
    return memo;
  }, []);
}
/** Powerful dropdown component aimed at supporting standard dropdown usages in the app
 * [CAPITALIZATION] Use title case for labels, no colon at the end
 * [CAPITALIZATION] Use sentence case for selection
 * @midnight-gray-supported
 * @alpine-light-supported
 */
const DropdownMenu = ({
  options: originalOptions,
  footerOption: originalFooterOption,
  triggerId = '',
  triggerTestId = '',
  triggerTourId,
  contentTestId = '',
  wrapperContentTestId = '',
  selectedItemTestId = '',
  defaultTriggerText = '',
  labelText = '',
  helperText = '',
  noResultsText,
  selectedKey,
  contentClassName = '',
  triggerClassName = '',
  triggerIsIcon = false,
  triggerTooltip = '',
  triggerAriaLabelText = '',
  triggerTooltipPlacement = TOOLTIP_PLACEMENT_TYPES.BOTTOM_END,
  triggerDynamicContentWrapperClassName,
  triggerDynamicContent,
  triggerAccentType = ACCENT_TYPES.COMMAND,
  triggerTooltipDelayType = TOOLTIP_DELAY_TYPES.SLOW,
  triggerOnClickHandler = () => {},
  triggerTooltipDocLink,
  triggerHideChevron = false,
  triggerTabIndex,
  triggerButtonSize,
  placement = DROPDOWN_PLACEMENT_TYPES.BOTTOM_END,
  selectbox,
  isSearchable = true,
  searchPlaceholderText,
  searchAutoComplete = 'off',
  isDisabled = false,
  hasErrored = false,
  hasSuccess = false,
  hasWarnings = false,
  isOptional = false,
  optionalLabelText,
  minSearchableItems = MIN_SEARCHABLE_ITEMS,
  onSelect = () => {},
  onSearch,
  setDefaultOptionsListOnClose = true,
  destroyOnScroll = false,
  isMultiLevel = false,
  isMultiLevelSectioned = false,
  required = false,
  onOpen,
  onClose,
  renderCustomDropdownHeader = () => null,
  searchQuery: searchQueryValue = '',
  dropdownProps = {},
  dropdownMenuContentProps = {},
  LinkComponent = DefaultLinkComponent
}) => {
  const {
    t
  } = useTranslation();
  const triggerUniqueId = useMemo(() => uniqueId('dropmenu-trigger-'), []);
  const [isOpen, setIsOpen] = useState(false);
  const [isKeyboardNavigationDisabled, setIsKeyboardNavigationDisabled] = useState(true);
  const triggerOnClickCallback = (_, isKeyEvent) => {
    if (isKeyEvent) {
      setIsKeyboardNavigationDisabled(false);
    } else {
      setIsKeyboardNavigationDisabled(true);
    }
    triggerOnClickHandler(_, isKeyEvent);
  };
  const dropdownContentId = useMemo(() => `dropmenu-container-${optionsId++}`, []);
  const triggerOptions = {
    defaultTriggerText,
    triggerClassName,
    triggerIsIcon,
    triggerDynamicContentWrapperClassName,
    triggerDynamicContent,
    triggerOnClickHandler: triggerOnClickCallback,
    triggerId: triggerId || triggerTestId || triggerUniqueId,
    triggerTestId,
    triggerTourId,
    triggerTooltip,
    triggerTooltipPlacement,
    triggerHideChevron,
    triggerTooltipDocLink,
    triggerAccentType,
    triggerTooltipDelayType,
    triggerAriaLabelText,
    labelText,
    selectedItemTestId,
    helperText,
    required,
    triggerTabIndex,
    dropdownContentId,
    triggerButtonSize
  };
  const options = useMemo(() => getOptionsWithPrivateKey(originalOptions), [originalOptions]);
  const footerOption = useMemo(() => originalFooterOption ? getFooterOptionWithPrivateKey(originalFooterOption) : null, [originalFooterOption]);
  const [searchQuery, setSearchQuery] = useState(searchQueryValue);
  const [renderedOptions, setRenderedOptions] = useState(options);
  const isSectioned = !!options.length && options.every(option => option.subOptions);
  const selectedItem = !isSectioned ? getSelectedItem(getFlattenedMultilevelOptions(options), selectedKey) : getSelectedItem(getFlattenedSubsectionOptions(options), selectedKey);
  const totalNumberOfSearchableItems = getTotalNumberOfSearchableItems(options, footerOption, isSectioned);

  // When options were updated with onSelect and shouldNotCloseDropdown was passed,
  // we need to update the rendered list. especially viable for checkbox and radio options
  useEffect(() => {
    // If using an API connected search, we don't want to cause an infinite loop
    // since a search already refreshes the list of options.
    if (!onSearch) {
      handleSearch();
    }
    if (onSearch) {
      setRenderedOptions(options);
    }
  }, [options]);
  useEffect(() => {
    setSearchQuery(searchQueryValue);
  }, [searchQueryValue]);
  function setDefaultOptionsList() {
    if (onSearch) {
      // clean query for onSearch if it was set and DropdownMenu was closed
      onSearch('');
      return;
    }
    setRenderedOptions(options);
  }
  function handleSearch(query) {
    if (onSearch) {
      onSearch(query);
      setSearchQuery(query ?? '');
    } else {
      if (!query || !query.length) {
        return setDefaultOptionsList();
      }
      const filteredOptions = getFilteredOptions(options, query);
      setRenderedOptions(filteredOptions);
    }
  }
  const showSearch = isSearchable && !isMultiLevel && !isMultiLevelSectioned && (totalNumberOfSearchableItems > minSearchableItems || !!onSearch);
  const showNoResults = !renderedOptions.length;
  const onSelectWrapper = (option, ...args) => {
    if (option?.privateKey) {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const {
        privateKey,
        ...omittedOption
      } = option;
      return onSelect(omittedOption, ...args);
    }
    return onSelect(option, ...args);
  };
  const fitWidthToRef = Boolean(selectbox) && (typeof selectbox !== 'boolean' ? selectbox?.fitWidthToSelector || Boolean(selectbox) : Boolean(selectbox));
  const adapter = useMemo(() => getMenuTreeNavigationAdapter({
    isExpandedByDefault: false
  }), []);
  const treeNavigationOptions = useMemo(() => {
    let navigatebleleOptions = !isSectioned ? renderedOptions : getFlattenedSubsectionOptions(renderedOptions);
    const result = navigatebleleOptions.reduce((prev, curr) => {
      let toAdd = [curr];
      if (curr.radioOptions) {
        // add DropdownMenuOption for each radioOption for keyboard navigation
        toAdd = [];
        curr.radioOptions.forEach(ropt => {
          toAdd.push({
            ...ropt,
            groupIdentifier: curr.groupIdentifier,
            activeItemId: curr.activeItemId,
            disabled: curr.disabled
          });
        });
      }
      return [...prev, ...toAdd];
    }, []);
    navigatebleleOptions = result;
    let treeOptions = footerOption ? [...navigatebleleOptions, footerOption] : navigatebleleOptions;
    if (showSearch) {
      const searchNode = {
        id: SEARCH_ID,
        children: [],
        parent: null,
        parents: [],
        label: '',
        isDisabled: false,
        isExpanded: false,
        isExpandable: false,
        isFocused: true,
        isLeaf: true
      };
      treeOptions = [searchNode, ...treeOptions];
    }
    return treeOptions;
  }, [showSearch, isSectioned, renderedOptions, footerOption, isOpen]);

  // last focused element is not limited to only trigger, it can also be a table cell
  useReturnFocusOnClose({
    isShown: isOpen
  });
  return /*#__PURE__*/_jsx(TreeNavigationProvider, {
    adapter: adapter,
    tree: treeNavigationOptions,
    isDisabled: isKeyboardNavigationDisabled,
    children: /*#__PURE__*/_jsx(Dropdown, {
      ...dropdownProps,
      placement: placement,
      fitWidthToRef: fitWidthToRef,
      destroyOnScroll: destroyOnScroll,
      isDisabled: isDisabled,
      hasCustomFocusHandling: true,
      onClose: () => {
        if (setDefaultOptionsListOnClose) {
          setDefaultOptionsList();
        }
        setIsKeyboardNavigationDisabled(true);
        onClose?.();
      },
      contentClassName: classNames(contentClassName, {
        'selectbox-dropdown-content': Boolean(selectbox)
      }),
      onOpen: openedWithKeyboard => {
        setIsOpen(openedWithKeyboard);
        onOpen?.(openedWithKeyboard);
      },
      testId: wrapperContentTestId,
      id: dropdownContentId,
      renderAnchor: ({
        ref,
        onClick,
        isOpen: open
      }) => /*#__PURE__*/_jsx(DropdownTrigger, {
        ref: ref,
        onClick: onClick,
        isOpen: open,
        isDisabled: isDisabled,
        hasErrored: hasErrored,
        hasSuccess: hasSuccess,
        hasWarnings: hasWarnings,
        selectedItem: selectedItem,
        options: options,
        selectbox: selectbox,
        triggerOptions: triggerOptions,
        isSectioned: isSectioned,
        isOptional: isOptional,
        optionalLabelText: optionalLabelText ?? t('Optional')
      }),
      children: ({
        closeDropdown
      }) => /*#__PURE__*/_jsx(DropdownMenuContent, {
        ...dropdownMenuContentProps,
        options: renderedOptions,
        footerOption: footerOption,
        onSelect: onSelectWrapper,
        isSectioned: isSectioned,
        isMultiLevel: isMultiLevel,
        isMultiLevelSectioned: isMultiLevelSectioned,
        closeDropdown: (...args) => {
          closeDropdown(...args);
          setIsOpen(false);
        },
        searchId: SEARCH_ID,
        handleSearch: handleSearch,
        showSearch: showSearch,
        searchPlaceholderText: searchPlaceholderText ?? t('Search'),
        searchAutoComplete: searchAutoComplete,
        searchQuery: searchQuery,
        showNoResults: showNoResults,
        noResultsText: noResultsText ?? t('No search results found.'),
        selectedKey: selectedKey,
        selectedItem: selectedItem,
        contentTestId: contentTestId,
        renderCustomHeader: renderCustomDropdownHeader,
        labelledBy: triggerOptions.triggerId,
        LinkComponent: LinkComponent
      })
    })
  });
};
export default DropdownMenu;