import { useRef, useCallback } from "react";
import { useDispatch, useSelector } from "../components/FinsembleProvider";
import { SearchActions } from "../actions/searchActions";
import { debounce } from "lodash";
import { FEAGlobals } from "../../common/Globals";
/**
 * A hook for working with search. Search results stream back, and this hook will relay those through
 * a reducer. Note that the reducer implementation is a singleton (if you have multiple search) inputs
 * across a component then they will all be synchronized to query and results.
 */
const useSearch = () => {
    const dispatch = useDispatch();
    const { searchQuery, searchResults } = useSelector((state) => state.search);
    const currentQuery = useRef({});
    const updateResults = useCallback((results) => {
        dispatch(SearchActions.UPDATE_RESULTS(results));
    }, [dispatch]);
    const onSearchClientResponse = (searchError, responses, query) => {
        // If we get a response back from an older query then drop the results
        if (query !== currentQuery.current)
            return;
        /**
         * The callback from `SearchClient.search` can return *multiple times*, and it uses a single global array
         * that it is constantly mutating. React isn't doing a deep comparison, so it never updates the results, we
         * therefore force an update by cloning the array into a new reference.
         */
        const clonedProviderResponses = JSON.parse(JSON.stringify(responses !== null && responses !== void 0 ? responses : []));
        if (!clonedProviderResponses)
            return updateResults([]);
        let providersWithData = clonedProviderResponses.filter((provider) => { var _a; return (_a = provider === null || provider === void 0 ? void 0 : provider.data) === null || _a === void 0 ? void 0 : _a.length; });
        if (providersWithData.length === 0)
            providersWithData = [];
        updateResults(providersWithData);
    };
    /**
     * The search client callback procedure is a little strange. In order to prevent a hangup for slow search
     * providers, your callback to searchClient.search is invoked multiple times -- one time per search provider.
     *
     * Your search goes out for "De", you get a response from the WindowService saying "No components match".
     * Then you get a response from the WorkspaceService saying "Hey DefaultWorkspace matches!". Your callback
     * has been hit twice. On the 2nd time, you get all of the data you would've gotten the first time.
     *
     * Without debouncing the search client response, we end up setting intermediary state, which forces the DOM to
     * render, which forces the window size to change to fit the DOM. Imagine the situation above.
     *
     * We send out a search for "DE", and the WindowService comes back at us first saying 'no components'. There's no data,
     * so there's no results. So there's no DOM spit out for the search results. This causes the window to size down
     * to its minimum. Milliseconds later, the workspace service comes at us saying "Hey I have workspaces!", which
     * causes the search menu to expand to fit the results in the DOM. By setting the intermediary
     * state, we cause unnecessary renders and we end up resizing resizing the window in a very jarring manner.
     * By waiting until they've had a chance to come at us in a burst, we make the menu resizing smoother and less prone to jitter.
     */
    const debouncedSearchResponse = debounce(onSearchClientResponse, 100);
    const doSearch = (query) => {
        // Run the search() effect. One day, move this to effects middleware.
        FEAGlobals.FSBL.Clients.SearchClient.search({ text: query }, (e, responses) => {
            debouncedSearchResponse(e, responses, query);
        });
    };
    const debouncedSearchQuery = debounce(doSearch, 200);
    const updateQuery = useCallback((query) => {
        dispatch(SearchActions.UPDATE_QUERY(query));
        currentQuery.current = query;
        debouncedSearchQuery(query);
    }, [dispatch, updateResults]);
    const resetSearch = useCallback(() => {
        dispatch(SearchActions.RESET_SEARCH());
    }, [dispatch]);
    return {
        searchResults,
        updateQuery,
        searchQuery,
        resetSearch,
    };
};
export { useSearch };
//# sourceMappingURL=useSearch.js.map