import { jsxs, jsx } from 'react/jsx-runtime';
import React__default, { useMemo, useState, useRef, useEffect, useCallback } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { createPortal } from 'react-dom';
import { TagIcon, PlusIcon, CheckIcon } from '@heroicons/react/24/outline';
import { cn } from '../../../utils/cn.js';
import { Input } from '../Input/Input.js';
import '../Input/Input.theme.js';
import { Tag } from '../../core/Tag/Tag.js';
import { inputTagTheme } from './InputTag.theme.js';
import { useSelectPortal } from '../../../hooks/useSelectPortal.js';
import { createDropdownAnimations } from '../Select/Select.animations.js';
import { useIsClient } from '../../../hooks/useIsClient.js';

/**
 * Hook to detect dark mode state from document
 */
const useDarkMode = () => {
    const [isDark, setIsDark] = 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 suggestions
 */
const defaultFilterFn = (option, inputValue) => {
    return option.label.toLowerCase().includes(inputValue.toLowerCase());
};
const InputTagOptions = ({ isOpen, onClose, triggerRef, filteredOptions, selectedTags, highlightedIndex, setHighlightedIndex, onSelectOption, showSelectedIndicator, portal, placement, offset, emptyMessage, className, theme, }) => {
    var _a;
    const optionsRef = useRef(null);
    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) => {
        optionsRef.current =
            el;
        if (contentRef &&
            typeof contentRef === "object" &&
            "current" in contentRef) {
            contentRef.current = el;
        }
    }, [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]);
    const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsx(motion.div, { ref: combinedRef, role: "listbox", className: cn(theme.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: theme.emptyMessageStyle, children: emptyMessage })) : (filteredOptions.map((option, index) => {
                const isSelected = selectedTags.includes(option.value);
                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(theme.optionStyle, isHighlighted && theme.highlightedOptionStyle, option.disabled && theme.disabledOptionStyle, isCreateOption && theme.createOptionStyle), onMouseEnter: () => !option.disabled && setHighlightedIndex(index), onClick: () => {
                        if (!option.disabled) {
                            onSelectOption(option, isCreateOption);
                        }
                    }, 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 })] })) : (jsxs("span", { className: "flex items-center justify-between gap-2", children: [jsx("span", { className: "block truncate", children: option.label }), showSelectedIndicator && isSelected && (jsx(CheckIcon, { className: "size-4 shrink-0 text-[var(--color-primary)]", strokeWidth: 2 }))] })) }, option.id));
            })) })) }));
    if (portal && isClient) {
        return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: optionsContent }), document.body);
    }
    return optionsContent;
};
// ============================================================================
// Main InputTag Component
// ============================================================================
/**
 * InputTag component for entering multiple tags/keywords.
 *
 * Supports:
 * - Enter key to add tag
 * - Comma to add tag
 * - Paste multiple comma-separated values
 * - Backspace to remove last tag (when input is empty)
 * - Click X to remove individual tags
 * - Optional dropdown suggestions with filtering
 *
 * @component
 * @example
 * ```tsx
 * // Basic free-form tags (uncontrolled)
 * <InputTag
 *   defaultValue={['JavaScript', 'React']}
 *   onChange={(tags) => console.log(tags)}
 * />
 *
 * // Controlled with max tags
 * <InputTag
 *   value={tags}
 *   onChange={setTags}
 *   maxTags={5}
 * />
 *
 * // With suggestions dropdown
 * <InputTag
 *   suggestions={[
 *     { id: '1', label: 'React', value: 'react' },
 *     { id: '2', label: 'Vue', value: 'vue' },
 *   ]}
 *   creatable={true}
 * />
 * ```
 */
