import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import React__default, { createContext, useMemo, useRef, useCallback, useEffect, useContext } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { createPortal } from 'react-dom';
import { selectTheme } from './Select.theme.js';
import { FormLabel } from '../FormLabel/FormLabel.js';
import { Input } from '../Input/Input.js';
import '../Input/Input.theme.js';
import { createDropdownAnimations } from './Select.animations.js';
import { ChevronUpDownIcon, PlusIcon, CheckIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useSelectPortal } from '../../../hooks/useSelectPortal.js';
import { cn } from '../../../utils/cn.js';
import { isValidTypeaheadChar, findNextMatchingOption, TYPEAHEAD_TIMEOUT_MS } from '../../../utils/typeaheadUtils.js';
import { Tag } from '../../core/Tag/Tag.js';
import { useIsClient } from '../../../hooks/useIsClient.js';
import { useSelectState } from './useSelectState.js';

// ============================================================================
// Dark Mode Hook
// ============================================================================
/**
 * 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();
        // Observe class changes on html and body
        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;
};
/**
 * SelectContext - exported for external consumption
 */
const SelectContext = createContext(undefined);
/**
 * useSelectContext - Hook to consume SelectContext
 * Throws if used outside of SelectProvider
 */
const useSelectContext = () => {
    const context = useContext(SelectContext);
    if (!context) {
        throw new Error("Select components must be used within a SelectProvider");
    }
    return context;
};
/**
 * SelectProvider - Provides select context to children
 *
 * This component allows building custom dropdown components using Select primitives.
 *
 * @example
 * ```tsx
 * const selectState = useSelectState({ options, value, onChange });
 *
 * <SelectProvider selectState={selectState} size="md">
 *   <MyCustomTrigger />
 *   <SelectOptions isOpen={selectState.isOpen}>
 *     {selectState.filteredOptions.map(option => (
 *       <SelectOption key={option.id} value={option.value}>
 *         {option.label}
 *       </SelectOption>
 *     ))}
 *   </SelectOptions>
 * </SelectProvider>
 * ```
 */
function SelectProvider({ children, selectState, size = "md", componentState = "default", theme: themeOverrides, searchable = false, enableTypeahead = true, }) {
    const theme = useMemo(() => ({ ...selectTheme, ...(themeOverrides || {}) }), [themeOverrides]);
    // Cast triggerRef to buttonRef for backward compatibility
    const buttonRef = selectState.triggerRef;
    const contextValue = useMemo(() => ({
        ...selectState,
        size,
        state: componentState,
        theme,
        searchable,
        enableTypeahead,
        buttonRef,
    }), [selectState, size, componentState, theme, searchable, enableTypeahead, buttonRef]);
    return (jsx(SelectContext.Provider, { value: contextValue, children: children }));
}
// ============================================================================
// SelectButton Component
// ============================================================================
/**
 * Select Button Component
 */
