import React, { useState, useCallback, useRef, useEffect, useImperativeHandle, forwardRef, } from "react";
import Icon from "../../Icon/Icon";
import IconProp from "../../../../Types/Icon/IconProp";
import TelemetrySearchSuggestions from "./TelemetrySearchSuggestions";
import TelemetrySearchHelp from "./TelemetrySearchHelp";
const TelemetrySearchBar = forwardRef((props, ref) => {
    const [isFocused, setIsFocused] = useState(false);
    const [showSuggestions, setShowSuggestions] = useState(false);
    const [showHelp, setShowHelp] = useState(false);
    const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1);
    const inputRef = useRef(null);
    const containerRef = useRef(null);
    const fieldAliasMap = props.fieldAliasMap || {};
    useImperativeHandle(ref, () => {
        return {
            focus: () => {
                var _a;
                (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
            },
        };
    }, []);
    const currentWord = extractCurrentWord(props.value);
    const hasAtPrefix = currentWord.startsWith("@");
    const normalizedWord = hasAtPrefix
        ? currentWord.substring(1)
        : currentWord;
    const colonIndex = normalizedWord.indexOf(":");
    const isValueMode = colonIndex > 0;
    /*
     * Preserve user casing — telemetry attribute keys (e.g. `requestId`,
     * `http.method`) are case-sensitive in the data, so lowercasing here
     * would silently break searches against camelCase keys. Alias lookups
     * via `fieldAliasMap` handle their own case normalisation.
     */
    const fieldPrefix = isValueMode
        ? normalizedWord.substring(0, colonIndex)
        : "";
    const partialValue = isValueMode
        ? normalizedWord.substring(colonIndex + 1)
        : "";
    /*
     * Pick the suggestion list based on whether the user typed `@`.
     * `@` is the explicit trigger for attribute mode — only show attribute
     * keys there. Without `@`, only show top-level field names. Mixing the
     * two led to confusing dropdowns that rendered field names like "name"
     * as if they were attributes.
     */
    const activeSuggestions = hasAtPrefix
        ? props.attributeSuggestions || []
        : props.suggestions || [];
    const filteredSuggestions = isValueMode
        ? getValueSuggestions(fieldPrefix, partialValue, props.valueSuggestions || {}, fieldAliasMap)
        : activeSuggestions.filter((s) => {
            if (!normalizedWord && !hasAtPrefix) {
                return false;
            }
            if (hasAtPrefix && normalizedWord.length === 0) {
                return true;
            }
            return s.toLowerCase().startsWith(normalizedWord.toLowerCase());
        });
    /*
     * Show a loader inside the dropdown while the parent is fetching:
     *   - attribute keys: `@` was just typed but the keys haven't arrived
     *   - attribute values: `@key:` was typed but values for that key
     *     haven't arrived yet
     */
    const isLoadingForCurrentMode = isValueMode
        ? Boolean(props.isValuesLoading)
        : hasAtPrefix
            ? Boolean(props.isAttributesLoading)
            : false;
    const shouldShowSuggestions = showSuggestions &&
        isFocused &&
        (filteredSuggestions.length > 0 || isLoadingForCurrentMode) &&
        (isValueMode ? true : currentWord.length > 0);
    const shouldShowHelp = showHelp &&
        isFocused &&
        props.value.length === 0 &&
        !shouldShowSuggestions &&
        props.helpRows !== undefined &&
        props.helpRows.length > 0;
    useEffect(() => {
        setSelectedSuggestionIndex(-1);
    }, [currentWord]);
    const handleKeyDown = useCallback((e) => {
        if (e.key === "Enter") {
            if (shouldShowSuggestions &&
                !isLoadingForCurrentMode &&
                selectedSuggestionIndex >= 0 &&
                selectedSuggestionIndex < filteredSuggestions.length) {
                applySuggestion(filteredSuggestions[selectedSuggestionIndex]);
                e.preventDefault();
                return;
            }
            if (isValueMode &&
                partialValue.length > 0 &&
                props.onFieldValueSelect) {
                /*
                 * Prefer a match from the suggestion list (so casing matches
                 * what's actually in the data); otherwise accept the typed
                 * value as-is so users aren't blocked when no suggestion exists.
                 */
                const resolvedField = fieldAliasMap[fieldPrefix.toLowerCase()] || fieldPrefix;
                const availableValues = (props.valueSuggestions || {})[resolvedField] || [];
                const lowerPartial = partialValue.toLowerCase();
                const exactMatch = availableValues.find((v) => {
                    return v.toLowerCase() === lowerPartial;
                });
                const resolvedMatch = exactMatch ||
                    (filteredSuggestions.length === 1
                        ? filteredSuggestions[0]
                        : partialValue);
                props.onFieldValueSelect(fieldPrefix, resolvedMatch);
                const parts = props.value.split(/\s+/);
                parts.pop();
                const remaining = parts.join(" ");
                props.onChange(remaining ? remaining + " " : "");
                setShowSuggestions(false);
                setShowHelp(false);
                e.preventDefault();
                return;
            }
            props.onSubmit();
            setShowSuggestions(false);
            setShowHelp(false);
            return;
        }
        if (e.key === "Escape") {
            setShowSuggestions(false);
            setShowHelp(false);
            return;
        }
        if (!shouldShowSuggestions || isLoadingForCurrentMode) {
            return;
        }
        if (e.key === "ArrowDown") {
            e.preventDefault();
            setSelectedSuggestionIndex((prev) => {
                return Math.min(prev + 1, filteredSuggestions.length - 1);
            });
            return;
        }
        if (e.key === "ArrowUp") {
            e.preventDefault();
            setSelectedSuggestionIndex((prev) => {
                return Math.max(prev - 1, 0);
            });
        }
    }, [
        shouldShowSuggestions,
        selectedSuggestionIndex,
        filteredSuggestions,
        isValueMode,
        fieldPrefix,
        partialValue,
        props,
        fieldAliasMap,
        isLoadingForCurrentMode,
    ]);
    const applySuggestion = useCallback((suggestion) => {
        var _a, _b;
        if (isValueMode) {
            if (props.onFieldValueSelect) {
                props.onFieldValueSelect(fieldPrefix, suggestion);
            }
            const parts = props.value.split(/\s+/);
            parts.pop();
            const remaining = parts.join(" ");
            props.onChange(remaining ? remaining + " " : "");
            setShowSuggestions(false);
            setShowHelp(false);
            (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
            return;
        }
        const parts = props.value.split(/\s+/);
        if (parts.length > 0) {
            /*
             * Attribute suggestions are stored without `@`; add it back
             * when filling the bar so the parser recognizes it as an attribute.
             */
            parts[parts.length - 1] = hasAtPrefix
                ? "@" + suggestion + ":"
                : suggestion + ":";
        }
        props.onChange(parts.join(" "));
        setShowSuggestions(false);
        setShowHelp(false);
        (_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.focus();
    }, [props, isValueMode, fieldPrefix, hasAtPrefix]);
    const handleExampleClick = useCallback((example) => {
        var _a;
        props.onChange(example);
        setShowHelp(false);
        (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
    }, [props]);
    useEffect(() => {
        const handleClickOutside = (e) => {
            if (containerRef.current &&
                !containerRef.current.contains(e.target)) {
                setShowSuggestions(false);
                setShowHelp(false);
            }
        };
        document.addEventListener("mousedown", handleClickOutside);
        return () => {
            document.removeEventListener("mousedown", handleClickOutside);
        };
    }, []);
    const loadingMessage = isValueMode
        ? `Loading values for ${fieldPrefix}...`
        : "Loading attributes...";
    const emptyMessage = isValueMode &&
        !isLoadingForCurrentMode &&
        filteredSuggestions.length === 0
        ? `No matching values — press Enter to filter by "${partialValue}"`
        : undefined;
    return (React.createElement("div", { ref: containerRef, className: "relative" },
        React.createElement("div", { className: `flex items-center gap-2 rounded-lg border bg-white px-3 py-2 transition-colors ${isFocused
                ? "border-indigo-400 ring-2 ring-indigo-100"
                : "border-gray-200 hover:border-gray-300"}` },
            React.createElement(Icon, { icon: IconProp.Search, className: "h-4 w-4 flex-none text-gray-400" }),
            React.createElement("input", { ref: inputRef, type: "text", value: props.value, onChange: (e) => {
                    props.onChange(e.target.value);
                    setShowSuggestions(true);
                    setShowHelp(false);
                }, onFocus: () => {
                    setIsFocused(true);
                    setShowSuggestions(true);
                    if (props.value.length === 0) {
                        setShowHelp(true);
                    }
                }, onBlur: () => {
                    setIsFocused(false);
                }, onKeyDown: handleKeyDown, placeholder: props.placeholder || "Search...", className: "flex-1 bg-transparent font-mono text-sm text-gray-900 placeholder-gray-400 outline-none", spellCheck: false, autoComplete: "off" }),
            props.value.length > 0 && (React.createElement("button", { type: "button", className: "flex-none rounded-full p-1 text-gray-400 hover:bg-gray-100", onClick: () => {
                    var _a;
                    props.onChange("");
                    setShowHelp(true);
                    setShowSuggestions(false);
                    (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
                }, title: "Clear search" },
                React.createElement(Icon, { icon: IconProp.Close, className: "h-3.5 w-3.5" })))),
        shouldShowSuggestions && (React.createElement(TelemetrySearchSuggestions, { suggestions: filteredSuggestions, selectedIndex: selectedSuggestionIndex, onSelect: applySuggestion, fieldContext: isValueMode ? fieldPrefix : undefined, isAttributeMode: hasAtPrefix, isLoading: isLoadingForCurrentMode, loadingMessage: loadingMessage, emptyMessage: emptyMessage })),
        shouldShowHelp && props.helpRows && (React.createElement(TelemetrySearchHelp, { rows: props.helpRows, combinedExample: props.helpCombinedExample, onExampleClick: handleExampleClick }))));
});
function extractCurrentWord(value) {
    const parts = value.split(/\s+/);
    return parts[parts.length - 1] || "";
}
function getValueSuggestions(fieldName, partialValue, valueSuggestions, aliasMap) {
    // Case-insensitive alias lookup, preserve original case as fallback for attributes
    const resolvedField = aliasMap[fieldName.toLowerCase()] || fieldName;
    const values = valueSuggestions[resolvedField];
    if (!values || values.length === 0) {
        return [];
    }
    if (!partialValue || partialValue.length === 0) {
        return values;
    }
    const lowerPartial = partialValue.toLowerCase();
    return values.filter((v) => {
        return v.toLowerCase().startsWith(lowerPartial);
    });
}
TelemetrySearchBar.displayName = "TelemetrySearchBar";
export default TelemetrySearchBar;
//# sourceMappingURL=TelemetrySearchBar.js.map