import { jsx, jsxs } from 'react/jsx-runtime';
import React__default, { useId, useRef, useEffect, useCallback } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { createPortal } from 'react-dom';
import { XMarkIcon, PlusIcon } from '@heroicons/react/24/outline';
import { comboboxTheme } from './Combobox.theme.js';
import { FormLabel } from '../FormLabel/FormLabel.js';
import { createDropdownAnimations } from '../Select/Select.animations.js';
import { useSelectPortal } from '../../../hooks/useSelectPortal.js';
import { cn } from '../../../utils/cn.js';
import { SelectProvider, useSelectContext } from '../Select/Select.js';
import '../Select/Select.theme.js';
import { useIsClient } from '../../../hooks/useIsClient.js';
import { useSelectState } from '../Select/useSelectState.js';

/**
 * Hook to detect dark mode state from document
 */
const useDarkMode = () => {
    const [isDark, setIsDark] = React__default.useState(false);
    useEffect(() => {
        if (typeof document === "undefined")
            return;
        const checkDarkMode = () => {
            const hasDarkClass = document.documentElement.classList.contains("dark") ||
                document.body.classList.contains("dark") ||
                document.querySelector(".dark") !== null;
            setIsDark(hasDarkClass);
        };
        checkDarkMode();
        const observer = new MutationObserver(checkDarkMode);
        observer.observe(document.documentElement, {
            attributes: true,
            attributeFilter: ["class"],
        });
        observer.observe(document.body, {
            attributes: true,
            attributeFilter: ["class"],
        });
        return () => observer.disconnect();
    }, []);
    return isDark;
};
/**
 * Default filter function for options
 */
const defaultFilterFn = (option, inputValue) => {
    return option.label.toLowerCase().includes(inputValue.toLowerCase());
};
const ComboboxOptions = ({ isOpen, onClose, triggerRef, portal, placement, offset, emptyMessage, className, }) => {
    var _a;
    const { filteredOptions, highlightedIndex, setHighlightedIndex, selectedValue, selectOption, optionsRef, theme, } = useSelectContext();
    const isDarkMode = useDarkMode();
    const isClient = useIsClient();
    // Use enhanced dropdown positioning hook
    const triggerWidth = ((_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
    const { position, cssVariables, contentRef, isFlipped } = useSelectPortal({
        triggerRef,
        isOpen,
        placement,
        offset,
        estimatedWidth: triggerWidth,
    });
    const animations = createDropdownAnimations(isFlipped);
    // Combine refs
    const combinedRef = useCallback((el) => {
        if (optionsRef &&
            typeof optionsRef === "object" &&
            "current" in optionsRef) {
            optionsRef.current =
                el;
        }
        if (contentRef &&
            typeof contentRef === "object" &&
            "current" in contentRef) {
            contentRef.current = el;
        }
    }, [optionsRef, contentRef]);
    // Handle click outside
    useEffect(() => {
        if (!isOpen)
            return;
        const handleClickOutside = (event) => {
            const currentContentRef = portal
                ? contentRef.current
                : optionsRef.current;
            if (currentContentRef &&
                !currentContentRef.contains(event.target) &&
                triggerRef.current &&
                !triggerRef.current.contains(event.target)) {
                onClose();
            }
        };
        document.addEventListener("mousedown", handleClickOutside);
        return () => document.removeEventListener("mousedown", handleClickOutside);
    }, [isOpen, onClose, portal, contentRef, optionsRef, triggerRef]);
    // Theme can come from Select's theme (optionsContainerStyle) or Combobox's theme (dropdownStyle)
    const comboboxThemeRef = theme;
    const dropdownStyle = comboboxThemeRef.dropdownStyle ||
        theme.optionsContainerStyle ||
        comboboxTheme.dropdownStyle;
    const optionStyle = theme.optionStyle || comboboxTheme.optionStyle;
    const selectedOptionStyle = theme.selectedOptionStyle || comboboxTheme.selectedOptionStyle;
    const highlightedOptionStyle = theme.highlightedOptionStyle || comboboxTheme.highlightedOptionStyle;
    const disabledOptionStyle = theme.disabledOptionStyle || comboboxTheme.disabledOptionStyle;
    const createOptionStyleTheme = theme.createOptionStyle || comboboxTheme.createOptionStyle;
    const emptyMessageStyle = comboboxTheme.emptyMessageStyle;
    const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsx(motion.div, { ref: combinedRef, role: "listbox", className: cn(dropdownStyle, className), style: {
                ...cssVariables,
                overscrollBehavior: "contain",
                ...(portal
                    ? {
                        top: `${position.top}px`,
                        left: `${position.left}px`,
                        width: `${position.width}px`,
                    }
                    : {}),
            }, tabIndex: -1, variants: animations, initial: "initial", animate: "visible", exit: "exit", children: filteredOptions.length === 0 ? (jsx("div", { className: emptyMessageStyle, children: emptyMessage })) : (filteredOptions.map((option, index) => {
                const isSelected = option.value === selectedValue;
                const isHighlighted = index === highlightedIndex;
                const isCreateOption = option.id === "__create__";
                return (jsx("div", { role: "option", "aria-selected": isSelected, "aria-disabled": option.disabled, "data-option-index": index, className: cn(optionStyle, isSelected && selectedOptionStyle, isHighlighted && highlightedOptionStyle, option.disabled && disabledOptionStyle, isCreateOption && createOptionStyleTheme), onMouseEnter: () => !option.disabled && setHighlightedIndex(index), onClick: () => {
                        if (!option.disabled) {
                            selectOption(option.value, isCreateOption);
                            onClose();
                        }
                    }, children: isCreateOption ? (jsxs("span", { className: "flex items-center gap-1", children: [jsx(PlusIcon, { className: "size-4 shrink-0", strokeWidth: 2 }), jsx("span", { className: "block truncate", children: option.label })] })) : (jsx("span", { className: "block truncate", children: option.label })) }, option.id));
            })) })) }));
    if (portal && isClient) {
        return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: optionsContent }), document.body);
    }
    return optionsContent;
};
// ============================================================================
// Main Combobox Component
// ============================================================================
/**
 * Combobox Component
 *
 * An autocomplete input where the input IS the trigger.
 * Type directly to filter options. Built on Select primitives.
 *
 * @example
 * ```tsx
 * <Combobox
 *   options={[
 *     { id: "1", label: "Apple", value: "apple" },
 *     { id: "2", label: "Banana", value: "banana" },
 *   ]}
 *   value={selected}
 *   onChange={setSelected}
 *   placeholder="Search fruits..."
 * />
 * ```
 */
