'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * Chip-style tag editor with autocomplete from values already in use.
 *
 * Suggestions come from `GET /collections/:collection/field-values?field=...`
 * (distinct values across the collection, most-used first) and are fetched
 * once on first focus, then filtered client-side as the user types.
 *
 * Deduplication is case-insensitive and resolves to the existing canonical
 * casing: typing "ai" when the collection already uses "AI" adds "AI", so a
 * vocabulary never splinters into `AI` / `ai` / `Ai` variants.
 */
import { useCallback, useEffect, useRef, useState } from 'react';
import { X } from 'lucide-react';
import { cmsApi } from '../lib/api.js';
const INPUT = 'border-input bg-input-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-sm focus-visible:ring-2 focus-visible:outline-none';
export function TagInput({ id, value, onChange, collection, field = 'tags', disabled, placeholder, maxTags, }) {
    const [draft, setDraft] = useState('');
    const [known, setKnown] = useState(null);
    const [open, setOpen] = useState(false);
    const [activeIndex, setActiveIndex] = useState(-1);
    const rootRef = useRef(null);
    const atCap = typeof maxTags === 'number' && value.length >= maxTags;
    // Fetch the in-use vocabulary once, on first focus.
    const ensureSuggestions = useCallback(() => {
        if (known !== null || !collection)
            return;
        setKnown([]); // sentinel: fetch in flight, don't refetch
        cmsApi(`/collections/${encodeURIComponent(collection)}/field-values?field=${encodeURIComponent(field)}`).then((res) => {
            if (res.data?.values)
                setKnown(res.data.values.map((v) => v.value));
        });
    }, [known, collection, field]);
    // Close the suggestion list on outside click.
    useEffect(() => {
        if (!open)
            return;
        function onPointerDown(e) {
            if (rootRef.current && !rootRef.current.contains(e.target))
                setOpen(false);
        }
        document.addEventListener('pointerdown', onPointerDown);
        return () => document.removeEventListener('pointerdown', onPointerDown);
    }, [open]);
    const lowerExisting = new Set(value.map((t) => t.toLowerCase()));
    const matches = (known ?? [])
        .filter((s) => !lowerExisting.has(s.toLowerCase()))
        .filter((s) => !draft.trim() || s.toLowerCase().includes(draft.trim().toLowerCase()))
        .slice(0, 8);
    function addTag(raw) {
        const tag = raw.trim();
        if (!tag || atCap)
            return;
        const lower = tag.toLowerCase();
        if (lowerExisting.has(lower)) {
            setDraft('');
            return;
        }
        // Prefer the canonical casing already used elsewhere in the collection.
        const canonical = (known ?? []).find((s) => s.toLowerCase() === lower) ?? tag;
        onChange([...value, canonical]);
        setDraft('');
        setActiveIndex(-1);
    }
    function removeTag(index) {
        onChange(value.filter((_, i) => i !== index));
    }
    function handleKeyDown(e) {
        if (e.key === 'Enter' || e.key === ',') {
            e.preventDefault();
            if (activeIndex >= 0 && activeIndex < matches.length)
                addTag(matches[activeIndex]);
            else
                addTag(draft);
        }
        else if (e.key === 'ArrowDown') {
            e.preventDefault();
            setOpen(true);
            setActiveIndex((i) => Math.min(i + 1, matches.length - 1));
        }
        else if (e.key === 'ArrowUp') {
            e.preventDefault();
            setActiveIndex((i) => Math.max(i - 1, -1));
        }
        else if (e.key === 'Escape') {
            setOpen(false);
            setActiveIndex(-1);
        }
        else if (e.key === 'Backspace' && !draft && value.length > 0) {
            removeTag(value.length - 1);
        }
    }
    return (_jsxs("div", { ref: rootRef, className: "relative", children: [value.length > 0 && (_jsx("div", { className: "mb-1.5 flex flex-wrap gap-1.5", children: value.map((tag, i) => (_jsxs("span", { className: "bg-accent text-foreground inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs", children: [tag, !disabled && (_jsx("button", { type: "button", "aria-label": `Remove tag ${tag}`, onClick: () => removeTag(i), className: "text-muted-foreground hover:text-destructive rounded p-0.5", children: _jsx(X, { className: "h-3 w-3", "aria-hidden": true }) }))] }, `${tag}-${i}`))) })), _jsx("input", { id: id, className: INPUT, role: "combobox", "aria-expanded": open && matches.length > 0, "aria-autocomplete": "list", "aria-controls": `${id}-listbox`, value: draft, disabled: disabled || atCap, placeholder: atCap ? `Maximum of ${maxTags} tags` : (placeholder ?? 'Add a tag…'), onFocus: () => {
                    ensureSuggestions();
                    setOpen(true);
                }, onChange: (e) => {
                    setDraft(e.target.value);
                    setOpen(true);
                    setActiveIndex(-1);
                }, onKeyDown: handleKeyDown }), open && matches.length > 0 && !disabled && (_jsx("ul", { id: `${id}-listbox`, role: "listbox", "aria-label": "Tag suggestions", className: "border-border bg-card absolute z-20 mt-1 max-h-44 w-full overflow-y-auto rounded-md border py-1 shadow-md", children: matches.map((s, i) => (_jsx("li", { role: "option", "aria-selected": i === activeIndex, children: _jsx("button", { type: "button", className: `w-full px-2.5 py-1.5 text-left text-sm ${i === activeIndex
                            ? 'bg-accent text-foreground'
                            : 'text-foreground hover:bg-accent'}`, onMouseEnter: () => setActiveIndex(i), onClick: () => addTag(s), children: s }) }, s))) }))] }));
}
//# sourceMappingURL=TagInput.js.map