const InputTag = React__default.forwardRef(({ value, defaultValue = [], onChange, maxTags, allowDuplicates = false, size = "md", state = "default", label, helperText, placeholder = "Add a tag...", disabled = false, className, inputClassName, tagsContainerClassName, tagClassName, theme: customTheme, 
// Dropdown props
suggestions, creatable = true, openOnFocus = true, filterFn = defaultFilterFn, emptyMessage = "No suggestions", createLabel, hideSelected = true, showSelectedIndicator = false, portal = true, placement = "bottom-start", offset = 8, dropdownClassName, ...props }, ref) => {
    // Merge default theme with custom theme overrides
    const theme = useMemo(() => ({
        ...inputTagTheme,
        ...(customTheme || {}),
    }), [customTheme]);
    // Determine if dropdown mode is enabled
    const hasDropdown = suggestions !== undefined && suggestions.length > 0;
    // Internal state for tags (controlled/uncontrolled pattern)
    const [internalTags, setInternalTags] = useState(defaultValue || []);
    const [inputValue, setInputValue] = useState("");
    const [duplicateWarning, setDuplicateWarning] = useState(null);
    const [isOpen, setIsOpen] = useState(false);
    const [highlightedIndex, setHighlightedIndex] = useState(-1);
    const inputRef = useRef(null);
    const wrapperRef = useRef(null);
    const warningTimeoutRef = useRef(null);
    // Determine if component is controlled
    const isControlled = value !== undefined;
    const tags = isControlled ? value : internalTags;
    // Sync internal state when controlled value changes
    useEffect(() => {
        if (isControlled) {
            setInternalTags(value);
        }
    }, [isControlled, value]);
    // Cleanup warning timeout on unmount
    useEffect(() => {
        return () => {
            if (warningTimeoutRef.current) {
                clearTimeout(warningTimeoutRef.current);
            }
        };
    }, []);
    // Filter suggestions based on input and already-selected tags
    const filteredOptions = useMemo(() => {
        if (!hasDropdown || !suggestions)
            return [];
        let options = suggestions;
        // Filter by input value
        if (inputValue.trim()) {
            options = options.filter((opt) => filterFn(opt, inputValue));
        }
        // Hide already selected tags
        if (hideSelected) {
            options = options.filter((opt) => !tags.includes(opt.value));
        }
        // Check if we should show create option
        const shouldShowCreate = creatable &&
            inputValue.trim() !== "" &&
            !options.some((opt) => opt.label.toLowerCase() === inputValue.toLowerCase()) &&
            !tags.includes(inputValue.trim());
        if (shouldShowCreate) {
            const createOptionLabel = typeof createLabel === "function"
                ? createLabel(inputValue)
                : createLabel || `Create "${inputValue}"`;
            return [
                {
                    id: "__create__",
                    label: createOptionLabel,
                    value: inputValue.trim(),
                },
                ...options,
            ];
        }
        return options;
    }, [
        hasDropdown,
        suggestions,
        inputValue,
        tags,
        hideSelected,
        creatable,
        createLabel,
        filterFn,
    ]);
    // Reset highlighted index when options change
    useEffect(() => {
        setHighlightedIndex(-1);
    }, [filteredOptions.length, inputValue]);
    // Show duplicate warning temporarily
    const showDuplicateWarning = useCallback((tagName) => {
        if (warningTimeoutRef.current) {
            clearTimeout(warningTimeoutRef.current);
        }
        setDuplicateWarning(`"${tagName}" already exists`);
        warningTimeoutRef.current = setTimeout(() => {
            setDuplicateWarning(null);
        }, 2000);
    }, []);
    // Update tags (handles both controlled and uncontrolled modes)
    const updateTags = useCallback((newTags) => {
        if (!isControlled) {
            setInternalTags(newTags);
        }
        onChange === null || onChange === void 0 ? void 0 : onChange(newTags);
    }, [isControlled, onChange]);
    // Add a single tag
    const addTag = useCallback((tagValue) => {
        const trimmedTag = tagValue.trim();
        if (!trimmedTag)
            return false;
        // Check for max tags
        if (maxTags && tags.length >= maxTags)
            return false;
        // Check for duplicates
        if (!allowDuplicates && tags.includes(trimmedTag)) {
            showDuplicateWarning(trimmedTag);
            return false;
        }
        updateTags([...tags, trimmedTag]);
        return true;
    }, [tags, maxTags, allowDuplicates, updateTags, showDuplicateWarning]);
    // Remove a tag by index
    const removeTag = useCallback((index) => {
        const newTags = tags.filter((_, i) => i !== index);
        updateTags(newTags);
    }, [tags, updateTags]);
    // Handle input change
    const handleInputChange = useCallback((e) => {
        const newValue = e.target.value;
        setInputValue(newValue);
        // Clear duplicate warning when user starts typing
        if (duplicateWarning) {
            setDuplicateWarning(null);
            if (warningTimeoutRef.current) {
                clearTimeout(warningTimeoutRef.current);
            }
        }
        // Open dropdown when typing (if suggestions mode)
        if (hasDropdown && newValue && !isOpen) {
            setIsOpen(true);
        }
    }, [duplicateWarning, hasDropdown, isOpen]);
    // Add all tags from input (handles comma-separated values)
    const addTagsFromInput = useCallback(() => {
        if (!inputValue.trim())
            return false;
        // Split by comma and collect valid tags
        const newTagsToAdd = inputValue
            .split(",")
            .map((t) => t.trim())
            .filter(Boolean);
        // Filter out duplicates and respect max tags
        const validTags = [];
        const duplicateTags = [];
        for (const tag of newTagsToAdd) {
            // Check for max tags
            if (maxTags && tags.length + validTags.length >= maxTags)
                break;
            // Check for duplicates (against both existing and new tags being added)
            if (!allowDuplicates &&
                (tags.includes(tag) || validTags.includes(tag))) {
                if (!duplicateTags.includes(tag)) {
                    duplicateTags.push(tag);
                }
                continue;
            }
            validTags.push(tag);
        }
        // Show warning for duplicates
        if (duplicateTags.length > 0) {
            showDuplicateWarning(duplicateTags[0]);
        }
        if (validTags.length > 0) {
            updateTags([...tags, ...validTags]);
            setInputValue("");
            return true;
        }
        // Clear input even if only duplicates were entered
        if (duplicateTags.length > 0) {
            setInputValue("");
        }
        return false;
    }, [
        inputValue,
        tags,
        maxTags,
        allowDuplicates,
        updateTags,
        showDuplicateWarning,
    ]);
    // Handle selecting an option from dropdown
    const handleSelectOption = useCallback((option, isCreate) => {
        var _a;
        const added = addTag(option.value);
        if (added) {
            setInputValue("");
            setIsOpen(false);
            setHighlightedIndex(-1);
            (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
        }
    }, [addTag]);
    // Handle key down
    const handleKeyDown = useCallback((e) => {
        // Handle dropdown navigation when open
        if (hasDropdown && isOpen && filteredOptions.length > 0) {
            switch (e.key) {
                case "ArrowDown":
                    e.preventDefault();
                    setHighlightedIndex((prev) => prev >= filteredOptions.length - 1 ? 0 : prev + 1);
                    return;
                case "ArrowUp":
                    e.preventDefault();
                    setHighlightedIndex((prev) => prev <= 0 ? filteredOptions.length - 1 : prev - 1);
                    return;
                case "Enter":
                    e.preventDefault();
                    if (highlightedIndex >= 0 &&
                        highlightedIndex < filteredOptions.length) {
                        const option = filteredOptions[highlightedIndex];
                        const isCreate = option.id === "__create__";
                        handleSelectOption(option, isCreate);
                    }
                    else if (creatable || !hasDropdown) {
                        // Add typed value if creatable
                        addTagsFromInput();
                    }
                    return;
                case "Escape":
                    e.preventDefault();
                    setIsOpen(false);
                    setHighlightedIndex(-1);
                    return;
                case "Tab":
                    setIsOpen(false);
                    setHighlightedIndex(-1);
                    return;
            }
        }
        // Standard tag input behavior
        if (e.key === "Enter") {
            e.preventDefault();
            if (hasDropdown && isOpen && highlightedIndex === -1 && creatable) {
                addTagsFromInput();
            }
            else if (!hasDropdown || creatable) {
                addTagsFromInput();
            }
        }
        else if (e.key === "Backspace" &&
            inputValue === "" &&
            tags.length > 0) {
            // Remove last tag when backspace is pressed on empty input
            removeTag(tags.length - 1);
        }
        else if (e.key === "ArrowDown" && hasDropdown && !isOpen) {
            // Open dropdown on arrow down
            e.preventDefault();
            setIsOpen(true);
        }
    }, [
        hasDropdown,
        isOpen,
        filteredOptions,
        highlightedIndex,
        creatable,
        handleSelectOption,
        addTagsFromInput,
        inputValue,
        tags.length,
        removeTag,
    ]);
    // Handle paste
    const handlePaste = useCallback((e) => {
        e.preventDefault();
        const pastedText = e.clipboardData.getData("text");
        // Split by comma and add each tag
        const pastedTags = pastedText
            .split(",")
            .map((t) => t.trim())
            .filter(Boolean);
        if (pastedTags.length > 0) {
            pastedTags.forEach((tag) => addTag(tag));
            setInputValue("");
        }
    }, [addTag]);
    // Handle focus
    const handleFocus = useCallback(() => {
        if (hasDropdown && openOnFocus && !disabled) {
            setIsOpen(true);
        }
    }, [hasDropdown, openOnFocus, disabled]);
    // Handle blur - add current input as tags (only if no dropdown or closed)
    const handleBlur = useCallback(() => {
        // Delay to allow click events on dropdown options
        setTimeout(() => {
            if (!hasDropdown || !isOpen) {
                if (creatable || !hasDropdown) {
                    addTagsFromInput();
                }
            }
        }, 150);
    }, [hasDropdown, isOpen, creatable, addTagsFromInput]);
    // Close dropdown handler
    const handleCloseDropdown = useCallback(() => {
        setIsOpen(false);
        setHighlightedIndex(-1);
    }, []);
    // Check if input should be disabled
    const isInputDisabled = disabled || (maxTags !== undefined && tags.length >= maxTags);
    // 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]);
    // Pre-compute occurrence map to avoid O(n^2) in .map()
    const occurrenceMap = useMemo(() => {
        const map = new Map();
        return tags.map(tag => {
            const count = map.get(tag) || 0;
            map.set(tag, count + 1);
            return count;
        });
    }, [tags]);
    // Helper text to display (for dropdown mode, we render it separately)
    const displayHelperText = duplicateWarning || helperText;
    return (jsxs("div", { ref: ref, className: cn("w-full", className), ...props, children: [jsxs("div", { ref: wrapperRef, className: "relative", children: [jsx(Input, { ref: inputRef, value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onPaste: handlePaste, onFocus: handleFocus, onBlur: handleBlur, iconStart: jsx(TagIcon, { className: "size-4" }), placeholder: maxTags && tags.length >= maxTags
                            ? `Maximum ${maxTags} tags reached`
                            : placeholder, size: size, state: state, label: label, 
                        // Only pass helperText to Input when NOT in dropdown mode
                        // Otherwise the dropdown positions below the helper text
                        helperText: hasDropdown ? undefined : displayHelperText, disabled: isInputDisabled, className: inputClassName, "aria-expanded": hasDropdown ? isOpen : undefined, "aria-haspopup": hasDropdown ? "listbox" : undefined, "aria-autocomplete": hasDropdown ? "list" : undefined }), hasDropdown && (jsx(InputTagOptions, { isOpen: isOpen, onClose: handleCloseDropdown, triggerRef: wrapperRef, filteredOptions: filteredOptions, selectedTags: tags, highlightedIndex: highlightedIndex, setHighlightedIndex: setHighlightedIndex, onSelectOption: handleSelectOption, showSelectedIndicator: showSelectedIndicator, portal: portal, placement: placement, offset: offset, emptyMessage: emptyMessage, className: dropdownClassName, theme: theme }))] }), hasDropdown && displayHelperText && (jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-1.5", children: displayHelperText })), jsx("div", { className: cn(theme.tagsContainerStyle, "empty:hidden", tagsContainerClassName), children: jsx(AnimatePresence, { mode: "popLayout", children: tags.map((tag, index) => {
                        return (jsx(Tag, { size: size, onRemove: () => removeTag(index), disabled: disabled, className: tagClassName, children: tag }, `${tag}-${occurrenceMap[index]}`));
                    }) }) })] }));
});
InputTag.displayName = "InputTag";

export { InputTag };
