'use client';
import { useSelectableCollection } from '@react-aria/selection';
import { chain, mergeProps, filterDOMProps, isMac } from '@react-aria/utils';
import { useListState } from '@react-stately/list';
import { useRef, useEffect, forwardRef, useState, useImperativeHandle, useId, createContext, useContext } from 'react';
import { useListBoxLayout, ListBoxBase, listStyles } from '@keystar/ui/listbox';
export { Item, Section } from '@keystar/ui/listbox';
import { jsx } from 'react/jsx-runtime';
import { useFloating, autoUpdate, FloatingPortal, offset, shift, limitShift, flip, size, inline } from '@floating-ui/react';
import { useStyleProps, classNames, css, tokenSchema } from '@keystar/ui/style';
import { FocusScope, createFocusManager } from '@react-aria/focus';
import { useLocale } from '@react-aria/i18n';
import { PressResponder } from '@react-aria/interactions';
import { useControlledState } from '@react-stately/utils';
import { assertNever } from 'emery';
import { ActionButton, ToggleButton } from '@keystar/ui/button';
import { Flex, Divider } from '@keystar/ui/layout';

function EditorListbox(props) {
  let {
    listenerRef,
    onEscape,
    scrollRef,
    ...otherProps
  } = props;
  let state = useListState(props);
  let layout = useListBoxLayout(state);

  // keyboard and selection management
  let listboxRef = useRef(null);
  let {
    collectionProps
  } = useSelectableCollection({
    keyboardDelegate: layout,
    ref: listenerRef,
    scrollRef: scrollRef !== null && scrollRef !== void 0 ? scrollRef : listboxRef,
    selectionManager: state.selectionManager,
    disallowEmptySelection: true,
    disallowTypeAhead: true,
    isVirtualized: true,
    shouldFocusWrap: true
  });
  let onKeyDown = e => {
    var _props$onAction;
    switch (e.key) {
      case 'Enter':
        state.selectionManager.select(state.selectionManager.focusedKey);
        (_props$onAction = props.onAction) === null || _props$onAction === void 0 ? void 0 : _props$onAction.call(props, state.selectionManager.focusedKey);
        break;
      case 'Escape':
        onEscape === null || onEscape === void 0 ? void 0 : onEscape();
        break;
    }
  };
  let keydownListener = chain(onKeyDown, collectionProps.onKeyDown);
  useEffect(() => {
    let domNode = listenerRef.current;
    domNode === null || domNode === void 0 ? void 0 : domNode.addEventListener('keydown', keydownListener);
    return () => domNode === null || domNode === void 0 ? void 0 : domNode.removeEventListener('keydown', keydownListener);
  }, [keydownListener, listenerRef]);
  return /*#__PURE__*/jsx(ListBoxBase, {
    ref: listboxRef,
    layout: layout,
    state: state,
    autoFocus: "first"
    // focusOnPointerEnter
    ,
    shouldUseVirtualFocus: true,
    shouldFocusWrap: true,
    UNSAFE_className: listStyles,
    ...otherProps
  });
}

const EditorPopover = /*#__PURE__*/forwardRef(function EditorPopover(props, forwardedRef) {
  const {
    children,
    reference,
    placement = 'bottom'
  } = props;
  const styleProps = useStyleProps(props);
  const [floating, setFloating] = useState(null);
  const middleware = getMiddleware(props);
  const {
    floatingStyles,
    context,
    update
  } = useFloating({
    elements: {
      reference,
      floating
    },
    middleware,
    placement,
    whileElementsMounted: autoUpdate
  });
  useImperativeHandle(forwardedRef, () => {
    return {
      context,
      update
    };
  }, [context, update]);
  return /*#__PURE__*/jsx(FloatingPortal, {
    children: /*#__PURE__*/jsx(DialogElement, {
      ref: setFloating,
      ...styleProps,
      style: {
        ...floatingStyles,
        ...styleProps.style
      },
      children: children
    })
  });
});

// Utils
// ------------------------------

const DEFAULT_OFFSET = 8;
function getMiddleware(props) {
  const {
    adaptToViewport
  } = props;
  if (adaptToViewport === 'stick') {
    return [offset(DEFAULT_OFFSET), shift({
      crossAxis: true,
      padding: DEFAULT_OFFSET,
      limiter: limitShift({
        offset: _ref => {
          let {
            rects
          } = _ref;
          return {
            crossAxis: rects.floating.height
          };
        }
      })
    })];
  }
  if (adaptToViewport === 'stretch') {
    return [flip(), offset(DEFAULT_OFFSET), size({
      apply(_ref2) {
        let {
          elements,
          availableHeight
        } = _ref2;
        Object.assign(elements.floating.style, {
          maxHeight: `${availableHeight}px`
        });
      },
      padding: DEFAULT_OFFSET
    })];
  }
  return [offset(DEFAULT_OFFSET), flip({
    padding: DEFAULT_OFFSET
  }), shift({
    padding: DEFAULT_OFFSET
  }), inline()];
}

