/**
 * 
 * This is a generic combobox-search component that replaces the basic <select>
 * html element with a searchable list that only renders a subset page of results.
 * This allows for lazy loading and a faster UI experience for the user.
 * 
 */

const SearchSelect = (props) => {
  const {
    options = [],
    value = null,
    onChange,
    disabled = false,
    pageSize = 50,
    hideSearch = false
  } = props;

  const rootRef = React.useRef(null);
  const inputRef = React.useRef(null);
  const listRef = React.useRef(null);

  const [open, setOpen] = React.useState(false);
  const [query, setQuery] = React.useState("");
  const [hoverIndex, setHoverIndex] = React.useState(-1);
  const [visibleCount, setVisibleCount] = React.useState(pageSize);

  React.useEffect(() => {
    const onDown = (e) => {
      if (rootRef.current && !rootRef.current.contains(e.target)) {
        setOpen(false);
        setQuery("");
        setHoverIndex(-1);
        setVisibleCount(pageSize);
      }
    };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [pageSize]);

  React.useEffect(() => {
    setVisibleCount(pageSize);
    setHoverIndex(-1);
  }, [query, pageSize]);

  const selectedOption = React.useMemo(() => {
    if (value == null) return null;
    return options.find((o) => o.value === value) || null;
  }, [options, value]);

  const normalizedQuery = query.trim().toLowerCase();

  const filtered = React.useMemo(() => {
    if (!normalizedQuery) return options;

    return options.filter((o) => {
      const label = String(o.label || "").toLowerCase();
      const section = String(o.courseSection || "").toLowerCase();

      if (label.includes(normalizedQuery) || section.includes(normalizedQuery)) return true;

      for (const k of Object.keys(o)) {
        const v = o[k];
        if (typeof v === "string" && v.toLowerCase().includes(normalizedQuery)) {
          return true;
        }
      }
      return false;
    });
  }, [options, normalizedQuery]);

  const shown = React.useMemo(() => {
    return filtered.slice(0, Math.min(visibleCount, filtered.length));
  }, [filtered, visibleCount]);

  const openMenu = () => {
    if (disabled) return;
    setOpen(true);
    requestAnimationFrame(() => inputRef.current?.focus());
  };

  const closeMenu = () => {
    setOpen(false);
    setQuery("");
    setHoverIndex(-1);
    setVisibleCount(pageSize);
  };

  const commitSelection = (opt) => {
    onChange?.(opt.value, opt);
    closeMenu();
  };

  const total = filtered.length;
  const shownCount = shown.length;

  const hoverBg = "#e7f1ff";
  const itemTextColor = "#212529";

  return (
    <div ref={rootRef} className="position-relative w-100">
      <button
        type="button"
        className="form-select text-start"
        disabled={disabled}
        onClick={() => (open ? closeMenu() : openMenu())}
        style={{ display: "flex", alignItems: "center", justifyContent: "flex-start" }}
      >
        <span className="text-truncate d-block w-100">
          {selectedOption ? selectedOption.label : `${window.globalLabels?.select}...`}
        </span>
      </button>

      {open && (
        <div className="dropdown-menu show w-100 p-0">
          {!hideSearch && (
            <div className="px-2 py-2">
              <input
                ref={inputRef}
                className="form-control"
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                placeholder={`${window.globalLabels?.search}...`}
                autoComplete="off"
                disabled={disabled}
              />
            </div>
          )}

          <div
            ref={listRef}
            className="overflow-auto"
            style={{ maxHeight: "260px", overflowX: "hidden" }}
          >
            {shownCount === 0 && (
              <div className="dropdown-item text-muted">
                {window.globalLabels?.noResults}
              </div>
            )}

            {shown.map((opt, idx) => {
              const isSelected = value != null && opt.value === value;
              const isHovered = (idx === hoverIndex);

              return (
                <button
                  key={`${String(opt.value)}-${idx}`}
                  type="button"
                  data-ss-item={idx}
                  className={`dropdown-item text-start ${isSelected ? "fw-semibold" : ""}`}
                  onMouseEnter={() => setHoverIndex(idx)}
                  onMouseLeave={() => setHoverIndex(-1)}
                  onMouseDown={(e) => {
                    e.preventDefault();
                    commitSelection(opt);
                  }}
                  style={{
                    color: itemTextColor,
                    backgroundColor: isHovered ? hoverBg : "transparent",
                    whiteSpace: "normal",
                    wordBreak: "break-word"
                  }}
                >
                  {opt.label}
                </button>
              );
            })}
          </div>

          <div className="d-flex justify-content-between align-items-center border-top px-2 py-2">
            <div className="text-muted small">
              {total > shownCount
                ? `${window.globalLabels?.results}: ${shownCount} / ${total}`
                : `${window.globalLabels?.results}: ${total}`}
            </div>
            {total > shownCount && (
              <button
                type="button"
                className="btn btn-outline-secondary btn-sm"
                onMouseDown={(e) => e.preventDefault()}
                onClick={() => setVisibleCount((c) => c + pageSize)}
              >
                {window.globalLabels?.loadMore}
              </button>
            )}
          </div>
        </div>
      )}
    </div>
  );
};

window.SearchSelect = SearchSelect;