"use client";
import { jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { useT } from "@open-mercato/shared/lib/i18n/context";
import { Button } from "../../primitives/button.js";
function normalizeOptions(input) {
  if (!Array.isArray(input)) return [];
  return input.map((option) => {
    if (typeof option === "string") {
      const trimmed = option.trim();
      if (!trimmed) return null;
      return { value: trimmed, label: trimmed };
    }
    const value = typeof option.value === "string" ? option.value.trim() : "";
    if (!value) return null;
    return {
      value,
      label: option.label?.trim() || value,
      description: option.description ?? null
    };
  }).filter((option) => !!option);
}
function areOptionsEqual(a, b) {
  if (a.length !== b.length) return false;
  return a.every((option, index) => {
    const next = b[index];
    return option.value === next.value && option.label === next.label && (option.description ?? null) === (next.description ?? null);
  });
}
function ComboboxInput({
  value,
  onChange,
  placeholder,
  suggestions,
  seedOptions,
  loadSuggestions,
  resolveLabel,
  resolveDescription,
  autoFocus,
  disabled = false,
  allowCustomValues = true
}) {
  const t = useT();
  const resolvedPlaceholder = placeholder ?? t("ui.inputs.comboboxInput.placeholder", "Type to search...");
  const loadingLabel = t("ui.inputs.comboboxInput.loading", "Loading suggestions\u2026");
  const [input, setInput] = React.useState("");
  const [asyncOptions, setAsyncOptions] = React.useState([]);
  const [resolvedOptions, setResolvedOptions] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [touched, setTouched] = React.useState(false);
  const [showSuggestions, setShowSuggestions] = React.useState(false);
  const [selectedIndex, setSelectedIndex] = React.useState(-1);
  const inputRef = React.useRef(null);
  const suppressOpenOnFocusRef = React.useRef(Boolean(autoFocus && !disabled));
  const eagerFallbackLoadedValueRef = React.useRef(null);
  const staticOptions = React.useMemo(
    () => normalizeOptions([...seedOptions ?? [], ...suggestions ?? []]),
    [seedOptions, suggestions]
  );
  const knownLabelValues = React.useMemo(() => {
    const set = /* @__PURE__ */ new Set();
    for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {
      if (option.label && option.label !== option.value) set.add(option.value);
    }
    return set;
  }, [staticOptions, asyncOptions, resolvedOptions]);
  const coveredOptionValues = React.useMemo(() => {
    const set = /* @__PURE__ */ new Set();
    for (const option of [...staticOptions, ...asyncOptions, ...resolvedOptions]) {
      set.add(option.value);
    }
    return set;
  }, [staticOptions, asyncOptions, resolvedOptions]);
  const optionMap = React.useMemo(() => {
    const map = /* @__PURE__ */ new Map();
    const register = (option) => {
      const existing = map.get(option.value);
      if (!existing || existing.label === existing.value && option.label !== option.value) {
        map.set(option.value, option);
      }
    };
    staticOptions.forEach(register);
    asyncOptions.forEach(register);
    resolvedOptions.forEach(register);
    if (value) {
      const existing = map.get(value);
      if (!existing) {
        map.set(value, {
          value,
          label: value,
          description: resolveDescription?.(value) ?? null
        });
      }
    }
    return map;
  }, [asyncOptions, resolvedOptions, resolveDescription, staticOptions, value]);
  const availableOptions = React.useMemo(() => {
    return Array.from(optionMap.values());
  }, [optionMap]);
  const filteredSuggestions = React.useMemo(() => {
    const query = input.toLowerCase().trim();
    if (!query) return availableOptions;
    return availableOptions.filter((option) => {
      const labelMatch = option.label.toLowerCase().includes(query);
      const descMatch = option.description?.toLowerCase().includes(query);
      return labelMatch || Boolean(descMatch);
    });
  }, [availableOptions, input]);
  React.useEffect(() => {
    if (!loadSuggestions || !touched || disabled) return;
    const query = input.trim();
    let cancelled = false;
    const handle = window.setTimeout(async () => {
      setLoading(true);
      try {
        const items = await loadSuggestions(query);
        if (!cancelled) {
          const normalized = normalizeOptions(items);
          setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized);
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }, 200);
    return () => {
      cancelled = true;
      window.clearTimeout(handle);
    };
  }, [disabled, input, loadSuggestions, touched]);
  const eagerResolveLabel = typeof resolveLabel === "function" ? resolveLabel : void 0;
  React.useEffect(() => {
    if (!value || disabled) return;
    let cancelled = false;
    const apply = (label, description) => {
      const clean = typeof label === "string" ? label.trim() : "";
      if (cancelled || !clean || clean === value) return;
      setResolvedOptions((prev) => {
        if (prev.some((option) => option.value === value && option.label === clean)) return prev;
        return [...prev.filter((option) => option.value !== value), { value, label: clean, description: description ?? null }];
      });
    };
    if (eagerResolveLabel) {
      if (knownLabelValues.has(value)) return;
      Promise.resolve().then(() => eagerResolveLabel(value)).then((label) => apply(label, resolveDescription?.(value))).catch(() => {
      });
      return () => {
        cancelled = true;
      };
    }
    if (coveredOptionValues.has(value)) return;
    if (eagerFallbackLoadedValueRef.current === value) return;
    eagerFallbackLoadedValueRef.current = value;
    if (loadSuggestions) {
      setLoading(true);
      Promise.resolve().then(() => loadSuggestions()).then((items) => {
        if (cancelled) return;
        const normalized = normalizeOptions(items);
        setAsyncOptions((prev) => areOptionsEqual(prev, normalized) ? prev : normalized);
      }).catch(() => {
      }).finally(() => {
        if (!cancelled) setLoading(false);
      });
    }
    return () => {
      cancelled = true;
    };
  }, [value, disabled, knownLabelValues, coveredOptionValues, eagerResolveLabel, loadSuggestions]);
  React.useEffect(() => {
    if (document.activeElement !== inputRef.current) {
      const option = optionMap.get(value);
      setInput(option?.label ?? value ?? "");
    }
  }, [value, optionMap]);
  const selectValue = React.useCallback(
    (nextValue) => {
      if (disabled) return;
      const trimmed = nextValue.trim();
      onChange(trimmed);
      const option = optionMap.get(trimmed);
      setInput(option?.label ?? trimmed);
      setShowSuggestions(false);
      setSelectedIndex(-1);
    },
    [disabled, onChange, optionMap]
  );
  const findOptionForInput = React.useCallback(
    (raw) => {
      const query = raw.trim().toLowerCase();
      if (!query) return null;
      for (const option of optionMap.values()) {
        if (option.value === raw.trim()) return option;
        if (option.label.toLowerCase() === query) return option;
      }
      return null;
    },
    [optionMap]
  );
  const confirmSelection = React.useCallback(
    (raw) => {
      if (disabled) return;
      const option = findOptionForInput(raw);
      if (option) {
        selectValue(option.value);
        return;
      }
      if (!allowCustomValues) {
        setShowSuggestions(false);
        const currentOption = optionMap.get(value);
        if (currentOption && currentOption.label !== currentOption.value) {
          setInput(currentOption.label);
        } else if (!value) {
          setInput("");
        }
        return;
      }
      selectValue(raw);
    },
    [allowCustomValues, disabled, findOptionForInput, optionMap, selectValue, value]
  );
  const handleKeyDown = React.useCallback(
    (event) => {
      if (disabled) return;
      if (event.key === "ArrowDown") {
        event.preventDefault();
        if (!showSuggestions) {
          setShowSuggestions(true);
          setSelectedIndex(0);
        } else {
          setSelectedIndex((prev) => Math.min(prev + 1, filteredSuggestions.length - 1));
        }
      } else if (event.key === "ArrowUp") {
        event.preventDefault();
        setSelectedIndex((prev) => Math.max(prev - 1, -1));
      } else if (event.key === "Enter") {
        event.preventDefault();
        if (selectedIndex >= 0 && filteredSuggestions[selectedIndex]) {
          selectValue(filteredSuggestions[selectedIndex].value);
        } else {
          confirmSelection(input);
        }
      } else if (event.key === "Escape") {
        event.preventDefault();
        setShowSuggestions(false);
        setSelectedIndex(-1);
      }
    },
    [confirmSelection, disabled, filteredSuggestions, input, selectValue, selectedIndex, showSuggestions]
  );
  return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
    /* @__PURE__ */ jsx(
      "input",
      {
        ref: inputRef,
        type: "text",
        className: "w-full h-9 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors outline-none placeholder:text-muted-foreground focus-visible:shadow-focus focus-visible:border-foreground disabled:bg-bg-disabled disabled:border-border-disabled disabled:text-muted-foreground disabled:cursor-not-allowed",
        value: input,
        placeholder: resolvedPlaceholder,
        autoFocus,
        "data-crud-focus-target": "",
        disabled,
        onFocus: () => {
          setTouched(true);
          if (suppressOpenOnFocusRef.current) {
            suppressOpenOnFocusRef.current = false;
            return;
          }
          setShowSuggestions(true);
        },
        onChange: (event) => {
          setTouched(true);
          setInput(event.target.value);
          setShowSuggestions(true);
          setSelectedIndex(-1);
        },
        onKeyDown: handleKeyDown,
        onBlur: () => {
          setTimeout(() => {
            if (disabled) return;
            confirmSelection(input);
          }, 200);
        }
      }
    ),
    showSuggestions && !disabled && (loading || filteredSuggestions.length > 0) && /* @__PURE__ */ jsx("div", { className: "absolute z-popover w-full mt-1 rounded-md border border-input bg-popover p-2 shadow-md max-h-48 sm:max-h-60 overflow-auto", children: loading && touched ? /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs text-muted-foreground", children: loadingLabel }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: filteredSuggestions.map((option, index) => /* @__PURE__ */ jsxs(
      Button,
      {
        type: "button",
        variant: "ghost",
        size: "sm",
        className: [
          "w-full h-auto justify-start font-normal text-left flex flex-col items-start rounded-lg p-2",
          index === selectedIndex ? "bg-muted" : ""
        ].filter(Boolean).join(" "),
        onMouseDown: (event) => event.preventDefault(),
        onClick: () => selectValue(option.value),
        onMouseEnter: () => setSelectedIndex(index),
        children: [
          /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: option.label }),
          option.description ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.description }) : null
        ]
      },
      option.value
    )) }) })
  ] });
}
export {
  ComboboxInput
};
//# sourceMappingURL=ComboboxInput.js.map