// Styled components
// ------------------------------

const DialogElement = /*#__PURE__*/forwardRef(function DialogElement(props, forwardedRef) {
  return /*#__PURE__*/jsx("div", {
    role: "dialog",
    ref: forwardedRef,
    ...props,
    className: classNames(css({
      backgroundColor: tokenSchema.color.background.surface,
      borderRadius: tokenSchema.size.radius.medium,
      border: `${tokenSchema.size.border.regular} solid ${tokenSchema.color.border.emphasis}`,
      boxShadow: `${tokenSchema.size.shadow.medium} ${tokenSchema.color.shadow.regular}`,
      boxSizing: 'content-box',
      // resolves measurement/scroll issues related to border
      minHeight: tokenSchema.size.element.regular,
      minWidth: tokenSchema.size.element.regular,
      outline: 0
    }), props.className)
  });
});

const EditorToolbarContext = /*#__PURE__*/createContext(null);
function useToolbarContext() {
  let context = useContext(EditorToolbarContext);
  if (context == null) {
    throw new Error('useToolbarContext must be used within a EditorToolbar');
  }
  return context;
}
function EditorToolbar(props) {
  let {
    children
  } = props;
  let ref = useRef(null);
  let {
    state,
    toolbarProps
  } = useToolbar(props, ref);
  return /*#__PURE__*/jsx(EditorToolbarContext.Provider, {
    value: {
      state
    },
    children: /*#__PURE__*/jsx(FocusScope, {
      children: /*#__PURE__*/jsx(Flex, {
        gap: "regular",
        ref: ref,
        ...toolbarProps,
        children: children
      })
    })
  });
}

// =============================================================================
// Group
// =============================================================================

const GroupSelectionContext = /*#__PURE__*/createContext(null);
function useGroupSelectionContext() {
  let context = useContext(GroupSelectionContext);
  if (context == null) {
    throw new Error('useGroupSelectionContext must be used within a group');
  }
  return context;
}
function useSelectionItem(props) {
  let {
    isDisabled,
    value
  } = props;
  let context = useGroupSelectionContext();
  if (context.selectionMode === 'single') {
    let {
      selectedValue,
      setSelectedValue
    } = context;
    let isSelected = selectedValue === value;
    return {
      isSelected,
      buttonProps: {
        'aria-checked': isSelected,
        onPress: () => {
          if (isDisabled) {
            return;
          }
          if (isSelected) {
            setSelectedValue(null);
          } else {
            setSelectedValue(value);
          }
        },
        role: 'radio'
      }
    };
  }
  if (context.selectionMode === 'multiple') {
    let {
      selectedValue,
      setSelectedValue
    } = context;
    let isSelected = selectedValue.includes(value);
    return {
      isSelected,
      buttonProps: {
        'aria-pressed': isSelected,
        onPress: () => {
          if (isDisabled) {
            return;
          }
          if (selectedValue.includes(value)) {
            setSelectedValue(selectedValue.filter(existingValue => existingValue !== value));
          } else {
            setSelectedValue(selectedValue.concat(value));
          }
        }
      }
    };
  }
  assertNever(context);
}
function EditorToolbarGroup(props) {
  if (props.selectionMode === 'single') {
    return /*#__PURE__*/jsx(EditorSingleSelectionGroup, {
      ...props
    });
  }
  if (props.selectionMode === 'multiple') {
    return /*#__PURE__*/jsx(EditorMultipleSelectionGroup, {
      ...props
    });
  }
  return /*#__PURE__*/jsx(Flex, {
    gap: "xsmall",
    role: "group",
    ...filterDOMPropsWithLabelWarning(props),
    children: props.children
  });
}
/** @private SINGLE selection */
function EditorSingleSelectionGroup(props) {
  let [selectedValue, setSelectedValue] = useControlledState(props.value, props.defaultValue, props.onChange);
  let context = {
    selectionMode: props.selectionMode,
    selectedValue,
    setSelectedValue
  };
  return /*#__PURE__*/jsx(GroupSelectionContext.Provider, {
    value: context,
    children: /*#__PURE__*/jsx(Flex, {
      gap: "xsmall",
      role: "radiogroup",
      ...filterDOMPropsWithLabelWarning(props),
      children: props.children
    })
  });
}
/** @private MULTI selection */
function EditorMultipleSelectionGroup(props) {
  let [selectedValue, setSelectedValue] = useControlledState(props.value, props.defaultValue || [], props.onChange);
  let context = {
    selectionMode: props.selectionMode,
    selectedValue,
    setSelectedValue
  };
  return /*#__PURE__*/jsx(GroupSelectionContext.Provider, {
    value: context,
    children: /*#__PURE__*/jsx(Flex, {
      gap: "xsmall",
      role: "group",
      ...filterDOMPropsWithLabelWarning(props),
      children: props.children
    })
  });
}

