import mergeProps from 'merge-props';
import { useState, useRef, useEffect, useDeferredValue, useCallback } from 'react';
import { Listbox } from '@headlessui/react';
import { Breakpoint } from '@transferwise/neptune-tokens';
import { useScreenSize } from '../../common/hooks/useScreenSize.mjs';
import { useEffectEvent } from '../../common/hooks/useEffectEvent.mjs';
import { useInputAttributes } from '../contexts.mjs';
import { SelectInputBottomSheet } from './BottomSheet/SelectInputBottomSheet.mjs';
import { SelectInputPopover } from './Popover/SelectInputPopover.mjs';
import { SelectInputOptions } from './Options/SelectInputOptions.mjs';
import { jsx } from 'react/jsx-runtime';
import { DefaultRenderTrigger } from './DefaultRenderTrigger/SelectInputDefaultRenderTrigger.mjs';
import { SelectInputTriggerButtonPropsContext, SelectInputOptionContentWithinTriggerContext } from './SelectInput.contexts.mjs';
import { searchableString, sortByRelevance } from './SelectInput.utils.mjs';

const noop = () => {};
/**
 * SelectInput component allows users to select an option from a dropdown list.
 * Supports filtering, multiple selection, and customization.
 */
function SelectInput({
  id: idProp,
  parentId,
  name,
  multiple,
  placeholder,
  autocomplete,
  items,
  defaultValue,
  value: controlledValue,
  compareValues,
  renderValue = String,
  renderFooter,
  renderTrigger = DefaultRenderTrigger,
  filterable,
  filterPlaceholder,
  sortFilteredOptions,
  disabled,
  size = 'md',
  className,
  UNSAFE_triggerButtonProps,
  triggerRef: externalTriggerRef,
  onFilterChange = noop,
  onChange,
  onOpen,
  onClose,
  onClear
}) {
  const inputAttributes = useInputAttributes({
    nonLabelable: true
  });
  const id = idProp ?? inputAttributes.id;
  const [open, setOpen] = useState(false);
  const initialized = useRef(false);
  const handleClose = useEffectEvent(onClose ?? (() => {}));
  const handleOpen = useEffectEvent(onOpen ?? (() => {}));
  useEffect(() => {
    if (initialized.current) {
      if (open) {
        handleOpen?.();
      } else {
        handleClose?.();
      }
    } else {
      initialized.current = true;
    }
  }, [open]);
  const [filterQuery, _setFilterQuery] = useState('');
  const deferredFilterQuery = useDeferredValue(filterQuery);
  const previousFilterQueryRef = useRef(filterQuery);
  const setFilterQuery = useCallback(query => {
    _setFilterQuery(query);
    if (query !== previousFilterQueryRef.current) {
      onFilterChange({
        query,
        queryNormalized: query ? searchableString(query) : null
      });
      previousFilterQueryRef.current = query;
    }
  }, [onFilterChange]);
  const internalTriggerRef = useRef(null);
  const searchInputRef = useRef(null);
  const listboxRef = useRef(null);
  const controllerRef = filterable ? searchInputRef : listboxRef;
  const screenSm = useScreenSize(Breakpoint.SMALL);
  const OptionsOverlay = screenSm ? SelectInputPopover : SelectInputBottomSheet;
  /**
   * Attempts to resolve the `listbox` label
   * @see https://storybook.wise.design/?path=/docs/forms-selectinput-accessibility--docs#labelling
   */
  const getListBoxLabelProps = () => {
    if (UNSAFE_triggerButtonProps?.['aria-label']) {
      return {
        listBoxLabel: UNSAFE_triggerButtonProps['aria-label']
      };
    }
    if (UNSAFE_triggerButtonProps?.['aria-labelledby']) {
      return {
        listBoxLabelledBy: UNSAFE_triggerButtonProps['aria-labelledby']
      };
    }
    if (inputAttributes['aria-labelledby']) {
      return {
        listBoxLabelledBy: inputAttributes['aria-labelledby']
      };
    }
    return {};
  };
  return /*#__PURE__*/jsx(Listbox, {
    name: name,
    multiple: multiple,
    defaultValue: defaultValue,
    value: controlledValue,
    by: compareValues,
    disabled: disabled,
    onChange: value => {
      if (!multiple) {
        setOpen(false);
      }
      onChange?.(value);
    },
    children: ({
      disabled: uiDisabled,
      value
    }) => {
      const placeholderShown = multiple && Array.isArray(value) ? value.length === 0 : value == null;
      return /*#__PURE__*/jsx(OptionsOverlay, {
        placement: "bottom-start",
        open: open,
        renderTrigger: ({
          ref,
          getInteractionProps
        }) => /*#__PURE__*/jsx(SelectInputTriggerButtonPropsContext.Provider, {
          // eslint-disable-next-line react/jsx-no-constructed-context-values
          value: {
            ref: node => {
              ref(node);
              if (externalTriggerRef) {
                // eslint-disable-next-line no-param-reassign
                externalTriggerRef.current = node;
              } else {
                internalTriggerRef.current = node;
              }
            },
            size,
            ...inputAttributes,
            ...UNSAFE_triggerButtonProps,
            id,
            ...mergeProps({
              onClick: () => {
                setOpen(prev => !prev);
              },
              onKeyDown: event => {
                if (event.key === ' ' || event.key === 'Enter' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {
                  setOpen(prev => !prev);
                }
              }
            }, getInteractionProps())
          },
          children: renderTrigger({
            content: !placeholderShown ? /*#__PURE__*/jsx(SelectInputOptionContentWithinTriggerContext.Provider, {
              value: true,
              children: multiple && Array.isArray(value) ? value.map(option => renderValue(option, true)).filter(node => node != null).join(', ') : renderValue(value, true)
            }) : placeholder,
            placeholderShown,
            clear: onClear != null ? () => {
              onClear();
              (externalTriggerRef?.current ?? internalTriggerRef.current)?.focus({
                preventScroll: true
              });
            } : undefined,
            disabled: uiDisabled,
            size,
            className
          })
        }),
        initialFocusRef: controllerRef,
        size: filterable ? 'lg' : 'md',
        padding: "none",
        onClose: () => {
          setOpen(false);
        },
        onCloseEnd: () => {
          setFilterQuery('');
        },
        children: /*#__PURE__*/jsx(SelectInputOptions, {
          id: id ? `${id}Search` : undefined,
          parentId: parentId,
          items: items,
          compareValues: compareValues,
          renderValue: renderValue,
          renderFooter: renderFooter,
          filterable: filterable,
          filterPlaceholder: filterPlaceholder,
          sortFilteredOptions: sortFilteredOptions,
          searchInputRef: searchInputRef,
          listboxRef: listboxRef,
          filterQuery: deferredFilterQuery,
          autocomplete: autocomplete,
          name: name,
          onFilterChange: setFilterQuery,
          onAutocompleteSelect: matchedValue => {
            onChange?.(matchedValue);
            if (!multiple) {
              setOpen(false);
            }
          },
          ...getListBoxLabelProps()
        })
      });
    }
  });
}
// Attach sortByRelevance to the component for convenience
SelectInput.sortByRelevance = sortByRelevance;

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