const SelectButton = ({ id, disabled, isOpen, state = "default", size = "md", iconStart, iconEnd, className = "", onClick, onFocus, onBlur, children, }) => {
    var _a, _b, _c, _d, _e, _f;
    const { theme, buttonRef } = useSelectContext();
    const baseStyle = theme.baseStyle || "";
    const sizeStyle = ((_a = theme.sizes) === null || _a === void 0 ? void 0 : _a[size]) || "";
    const stateStyle = ((_b = theme.states) === null || _b === void 0 ? void 0 : _b[state]) || "";
    const hoverStateStyle = ((_c = theme.hoverStates) === null || _c === void 0 ? void 0 : _c[state]) || "";
    const focusStateStyle = ((_d = theme.focusStates) === null || _d === void 0 ? void 0 : _d[state]) || "";
    const buttonWrapperStyle = theme.buttonWrapperStyle || "";
    const buttonStyle = theme.buttonStyle || "";
    const iconStartStyle = theme.iconStartStyle || "";
    const iconEndStyle = theme.iconEndStyle || "";
    const iconStartPadding = iconStart ? ((_e = theme.iconPadding) === null || _e === void 0 ? void 0 : _e.left) || "" : "";
    const iconEndPadding = iconEnd ? ((_f = theme.iconPadding) === null || _f === void 0 ? void 0 : _f.right) || "" : "";
    // Use either the themed focus state or directly apply the focus classes if the theme is not working
    const activeFocusState = isOpen
        ? focusStateStyle ||
            "outline-2 outline-[var(--color-primary)] -outline-offset-1 ring-4 ring-[var(--color-primary)]/10"
        : "";
    return (jsx("div", { className: cn(buttonWrapperStyle), children: jsxs("button", { ref: buttonRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconStartPadding, iconEndPadding, buttonStyle, className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": isOpen && id ? `${id}-listbox` : undefined, children: [iconStart && jsx("span", { className: iconStartStyle, children: iconStart }), children, iconEnd && jsx("span", { className: iconEndStyle, children: iconEnd })] }) }));
};
/**
 * Select Options Component
 */
const SelectOptions = ({ isOpen, maxHeight = "15rem", className = "", onClose, children, portal = true, placement = "bottom-start", offset = 4, triggerRef: customTriggerRef, }) => {
    var _a;
    const context = useSelectContext();
    const { optionsRef, buttonRef, highlightedIndex, setHighlightedIndex, filteredOptions, searchable, searchValue, setSearchValue, enableTypeahead, typeaheadValue, setTypeaheadValue, typeaheadTimeoutRef, theme, selectOption, } = context;
    // Use custom triggerRef if provided, otherwise fall back to buttonRef
    const effectiveTriggerRef = customTriggerRef || buttonRef;
    // Detect dark mode for portal content
    const isDarkMode = useDarkMode();
    const isClient = useIsClient();
    const searchInputRef = useRef(null);
    // Use enhanced dropdown positioning hook with direction-aware animations
    const triggerWidth = ((_a = effectiveTriggerRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
    const { position, cssVariables, contentRef, isFlipped } = useSelectPortal({
        triggerRef: effectiveTriggerRef,
        isOpen: isOpen || false,
        placement,
        offset,
        estimatedWidth: triggerWidth,
    });
    // Create direction-aware animations based on actual placement
    const animations = createDropdownAnimations(isFlipped);
    const optionsContainerStyle = theme.optionsContainerStyle || "";
    const searchInputStyle = theme.searchInputStyle || "";
    // Convert maxHeight to a valid CSS value
    const maxHeightValue = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
    // Combine refs for portal and legacy usage
    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 to close options
    useEffect(() => {
        if (!isOpen)
            return;
        const handleClickOutside = (event) => {
            const currentContentRef = portal
                ? contentRef.current
                : optionsRef.current;
            if (currentContentRef &&
                !currentContentRef.contains(event.target) &&
                effectiveTriggerRef.current &&
                !effectiveTriggerRef.current.contains(event.target)) {
                onClose === null || onClose === void 0 ? void 0 : onClose();
            }
        };
        document.addEventListener("mousedown", handleClickOutside);
        return () => {
            document.removeEventListener("mousedown", handleClickOutside);
        };
    }, [isOpen, onClose, optionsRef, effectiveTriggerRef, contentRef, portal]);
    // Handle keyboard navigation
    useEffect(() => {
        if (!isOpen)
            return;
        const handleKeyDown = (event) => {
            // Handle typeahead if enabled and not in search input
            if (enableTypeahead && isValidTypeaheadChar(event.key)) {
                // Only do typeahead when not typing in search input
                if (!(event.target instanceof HTMLInputElement)) {
                    event.preventDefault();
                    // Clear existing timeout
                    if (typeaheadTimeoutRef.current) {
                        clearTimeout(typeaheadTimeoutRef.current);
                    }
                    // Build search string
                    const newTypeaheadValue = typeaheadValue + event.key.toLowerCase();
                    setTypeaheadValue(newTypeaheadValue);
                    // Find matching option
                    const matchingIndex = findNextMatchingOption({
                        options: filteredOptions,
                        searchChar: event.key.toLowerCase(),
                        currentIndex: highlightedIndex,
                        previousSearch: typeaheadValue,
                    });
                    if (matchingIndex >= 0) {
                        setHighlightedIndex(matchingIndex);
                        // Scroll to highlighted option
                        setTimeout(() => {
                            var _a;
                            const optionElement = (_a = optionsRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-option-index="${matchingIndex}"]`);
                            if (optionElement) {
                                optionElement.scrollIntoView({ block: "nearest" });
                            }
                        }, 0);
                    }
                    // Set timeout to reset typeahead
                    typeaheadTimeoutRef.current = setTimeout(() => {
                        setTypeaheadValue("");
                    }, TYPEAHEAD_TIMEOUT_MS);
                    return;
                }
            }
            // Handle navigation keys
            if (!["ArrowUp", "ArrowDown", "Enter", "Escape", "Tab"].includes(event.key))
                return;
            if (event.target instanceof HTMLInputElement) {
                // Don't navigate with arrow keys when typing in search input
                if (["ArrowUp", "ArrowDown"].includes(event.key)) {
                    return;
                }
            }
            event.preventDefault();
            if (event.key === "Escape" || event.key === "Tab") {
                onClose === null || onClose === void 0 ? void 0 : onClose();
                return;
            }
            if (event.key === "ArrowUp") {
                setHighlightedIndex((prev) => prev <= 0 ? filteredOptions.length - 1 : prev - 1);
            }
            else if (event.key === "ArrowDown") {
                setHighlightedIndex((prev) => prev >= filteredOptions.length - 1 ? 0 : prev + 1);
            }
            else if (event.key === "Enter") {
                if (highlightedIndex >= 0 &&
                    highlightedIndex < filteredOptions.length) {
                    const highlightedOption = filteredOptions[highlightedIndex];
                    if (!highlightedOption.disabled) {
                        const isCreateAction = highlightedOption.id === "__create__";
                        selectOption(highlightedOption.value, isCreateAction);
                        onClose === null || onClose === void 0 ? void 0 : onClose();
                    }
                }
            }
        };
        document.addEventListener("keydown", handleKeyDown);
        return () => {
            document.removeEventListener("keydown", handleKeyDown);
        };
    }, [
        isOpen,
        onClose,
        highlightedIndex,
        filteredOptions,
        setHighlightedIndex,
        enableTypeahead,
        typeaheadValue,
        setTypeaheadValue,
        typeaheadTimeoutRef,
        optionsRef,
        selectOption,
    ]);
    // Autofocus search input on open for searchable selects
    useEffect(() => {
        if (isOpen && searchable && searchInputRef.current) {
            searchInputRef.current.focus({ preventScroll: true });
            searchInputRef.current.select();
        }
    }, [isOpen, searchable]);
    // Add effect to measure content after render for improved positioning
    useEffect(() => {
        if (isOpen && contentRef.current) {
            // Trigger recalculation after content renders
            const timer = setTimeout(() => {
                // Hook will remeasure automatically due to contentRef dependency
            }, 0);
            return () => clearTimeout(timer);
        }
    }, [isOpen, children, filteredOptions.length]);
    // Handle search input change
    const handleSearchChange = (e) => {
        setSearchValue(e.target.value);
        setHighlightedIndex(-1); // Reset highlighted index when search changes
    };
    const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsxs(motion.div, { ref: combinedRef, className: cn(optionsContainerStyle, className), style: {
                ...cssVariables,
                overscrollBehavior: "contain",
                ...(portal
                    ? {
                        top: `${position.top}px`,
                        left: `${position.left}px`,
                        width: `${position.width}px`,
                        maxHeight: maxHeightValue,
                    }
                    : {
                        maxHeight: searchable ? "none" : maxHeightValue,
                    }),
            }, role: "listbox", tabIndex: -1, variants: animations, initial: "initial", animate: "visible", exit: "exit", children: [searchable && (jsx("div", { className: `sticky top-0 py-1 bg-[var(--color-background)] z-[10000] border-b border-[var(--color-border)] ${searchInputStyle}`, style: {
                        "--form-radius": "0",
                    }, children: jsx(Input, { placeholder: "Search...", "aria-label": "Search options", value: searchValue, onChange: handleSearchChange, type: "search", ref: searchInputRef, className: "w-full block bg-[var(--color-background)] border-none outline-none !min-h-8 py-2 ring-0 shadow-none [&:focus]:outline-none [&:focus]:ring-0 [&:focus]:border-none [&:focus-visible]:outline-none [&:focus-visible]:ring-0 [&:focus-visible]:border-none", iconStart: jsx(MagnifyingGlassIcon, { strokeWidth: 2, className: "size-4 text-[var(--color-text-muted)] group-focus-within:text-[var(--color-primary)]" }), wrapperClassName: "!border-none !outline-none !ring-0 !shadow-none", inputGroupClassName: "!border-none !outline-none !ring-0 !shadow-none flex items-center", style: {
                            border: "none",
                            outline: "none",
                            boxShadow: "none",
                        } }) })), children] })) }));
    // Use portal if enabled and client-side - wrap with dark class for dark mode support
    if (portal && isClient) {
        return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: optionsContent }), document.body);
    }
    return optionsContent;
};
// ============================================================================
// SelectOption Component
// ============================================================================
/**
 * Select Option Component
 */
const SelectOption = ({ value, isSelected, disabled, className = "", onSelect, children, }) => {
    var _a;
    const { highlightedIndex, setHighlightedIndex, filteredOptions, theme, selectOption, } = useSelectContext();
    const optionStyle = theme.optionStyle || "";
    const selectedOptionStyle = theme.selectedOptionStyle || "";
    const disabledOptionStyle = theme.disabledOptionStyle || "";
    const highlightedOptionStyle = theme.highlightedOptionStyle || "";
    const createOptionStyleTheme = theme.createOptionStyle || "";
    // Find index of this option in filteredOptions (not options, to support create option)
    const optionIndex = filteredOptions.findIndex((option) => option.value === value);
    const isHighlighted = optionIndex === highlightedIndex;
    // Check if this is the create option
    const isCreateOption = ((_a = filteredOptions[optionIndex]) === null || _a === void 0 ? void 0 : _a.id) === "__create__";
    const handleClick = () => {
        if (disabled)
            return;
        selectOption(value, isCreateOption);
        if (onSelect) {
            onSelect(value);
        }
    };
    // Apply the hover style directly when keyboard navigating
    const keyboardHighlightStyle = isHighlighted ? highlightedOptionStyle : "";
    // Style create option differently
    const createOptionStyle = isCreateOption ? createOptionStyleTheme : "";
    return (jsx("div", { className: cn(optionStyle, isSelected && selectedOptionStyle, disabled && disabledOptionStyle, keyboardHighlightStyle, createOptionStyle, className), role: "option", "aria-selected": isSelected, "aria-disabled": disabled, "data-option-index": optionIndex, onMouseEnter: () => !disabled && setHighlightedIndex(optionIndex), onClick: handleClick, children: children }));
};
// ============================================================================
// Main Select Component
// ============================================================================
/**
 * Main Select Component
 *
 * A fully-featured select/dropdown component with support for:
 * - Keyboard navigation
 * - Search/filtering
 * - Typeahead
 * - Creatable options
 * - Portal rendering
 * - Custom theming
 *
 * @example
 * ```tsx
 * <Select
 *   options={[
 *     { id: "1", label: "Option 1", value: "option1" },
 *     { id: "2", label: "Option 2", value: "option2" },
 *   ]}
 *   value={selected}
 *   onChange={setSelected}
 *   placeholder="Select an option"
 * />
 * ```
 */
const SelectInner = ({ id, name, size = "md", state = "default", value, displayValue, placeholder = "Select an option", disabled = false, required = false, options = [], label, helperText, onChange, onFocus, onBlur, iconStart, iconEnd, theme: themeOverrides, className = "", wrapperClassName = "", searchable = false, searchPlaceholder = "Search options...", portal = true, placement = "bottom-start", offset = 8, enableTypeahead = true, creatable = false, onCreateOption, createLabel, multiple = false, maxDisplayedChips = 3, children, }, ref) => {
    var _a;
    // Merge theme with overrides
    const theme = useMemo(() => ({ ...selectTheme, ...(themeOverrides || {}) }), [themeOverrides]);
    // Use the extracted state hook
    const selectState = useSelectState({
        options,
        value,
        onChange,
        searchable,
        creatable,
        onCreateOption,
        createLabel,
        enableTypeahead,
        multiple,
    });
    const { isOpen, setIsOpen, selectedValue, filteredOptions, selectedOption, selectedValues, isValueSelected, removeValue, } = selectState;
    // Cast triggerRef to buttonRef type
    selectState.triggerRef;
    // Build display for multi-select chips
    const multiSelectDisplay = useMemo(() => {
        if (!multiple || selectedValues.length === 0)
            return null;
        const selectedOpts = selectedValues
            .map((v) => options.find((o) => o.value === v))
            .filter(Boolean);
        const displayed = selectedOpts.slice(0, maxDisplayedChips);
        const overflow = selectedOpts.length - maxDisplayedChips;
        return (jsxs("div", { className: theme.chipContainerStyle, children: [jsx(AnimatePresence, { mode: "popLayout", children: displayed.map((opt) => (jsx(Tag, { size: "sm", onRemove: () => removeValue(opt.value), className: "shrink-0 max-w-[120px] truncate rounded-md border-none", children: opt.label }, String(opt.id)))) }), overflow > 0 && (jsxs("span", { className: theme.chipOverflowStyle, children: ["+", overflow, " more"] }))] }));
    }, [multiple, selectedValues, options, maxDisplayedChips, theme.chipContainerStyle, theme.chipOverflowStyle, removeValue]);
    const computedDisplayValue = displayValue ||
        (multiple
            ? multiSelectDisplay || placeholder
            : selectedOption
                ? selectedOption.label
                : placeholder);
    // Button click handler
    const handleButtonClick = () => {
        if (!disabled) {
            setIsOpen((prev) => !prev);
        }
    };
    // Helper text state style
    const helperTextStateStyle = ((_a = theme.helperTextStates) === null || _a === void 0 ? void 0 : _a[state]) || "";
    const helperTextStyle = theme.helperTextStyle || "";
    // Container style
    const containerStyle = theme.containerStyle || "";
    // Default chevron icon if no iconEnd provided
    const defaultChevronIcon = jsx(ChevronUpDownIcon, { className: "size-4" });
    // Generate a unique ID if not provided
    const generatedId = React__default.useId();
    const selectId = id || generatedId;
    return (jsx(SelectProvider, { selectState: selectState, size: size, componentState: state, theme: themeOverrides, searchable: searchable, enableTypeahead: enableTypeahead, children: jsxs("div", { ref: ref, className: cn("relative", containerStyle, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: selectId, required: required, children: label })) : (label)), jsx(SelectButton, { id: selectId, disabled: disabled, isOpen: isOpen, state: state, size: size, iconStart: iconStart, iconEnd: iconEnd || defaultChevronIcon, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, children: jsx("span", { className: cn(multiple && selectedValues.length > 0
                            ? "flex min-w-0"
                            : "block truncate", (multiple ? selectedValues.length > 0 : selectedOption)
                            ? theme.valueTextStyle
                            : theme.placeholderTextStyle), children: computedDisplayValue }) }), jsx(SelectOptions, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, children: children ||
                        filteredOptions.map((option) => {
                            const isCreateOption = option.id === "__create__";
                            const isSelected = multiple
                                ? isValueSelected(option.value)
                                : option.value === selectedValue;
                            return (jsx(SelectOption, { value: option.value, isSelected: isSelected, disabled: option.disabled || false, children: isCreateOption ? (jsxs("span", { className: "flex items-center gap-2", children: [jsx(PlusIcon, { className: "size-4 shrink-0" }), jsx("span", { className: "block truncate", children: option.label })] })) : multiple ? (jsxs(Fragment, { children: [jsx("span", { className: "block truncate", children: option.label }), isSelected && (jsx("span", { className: theme.checkIconStyle, children: jsx(CheckIcon, { className: "size-4", strokeWidth: 2 }) }))] })) : (jsx("span", { className: "block truncate", children: option.label })) }, option.id));
                        }) }), helperText && (typeof helperText === 'string' ? (jsx("p", { className: cn(helperTextStyle, helperTextStateStyle), children: helperText })) : (helperText)), name && (jsx("input", { type: "hidden", name: name, value: selectedValue !== undefined ? String(selectedValue) : "" }))] }) }));
};
const SelectForwarded = React__default.forwardRef(SelectInner);
SelectForwarded.displayName = "Select";
const Select = SelectForwarded;

export { Select, SelectButton, SelectContext, SelectOption, SelectOptions, SelectProvider, useSelectContext, useSelectState };