const ComboboxInner = ({ options, value, onChange, placeholder = "Search...", label, helperText, size = "md", state = "default", clearable = true, iconStart, openOnFocus = true, filterFn = defaultFilterFn, emptyMessage = "No results found", creatable = false, onCreateOption, createLabel, required = false, name, className, wrapperClassName, inputClassName, dropdownClassName, theme: themeOverrides, portal = true, placement = "bottom-start", offset = 8, id, ...props }, ref) => {
    var _a;
    // Merge theme
    const theme = React__default.useMemo(() => ({ ...comboboxTheme, ...(themeOverrides || {}) }), [themeOverrides]);
    // Generate unique ID
    const generatedId = useId();
    const comboboxId = id || `combobox-${generatedId}`;
    const listboxId = `${comboboxId}-listbox`;
    // Refs
    const inputRef = useRef(null);
    const wrapperRef = useRef(null);
    const justSelectedRef = useRef(false);
    // Convert ComboboxOption to SelectOption format
    const selectOptions = React__default.useMemo(() => options.map((opt) => ({
        id: opt.id,
        label: opt.label,
        value: opt.value,
        disabled: opt.disabled,
    })), [options]);
    // Custom filter wrapper - filter options before passing to useSelectState
    // This allows us to use the custom filterFn prop
    selectOptions.filter((opt) => {
        // If there's no search value in the internal state yet, return all
        // We'll handle filtering via searchValue from selectState
        return true; // Let useSelectState handle filtering
    });
    // Use the shared select state hook
    const selectState = useSelectState({
        options: selectOptions,
        value,
        onChange: (newValue) => {
            onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
        },
        searchable: true, // Combobox is always searchable
        creatable,
        onCreateOption,
        createLabel,
    });
    const { isOpen, setIsOpen, searchValue, setSearchValue, highlightedIndex, setHighlightedIndex, filteredOptions: baseFilteredOptions, selectedOption, selectOption, } = selectState;
    // Apply custom filter function if provided (override the default filtering)
    const customFilteredOptions = React__default.useMemo(() => {
        if (!searchValue)
            return baseFilteredOptions;
        // Re-filter using custom filterFn
        const refiltered = selectOptions.filter((option) => filterFn(option, searchValue));
        // Check if we should show create option
        const shouldShowCreate = creatable &&
            searchValue.trim() !== "" &&
            !refiltered.some((option) => option.label.toLowerCase() === searchValue.toLowerCase());
        if (shouldShowCreate) {
            const createOptionLabel = typeof createLabel === "function"
                ? createLabel(searchValue)
                : createLabel || `Create "${searchValue}"`;
            return [
                {
                    id: "__create__",
                    label: createOptionLabel,
                    value: searchValue,
                },
                ...refiltered,
            ];
        }
        return refiltered;
    }, [
        searchValue,
        selectOptions,
        filterFn,
        creatable,
        createLabel,
        baseFilteredOptions,
    ]);
    // Derived state
    const isDisabled = state === "disabled";
    const isInvalid = state === "invalid";
    // Sync input value with selected value when closed
    useEffect(() => {
        if (!isOpen && selectedOption) {
            setSearchValue(selectedOption.label);
        }
        else if (!isOpen && !value) {
            setSearchValue("");
        }
    }, [isOpen, selectedOption, value, setSearchValue]);
    // Reset highlighted index when options change
    useEffect(() => {
        setHighlightedIndex(-1);
    }, [customFilteredOptions.length, searchValue, setHighlightedIndex]);
    // Handle clear
    const handleClear = useCallback((e) => {
        var _a;
        e.stopPropagation();
        e.preventDefault();
        onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
        setSearchValue("");
        setIsOpen(false);
        (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
    }, [onChange, setSearchValue, setIsOpen]);
    // Handle input change
    const handleInputChange = useCallback((e) => {
        const newValue = e.target.value;
        setSearchValue(newValue);
        // Open dropdown when typing
        if (!isOpen && newValue) {
            setIsOpen(true);
        }
        // If input is cleared, clear the value
        if (!newValue && value !== undefined) {
            onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
        }
    }, [isOpen, value, onChange, setSearchValue, setIsOpen]);
    // Handle input focus
    const handleInputFocus = useCallback(() => {
        // Skip reopening if we just selected an option
        if (justSelectedRef.current) {
            justSelectedRef.current = false;
            return;
        }
        if (openOnFocus && !isDisabled) {
            setIsOpen(true);
        }
    }, [openOnFocus, isDisabled, setIsOpen]);
    // Handle option selection
    const handleSelectOption = useCallback((optionValue, isCreateAction = false) => {
        var _a;
        selectOption(optionValue, isCreateAction);
        // Find the selected option to get its label
        const selected = customFilteredOptions.find((opt) => opt.value === optionValue);
        if (selected) {
            setSearchValue(selected.label);
        }
        setIsOpen(false);
        setHighlightedIndex(-1);
        // Prevent reopening on focus after selection
        justSelectedRef.current = true;
        (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
    }, [
        selectOption,
        customFilteredOptions,
        setSearchValue,
        setIsOpen,
        setHighlightedIndex,
    ]);
    // Handle keyboard navigation
    const handleKeyDown = useCallback((event) => {
        if (isDisabled)
            return;
        switch (event.key) {
            case "ArrowDown":
                event.preventDefault();
                if (!isOpen) {
                    setIsOpen(true);
                }
                else {
                    setHighlightedIndex((prev) => prev >= customFilteredOptions.length - 1 ? 0 : prev + 1);
                }
                break;
            case "ArrowUp":
                event.preventDefault();
                if (!isOpen) {
                    setIsOpen(true);
                }
                else {
                    setHighlightedIndex((prev) => prev <= 0 ? customFilteredOptions.length - 1 : prev - 1);
                }
                break;
            case "Enter":
                event.preventDefault();
                if (isOpen &&
                    highlightedIndex >= 0 &&
                    highlightedIndex < customFilteredOptions.length) {
                    const highlightedOption = customFilteredOptions[highlightedIndex];
                    const isCreateOption = highlightedOption.id === "__create__";
                    handleSelectOption(highlightedOption.value, isCreateOption);
                }
                else if (!isOpen) {
                    setIsOpen(true);
                }
                break;
            case "Escape":
                event.preventDefault();
                if (isOpen) {
                    setIsOpen(false);
                    // Restore selected value
                    if (selectedOption) {
                        setSearchValue(selectedOption.label);
                    }
                    else {
                        setSearchValue("");
                    }
                }
                break;
            case "Tab":
                if (isOpen) {
                    setIsOpen(false);
                    // Restore selected value
                    if (selectedOption) {
                        setSearchValue(selectedOption.label);
                    }
                    else {
                        setSearchValue("");
                    }
                }
                break;
        }
    }, [isDisabled, isOpen, customFilteredOptions, highlightedIndex, handleSelectOption, selectedOption, setIsOpen, setHighlightedIndex, setSearchValue]);
    // Scroll highlighted option into view
    useEffect(() => {
        if (highlightedIndex >= 0 && isOpen) {
            const optionElement = document.querySelector(`[data-option-index="${highlightedIndex}"]`);
            if (optionElement) {
                optionElement.scrollIntoView({ block: "nearest" });
            }
        }
    }, [highlightedIndex, isOpen]);
    // Theme classes
    const containerClasses = theme.containerStyle;
    const inputGroupClasses = theme.inputGroupStyle;
    const inputClasses = theme.inputStyle;
    const sizeClasses = theme.sizes[size];
    const stateClasses = theme.states[state];
    const helperTextClasses = theme.helperTextStyle;
    const helperTextStateClasses = ((_a = theme.helperTextStates) === null || _a === void 0 ? void 0 : _a[state]) || "";
    // State class for CSS selectors
    const stateClass = isInvalid
        ? "state-invalid"
        : isDisabled
            ? "state-disabled"
            : "state-default";
    // Show clear button
    const showClearButton = clearable && value !== undefined && !isDisabled;
    // Icon padding
    const hasLeftIcon = Boolean(iconStart);
    const iconStartPadding = hasLeftIcon ? theme.iconPadding.left : "";
    // Create a modified selectState for the provider with our custom filtered options
    const modifiedSelectState = React__default.useMemo(() => ({
        ...selectState,
        filteredOptions: customFilteredOptions,
        selectOption: handleSelectOption,
    }), [selectState, customFilteredOptions, handleSelectOption]);
    return (jsx(SelectProvider, { selectState: modifiedSelectState, size: size, componentState: state, theme: theme, searchable: true, enableTypeahead: false, children: jsxs("div", { ref: ref, className: cn("relative", containerClasses, stateClass, wrapperClassName), ...props, children: [label && (jsx(FormLabel, { htmlFor: comboboxId, required: required, children: label })), jsxs("div", { ref: wrapperRef, className: theme.wrapperStyle, children: [jsx("div", { className: cn(inputGroupClasses, stateClasses, className), children: jsxs("div", { className: "relative flex-1", children: [jsx("input", { ref: inputRef, id: comboboxId, type: "text", role: "combobox", "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 &&
                                            customFilteredOptions[highlightedIndex]
                                            ? `${comboboxId}-option-${customFilteredOptions[highlightedIndex].id}`
                                            : undefined, "aria-autocomplete": "list", "aria-invalid": isInvalid, autoComplete: "off", className: cn(inputClasses, sizeClasses, iconStartPadding, showClearButton && "pr-8", inputClassName), value: searchValue, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isDisabled, required: required }), hasLeftIcon && (jsx("div", { className: theme.iconStartStyle, children: iconStart })), showClearButton && (jsx("button", { type: "button", className: theme.clearButtonStyle, onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx(XMarkIcon, { className: theme.clearIconStyle }) }))] }) }), jsx(ComboboxOptions, { isOpen: isOpen, onClose: () => setIsOpen(false), triggerRef: wrapperRef, portal: portal, placement: placement, offset: offset, emptyMessage: emptyMessage, className: dropdownClassName })] }), helperText && (jsx("p", { className: cn(helperTextClasses, helperTextStateClasses), children: helperText })), name && (jsx("input", { type: "hidden", name: name, value: value !== undefined ? String(value) : "" }))] }) }));
};
const ComboboxForwarded = React__default.forwardRef(ComboboxInner);
ComboboxForwarded.displayName = "Combobox";
const Combobox = ComboboxForwarded;

export { Combobox };