// =============================================================================
// Item
// =============================================================================

/** A toolbar item may be a checkbox/radio/toggle button, depending on context. */
function EditorToolbarItem(props) {
  let {
    children,
    isDisabled,
    ...otherProps
  } = props;
  let {
    itemProps
  } = useToolbarItem(props);
  let {
    isSelected,
    buttonProps
  } = useSelectionItem(props);
  return (
    /*#__PURE__*/
    // Use a PressResponder to send DOM props through, allow overriding things
    // like role and tabIndex.
    jsx(PressResponder, {
      ...mergeProps(buttonProps, itemProps),
      children: /*#__PURE__*/jsx(ActionButton, {
        prominence: "low",
        isDisabled: isDisabled,
        isSelected: isSelected,
        ...otherProps,
        children: children
      })
    })
  );
}
function EditorToolbarButton(props) {
  let {
    itemProps
  } = useToolbarItem(props);
  return /*#__PURE__*/jsx(PressResponder, {
    ...itemProps,
    children: /*#__PURE__*/jsx(ToggleButton, {
      prominence: "low",
      ...props
    })
  });
}
function EditorToolbarSeparator() {
  return /*#__PURE__*/jsx(Divider, {
    orientation: "vertical",
    flexShrink: 0
  });
}

// =============================================================================
// Utils
// =============================================================================

function filterDOMPropsWithLabelWarning(props) {
  let {
    'aria-labelledby': ariaLabelledby,
    'aria-label': ariaLabel
  } = props;
  if (!ariaLabelledby && !ariaLabel) {
    console.warn('You must specify an aria-label or aria-labelledby attribute for accessibility.');
  }
  return filterDOMProps(props, {
    labelable: true
  });
}
function useToolbarItem(props) {
  let {
    isDisabled
  } = props;
  let {
    state
  } = useToolbarContext();
  let {
    lastFocusedId,
    setLastFocusedId
  } = state;
  let id = useId();
  let tabIndex = lastFocusedId === id || lastFocusedId == null ? 0 : -1;

  // clear the last focused ID when the item is unmounted or becomes disabled,
  // which will reset the tabIndex for each item to 0 avoiding a situation where
  // the user cannot tab to any items
  useEffect(() => {
    let reset = lastId => lastId === id ? null : lastId;
    if (isDisabled) {
      setLastFocusedId(reset);
    }
    return () => {
      setLastFocusedId(reset);
    };
  }, [id, isDisabled, setLastFocusedId]);
  return {
    itemProps: {
      tabIndex,
      onFocus: () => {
        setLastFocusedId(id);
      }
    }
  };
}
function useToolbar(props, ref) {
  let [lastFocusedId, setLastFocusedId] = useState(null);
  let {
    direction
  } = useLocale();
  let focusManager = createFocusManager(ref, {
    wrap: true
  });
  let isRtl = direction === 'rtl';
  let onKeyDown = e => {
    if (!e.currentTarget.contains(e.target)) {
      return;
    }

    // let users navigate by group with alt/ctrl + arrow keys
    let accept = node => {
      var _node$parentElement, _node$parentElement2;
      let isFirstChild = ((_node$parentElement = node.parentElement) === null || _node$parentElement === void 0 ? void 0 : _node$parentElement.firstElementChild) === node;
      let isGroupChild = /group/.test(((_node$parentElement2 = node.parentElement) === null || _node$parentElement2 === void 0 ? void 0 : _node$parentElement2.role) || '');
      return !isGroupChild || isFirstChild;
    };
    let options = (isMac() ? e.altKey : e.ctrlKey) ? {
      accept
    } : {};
    switch (e.key) {
      case 'Home':
        e.preventDefault();
        e.stopPropagation();
        focusManager.focusFirst();
        break;
      case 'End':
        e.preventDefault();
        e.stopPropagation();
        focusManager.focusLast();
        break;
      case 'ArrowRight':
      case 'ArrowDown':
        e.preventDefault();
        e.stopPropagation();
        if (e.key === 'ArrowRight' && isRtl) {
          focusManager.focusPrevious(options);
        } else {
          focusManager.focusNext(options);
        }
        break;
      case 'ArrowLeft':
      case 'ArrowUp':
        e.preventDefault();
        e.stopPropagation();
        if (e.key === 'ArrowLeft' && isRtl) {
          focusManager.focusNext(options);
        } else {
          focusManager.focusPrevious(options);
        }
        break;
    }
  };
  return {
    toolbarProps: {
      ...filterDOMPropsWithLabelWarning(props),
      onKeyDown,
      role: 'toolbar',
      'aria-orientation': 'horizontal'
    },
    state: {
      lastFocusedId,
      setLastFocusedId
    }
  };
}

export { EditorListbox, EditorPopover, EditorToolbar, EditorToolbarButton, EditorToolbarGroup, EditorToolbarItem, EditorToolbarSeparator };
