import * as React from 'react';
import { useClick, useDismiss, useFloatingRootContext, useInteractions, useListNavigation, useRole, useTypeahead } from '@floating-ui/react';
import { useFieldControlValidation } from '../../field/control/useFieldControlValidation.js';
import { useFieldRootContext } from '../../field/root/FieldRootContext.js';
import { useBaseUiId } from '../../utils/useBaseUiId.js';
import { useControlled } from '../../utils/useControlled.js';
import { useTransitionStatus } from '../../utils/index.js';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect.js';
import { useEventCallback } from '../../utils/useEventCallback.js';
import { warn } from '../../utils/warn.js';
import { useOpenChangeComplete } from '../../utils/useOpenChangeComplete.js';
const EMPTY_ARRAY = [];
export function useSelectRoot(params) {
  const {
    id: idProp,
    disabled = false,
    readOnly = false,
    required = false,
    alignItemToTrigger: alignItemToTriggerParam = true,
    modal = false,
    onOpenChangeComplete
  } = params;
  const {
    setDirty,
    validityData,
    validationMode,
    setControlId,
    setFilled
  } = useFieldRootContext();
  const fieldControlValidation = useFieldControlValidation();
  const id = useBaseUiId(idProp);
  useEnhancedEffect(() => {
    setControlId(id);
    return () => {
      setControlId(undefined);
    };
  }, [id, setControlId]);
  const [value, setValueUnwrapped] = useControlled({
    controlled: params.value,
    default: params.defaultValue,
    name: 'Select',
    state: 'value'
  });
  const [open, setOpenUnwrapped] = useControlled({
    controlled: params.open,
    default: params.defaultOpen,
    name: 'Select',
    state: 'open'
  });
  useEnhancedEffect(() => {
    setFilled(value !== null);
  }, [setFilled, value]);
  const [controlledAlignItemToTrigger, setControlledAlignItemToTrigger] = React.useState(alignItemToTriggerParam);
  const listRef = React.useRef([]);
  const labelsRef = React.useRef([]);
  const popupRef = React.useRef(null);
  const valueRef = React.useRef(null);
  const valuesRef = React.useRef([]);
  const typingRef = React.useRef(false);
  const keyboardActiveRef = React.useRef(false);
  const selectedItemTextRef = React.useRef(null);
  const selectionRef = React.useRef({
    allowSelectedMouseUp: false,
    allowUnselectedMouseUp: false,
    allowSelect: false
  });
  const [triggerElement, setTriggerElement] = React.useState(null);
  const [positionerElement, setPositionerElement] = React.useState(null);
  const [activeIndex, setActiveIndex] = React.useState(null);
  const [selectedIndex, setSelectedIndex] = React.useState(null);
  const [label, setLabel] = React.useState('');
  const [touchModality, setTouchModality] = React.useState(false);
  const [scrollUpArrowVisible, setScrollUpArrowVisible] = React.useState(false);
  const [scrollDownArrowVisible, setScrollDownArrowVisible] = React.useState(false);
  const {
    mounted,
    setMounted,
    transitionStatus
  } = useTransitionStatus(open);
  const alignItemToTrigger = Boolean(mounted && controlledAlignItemToTrigger && !touchModality);
  if (!mounted && controlledAlignItemToTrigger !== alignItemToTriggerParam) {
    setControlledAlignItemToTrigger(alignItemToTriggerParam);
  }
  if (!alignItemToTriggerParam || !mounted) {
    if (scrollUpArrowVisible) {
      setScrollUpArrowVisible(false);
    }
    if (scrollDownArrowVisible) {
      setScrollDownArrowVisible(false);
    }
  }
  const setOpen = useEventCallback((nextOpen, event) => {
    params.onOpenChange?.(nextOpen, event);
    setOpenUnwrapped(nextOpen);

    // Workaround `enableFocusInside` in Floating UI setting `tabindex=0` of a non-highlighted
    // option upon close when tabbing out due to `keepMounted=true`:
    // https://github.com/floating-ui/floating-ui/pull/3004/files#diff-962a7439cdeb09ea98d4b622a45d517bce07ad8c3f866e089bda05f4b0bbd875R194-R199
    // This otherwise causes options to retain `tabindex=0` incorrectly when the popup is closed
    // when tabbing outside.
    if (!nextOpen && activeIndex !== null) {
      const activeOption = listRef.current[activeIndex];
      // Wait for Floating UI's focus effect to have fired
      queueMicrotask(() => {
        activeOption?.setAttribute('tabindex', '-1');
      });
    }
  });
  const handleUnmount = useEventCallback(() => {
    setMounted(false);
    setActiveIndex(null);
    onOpenChangeComplete?.(false);
  });
  useOpenChangeComplete({
    enabled: !params.actionsRef,
    open,
    ref: popupRef,
    onComplete() {
      if (!open) {
        handleUnmount();
      }
    }
  });
  React.useImperativeHandle(params.actionsRef, () => ({
    unmount: handleUnmount
  }), [handleUnmount]);
  const setValue = useEventCallback((nextValue, event) => {
    params.onValueChange?.(nextValue, event);
    setValueUnwrapped(nextValue);
    setDirty(nextValue !== validityData.initialValue);
    if (validationMode === 'onChange') {
      fieldControlValidation.commitValidation(nextValue);
    }
    const index = valuesRef.current.indexOf(nextValue);
    setSelectedIndex(index);
    setLabel(labelsRef.current[index] ?? '');
  });
  const hasRegisteredRef = React.useRef(false);
  const registerSelectedItem = useEventCallback(suppliedIndex => {
    if (suppliedIndex !== undefined) {
      hasRegisteredRef.current = true;
    }
    const stringValue = typeof value === 'string' || value === null ? value : JSON.stringify(value);
    const index = suppliedIndex ?? valuesRef.current.indexOf(stringValue);
    const hasIndex = index !== -1;
    if (hasIndex || value === null) {
      if (hasIndex) {
        setSelectedIndex(index);
      }
      setLabel(hasIndex ? labelsRef.current[index] ?? '' : '');
    } else if (value) {
      warn(`The value \`${stringValue}\` is not present in the select items.`);
    }
  });
  useEnhancedEffect(() => {
    if (!hasRegisteredRef.current) {
      return;
    }
    registerSelectedItem(undefined);
  }, [value, registerSelectedItem]);
  const floatingRootContext = useFloatingRootContext({
    open,
    onOpenChange: setOpen,
    elements: {
      reference: triggerElement,
      floating: positionerElement
    }
  });
  const click = useClick(floatingRootContext, {
    enabled: !readOnly && !disabled,
    event: 'mousedown'
  });
  const dismiss = useDismiss(floatingRootContext, {
    bubbles: false,
    outsidePressEvent: 'mousedown'
  });
  const role = useRole(floatingRootContext, {
    role: 'select'
  });
  const listNavigation = useListNavigation(floatingRootContext, {
    enabled: !readOnly && !disabled,
    listRef,
    activeIndex,
    selectedIndex,
    disabledIndices: EMPTY_ARRAY,
    onNavigate(nextActiveIndex) {
      // Retain the highlight while transitioning out.
      if (nextActiveIndex === null && !open) {
        return;
      }
      setActiveIndex(nextActiveIndex);
    },
    // Implement our own listeners since `onPointerLeave` on each option fires while scrolling with
    // the `alignItemToTrigger` prop enabled, causing a performance issue on Chrome.
    focusItemOnHover: false
  });
  const typeahead = useTypeahead(floatingRootContext, {
    enabled: !readOnly && !disabled,
    listRef: labelsRef,
    activeIndex,
    selectedIndex,
    onMatch(index) {
      if (open) {
        setActiveIndex(index);
      } else {
        setValue(valuesRef.current[index]);
      }
    },
    onTypingChange(typing) {
      // FIXME: Floating UI doesn't support allowing space to select an item while the popup is
      // closed and the trigger isn't a native <button>.
      typingRef.current = typing;
    }
  });
  const {
    getReferenceProps: getRootTriggerProps,
    getFloatingProps: getRootPositionerProps,
    getItemProps
  } = useInteractions([click, dismiss, role, listNavigation, typeahead]);
  const rootContext = React.useMemo(() => ({
    id,
    name: params.name,
    required,
    disabled,
    readOnly,
    triggerElement,
    setTriggerElement,
    positionerElement,
    setPositionerElement,
    scrollUpArrowVisible,
    setScrollUpArrowVisible,
    scrollDownArrowVisible,
    setScrollDownArrowVisible,
    setControlledAlignItemToTrigger,
    value,
    setValue,
    open,
    setOpen,
    mounted,
    setMounted,
    label,
    setLabel,
    valueRef,
    valuesRef,
    labelsRef,
    typingRef,
    selectionRef,
    getRootPositionerProps,
    getRootTriggerProps,
    getItemProps,
    listRef,
    popupRef,
    selectedItemTextRef,
    floatingRootContext,
    touchModality,
    setTouchModality,
    alignItemToTrigger,
    transitionStatus,
    fieldControlValidation,
    modal,
    registerSelectedItem,
    onOpenChangeComplete,
    keyboardActiveRef
  }), [id, params.name, required, disabled, readOnly, triggerElement, positionerElement, scrollUpArrowVisible, scrollDownArrowVisible, value, setValue, open, setOpen, mounted, setMounted, label, getRootPositionerProps, getRootTriggerProps, getItemProps, floatingRootContext, touchModality, alignItemToTrigger, transitionStatus, fieldControlValidation, modal, registerSelectedItem, onOpenChangeComplete, keyboardActiveRef]);
  const indexContext = React.useMemo(() => ({
    activeIndex,
    setActiveIndex,
    selectedIndex,
    setSelectedIndex
  }), [activeIndex, selectedIndex, setActiveIndex]);
  return React.useMemo(() => ({
    rootContext,
    indexContext
  }), [rootContext, indexContext]);
}