import { useTreeRootId, useExtendTree, useEnvironment } from "@contember/binding";
import { useState, useMemo, useRef, useEffect } from "react";
import { useDesugaredOptionPath } from "./useDesugaredOptionPath.js";
import { useTopLevelOptionAccessors } from "./useTopLevelOptionAccessor.js";
import { useNormalizedOptions } from "./useNormalizedOptions.js";
import { useDebounce } from "@contember/react-utils";
import { renderDynamicChoiceFieldStatic } from "../renderDynamicChoiceFieldStatic.js";
import { useCreateOptionsFilter } from "./useCreateOptionsFilter.js";
import Fuse from "fuse.js";
const useSelectOptions = (optionProps, additionalAccessors = []) => {
  const [input, setSearchInput] = useState(void 0);
  const { renderedState, isLoading } = useOptionsLoader(optionProps, input);
  const desugaredOptionPath = useDesugaredOptionPath(optionProps, renderedState.filter);
  const topLevelOptionAccessors = useTopLevelOptionAccessors(desugaredOptionPath, renderedState.treeRootId);
  const mergedEntities = useMergedEntities(topLevelOptionAccessors, additionalAccessors);
  const options = useNormalizedOptions(mergedEntities, desugaredOptionPath, optionProps);
  const fuseFilteredOptions = useFuseFilteredOptions(optionProps, options, input);
  const transformedOptions = useCustomTransformedOptions(optionProps, fuseFilteredOptions, input);
  const slicedOptions = useSlicedOptions(optionProps, transformedOptions);
  return {
    options: slicedOptions,
    allOptions: options,
    onSearch: setSearchInput,
    isLoading
  };
};
const useMergedEntities = (topLevelOptionAccessors, additionalAccessors) => {
  return useMemo(() => {
    const ids = new Set(topLevelOptionAccessors.map((it) => it.id));
    return [
      ...topLevelOptionAccessors,
      ...additionalAccessors.filter((it) => !ids.has(it.id))
    ];
  }, [additionalAccessors, topLevelOptionAccessors]);
};
const useFuseFilteredOptions = (optionProps, options, input) => {
  const fuseOpts = optionProps.fuseOptions ?? true;
  const fuse = useMemo(() => {
    if (!options.some((it) => it.searchKeywords !== "")) {
      return void 0;
    }
    if (!fuseOpts) {
      return void 0;
    }
    return new Fuse(options, {
      ...fuseOpts === true ? {} : fuseOpts,
      keys: ["searchKeywords"]
    });
  }, [fuseOpts, options]);
  return useMemo(() => {
    return input && fuse ? fuse.search(input).map((it) => it.item) : options;
  }, [fuse, input, options]);
};
const useCustomTransformedOptions = (optionProps, options, input) => {
  const transformFn = optionProps.transformOptions;
  return useMemo(() => {
    return transformFn?.(options, input) ?? options;
  }, [transformFn, options, input]);
};
const RENDERED_OPTIONS_LIMIT = 100;
const useSlicedOptions = (optionProps, options) => {
  const renderedLimit = optionProps.renderedOptionsLimit ?? RENDERED_OPTIONS_LIMIT;
  return useMemo(() => {
    if (renderedLimit === 0) {
      return options;
    }
    return options.slice(0, renderedLimit);
  }, [options, renderedLimit]);
};
const useOptionsLoader = (optionProps, input) => {
  const delay = typeof optionProps.lazy === "object" ? optionProps.lazy.inputDebounceDelay : null;
  const debouncedInput = useDebounce(input, delay ?? 250);
  const inputRef = useRef(input);
  useEffect(() => {
    inputRef.current = input;
  }, [input]);
  const initialRootId = useTreeRootId();
  const [renderedState, setRenderedState] = useState({
    treeRootId: initialRootId,
    filter: void 0,
    query: void 0
  });
  const desugaredOptionPath = useDesugaredOptionPath(optionProps, void 0);
  const extendTree = useExtendTree();
  const environment = useEnvironment();
  const createFilter = useCreateOptionsFilter(desugaredOptionPath, optionProps.searchByFields, optionProps.lazy);
  const isEmptyInit = typeof optionProps.lazy === "object" && optionProps.lazy.initialLimit === 0;
  useEffect(() => {
    if (input === "" && !isEmptyInit) {
      setRenderedState({
        treeRootId: initialRootId,
        filter: void 0,
        query: ""
      });
    }
  }, [initialRootId, input, isEmptyInit]);
  useEffect(() => {
    if (!optionProps.lazy || debouncedInput === renderedState.query || debouncedInput !== inputRef.current || debouncedInput === void 0) {
      return;
    }
    if (debouncedInput === "" && !isEmptyInit) {
      return;
    }
    (async () => {
      const filter = createFilter(debouncedInput);
      const { subTree } = renderDynamicChoiceFieldStatic({
        ...optionProps,
        createNewForm: void 0
      }, environment, filter);
      const treeRootId = await extendTree(subTree);
      if (treeRootId && inputRef.current === debouncedInput) {
        setRenderedState({
          filter,
          treeRootId,
          query: debouncedInput
        });
      }
    })();
  }, [createFilter, debouncedInput, environment, extendTree, isEmptyInit, optionProps, renderedState.query]);
  return {
    renderedState,
    isLoading: !!optionProps.lazy && renderedState.query !== input
  };
};
export { useSelectOptions };
//# sourceMappingURL=useSelectOptions.js.map
