import React, { useEffect, useMemo, useRef, useState, } from "react";
const BASE_INPUT_CLASS = "block w-full rounded-md border border-gray-300 bg-white py-2 pl-3 pr-3 text-sm placeholder-gray-500 focus:border-indigo-500 focus:text-gray-900 focus:placeholder-gray-400 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm";
/*
 * Aligned with the server-side attribute-value limit (ATTRIBUTE_VALUES_LIMIT)
 * so that when suggestions are fetched server-side (and already narrowed by
 * search text), the dropdown can show the full returned set rather than
 * truncating it further on the client.
 */
const MAX_SUGGESTIONS = 100;
// Provides a free-form text input with an optional suggestion dropdown.
const AutocompleteTextInput = (props) => {
    const [inputValue, setInputValue] = useState(props.value || "");
    const [isMenuVisible, setIsMenuVisible] = useState(false);
    const [highlightedIndex, setHighlightedIndex] = useState(-1);
    const containerRef = useRef(null);
    const blurTimeoutRef = useRef(null);
    const listboxIdRef = useRef(`autocomplete-suggestions-${Math.random().toString(36).slice(2, 10)}`);
    useEffect(() => {
        setInputValue(props.value || "");
    }, [props.value]);
    useEffect(() => {
        const handleClickOutside = (event) => {
            if (containerRef.current &&
                event.target instanceof Node &&
                !containerRef.current.contains(event.target)) {
                setIsMenuVisible(false);
                setHighlightedIndex(-1);
            }
        };
        document.addEventListener("mousedown", handleClickOutside);
        return () => {
            document.removeEventListener("mousedown", handleClickOutside);
        };
    }, []);
    const suggestions = useMemo(() => {
        const uniqueSuggestions = props.suggestions
            ? Array.from(new Set(props.suggestions))
            : [];
        if (uniqueSuggestions.length === 0) {
            return [];
        }
        const normalizedInput = inputValue.trim().toLowerCase();
        if (normalizedInput === "") {
            return uniqueSuggestions.slice(0, MAX_SUGGESTIONS);
        }
        return uniqueSuggestions
            .filter((suggestion) => {
            return suggestion.toLowerCase().includes(normalizedInput);
        })
            .slice(0, MAX_SUGGESTIONS);
    }, [inputValue, props.suggestions]);
    const isLoadingSuggestions = Boolean(props.isLoadingSuggestions);
    const showMenu = isMenuVisible && (suggestions.length > 0 || isLoadingSuggestions);
    const getInputClassName = () => {
        let className = props.className || BASE_INPUT_CLASS;
        if (props.disabled) {
            className += " bg-gray-100 text-gray-500 cursor-not-allowed";
        }
        return className;
    };
    const clearBlurTimeout = () => {
        if (blurTimeoutRef.current !== null) {
            window.clearTimeout(blurTimeoutRef.current);
            blurTimeoutRef.current = null;
        }
    };
    const handleSuggestionSelect = (value) => {
        var _a;
        clearBlurTimeout();
        setInputValue(value);
        (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value);
        setIsMenuVisible(false);
        setHighlightedIndex(-1);
    };
    const handleInputChange = (event) => {
        var _a;
        const value = event.target.value;
        setInputValue(value);
        (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value);
        setIsMenuVisible(true);
        setHighlightedIndex(-1);
    };
    const handleInputFocus = () => {
        var _a;
        clearBlurTimeout();
        setIsMenuVisible(true);
        (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props);
    };
    const handleInputBlur = () => {
        clearBlurTimeout();
        blurTimeoutRef.current = window.setTimeout(() => {
            var _a;
            setIsMenuVisible(false);
            setHighlightedIndex(-1);
            (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props);
        }, 150);
    };
    const handleKeyDown = (event) => {
        if (!showMenu || suggestions.length === 0) {
            return;
        }
        if (event.key === "ArrowDown") {
            event.preventDefault();
            setHighlightedIndex((previousIndex) => {
                const nextIndex = previousIndex + 1;
                if (nextIndex >= suggestions.length) {
                    return 0;
                }
                return nextIndex;
            });
        }
        if (event.key === "ArrowUp") {
            event.preventDefault();
            setHighlightedIndex((previousIndex) => {
                if (previousIndex <= 0) {
                    return suggestions.length - 1;
                }
                return previousIndex - 1;
            });
        }
        if (event.key === "Enter") {
            if (highlightedIndex >= 0 && highlightedIndex < suggestions.length) {
                event.preventDefault();
                handleSuggestionSelect(suggestions[highlightedIndex]);
            }
        }
        if (event.key === "Escape") {
            setIsMenuVisible(false);
            setHighlightedIndex(-1);
        }
    };
    useEffect(() => {
        if (highlightedIndex >= suggestions.length) {
            setHighlightedIndex(suggestions.length - 1);
        }
    }, [suggestions, highlightedIndex]);
    return (React.createElement("div", { ref: containerRef, className: props.outerDivClassName || "relative mt-2 mb-1 w-full" },
        React.createElement("input", { autoFocus: props.autoFocus, className: getInputClassName(), "data-testid": props.dataTestId, disabled: props.disabled, "aria-autocomplete": "list", "aria-controls": listboxIdRef.current, "aria-expanded": showMenu, role: "combobox", onBlur: handleInputBlur, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: props.placeholder, spellCheck: !props.disableSpellCheck, type: "text", value: inputValue }),
        showMenu && (React.createElement("div", { className: props.menuClassName ||
                "absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md border border-gray-200 bg-white py-1 text-sm shadow-lg", id: listboxIdRef.current, role: "listbox" },
            isLoadingSuggestions && (React.createElement("div", { className: "flex w-full items-center px-3 py-2 text-left text-gray-500" },
                React.createElement("svg", { className: "animate-spin -ml-0.5 mr-2 h-4 w-4 text-indigo-500", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "aria-hidden": "true" },
                    React.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
                    React.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })),
                React.createElement("span", null, props.loadingMessage || "Loading..."))),
            !isLoadingSuggestions &&
                suggestions.length === 0 &&
                props.noSuggestionsMessage && (React.createElement("div", { className: "flex w-full items-center px-3 py-2 text-left text-gray-500" }, props.noSuggestionsMessage)),
            !isLoadingSuggestions &&
                suggestions.map((suggestion, index) => {
                    const isActive = index === highlightedIndex;
                    return (React.createElement("button", { key: `${suggestion}-${index}`, type: "button", role: "option", "aria-selected": isActive, className: `flex w-full items-center px-3 py-2 text-left hover:bg-indigo-50 ${isActive ? "bg-indigo-600 text-white hover:bg-indigo-500" : "text-gray-700"}`, onMouseDown: (event) => {
                            event.preventDefault();
                        }, onClick: () => {
                            handleSuggestionSelect(suggestion);
                        } }, suggestion));
                })))));
};
export default AutocompleteTextInput;
//# sourceMappingURL=AutocompleteTextInput.js.map