/* ---------- Forms ---------- */
function Field({ label, error, children }) {
  return (
    <label className={"field" + (error ? " has-err" : "")}>
      <span className="field-head">
        <span className="field-label">{label}</span>
        {/* On the label's row rather than under the input: the message lands where
            the eye already is, nothing below it shifts, and in a scrolling modal it
            can't end up under the fold. The red border does the pointing. */}
        {error ? <span className="field-err"><Icon name="alert" size={13} />{error}</span> : null}
      </span>
      {children}
    </label>
  );
}
function Input(props) { return <input className="input" {...props} />; }
function Select({ options, placeholder, ...rest }) {
  return (
    <select className="input" {...rest}>
      {placeholder ? <option value="">{placeholder}</option> : null}
      {options.map((o) => (typeof o === "object" ? <option key={o.value} value={o.value}>{o.label}</option> : <option key={o} value={o}>{TV(o)}</option>))}
    </select>
  );
}

/* Searchable dropdown (combobox). Pass onCreate to allow adding new entries
   inline; onAdd to show an always-visible "+" row that hands off to a popup;
   onEditRow to show a pencil on each row (also hands off to a popup). */
function SearchSelect({ options, value, onChange, placeholder, onCreate, addLabel, disabled, meta, onAdd, onEditRow }) {
  const { useState, useEffect, useRef } = React;
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState(null); // null = not typing, show value
  const [hi, setHi] = useState(0);
  const [pos, setPos] = useState(null);
  const rootRef = useRef(null);
  const inputRef = useRef(null);

  // Options stay English (they are the stored value); TV() is applied for
  // display and for matching, so typing Arabic finds the same rows.
  const query = (q || "").trim();
  const filtered = q === null || query === "" ? options : options.filter((o) =>
    (o + " " + TV(o)).toLowerCase().includes(query.toLowerCase()));
  const canAdd = !onAdd && !!onCreate && query !== "" && !options.some((o) => o.toLowerCase() === query.toLowerCase());
  const hasAddRow = canAdd || !!onAdd;
  const total = filtered.length + (hasAddRow ? 1 : 0);

  const place = () => {
    const el = rootRef.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const vh = window.innerHeight;
    const below = vh - r.bottom;
    const up = below < 250 && r.top > below;
    setPos(up
      ? { left: r.left, width: r.width, bottom: vh - r.top + 6 }
      : { left: r.left, width: r.width, top: r.bottom + 6 });
  };

  useEffect(() => {
    if (!open) return;
    setHi(0);
    place();
    const onDown = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) close(); };
    const onMove = () => place();
    document.addEventListener("mousedown", onDown);
    window.addEventListener("scroll", onMove, true);
    window.addEventListener("resize", onMove);
    return () => {
      document.removeEventListener("mousedown", onDown);
      window.removeEventListener("scroll", onMove, true);
      window.removeEventListener("resize", onMove);
    };
  }, [open]);
  useEffect(() => { setHi(0); }, [q]);

  const close = () => { setOpen(false); setQ(null); };
  const pick = (o) => { onChange(o); close(); };
  const create = () => { if (onAdd) onAdd(query); else onCreate(query); close(); };

  return (
    <div className="ss" ref={rootRef}>
      <div className={"input ss-field" + (disabled ? " disabled" : "")} onClick={() => { if (!disabled && !open) { setOpen(true); inputRef.current && inputRef.current.focus(); } }}>
        <input ref={inputRef} className="ss-input" disabled={disabled}
          value={q === null ? (value ? TV(value) : "") : q}
          placeholder={(value ? TV(value) : "") || placeholder || T("common.choose")}
          onFocus={() => { setOpen(true); setQ(""); }}
          onChange={(e) => { setQ(e.target.value); if (!open) setOpen(true); }}
          onKeyDown={(e) => {
            if (e.key === "ArrowDown") { e.preventDefault(); if (!open) setOpen(true); else setHi((h) => Math.min(h + 1, total - 1)); }
            if (e.key === "ArrowUp") { e.preventDefault(); setHi((h) => Math.max(h - 1, 0)); }
            if (e.key === "Escape") { close(); e.target.blur(); }
            if (e.key === "Enter") {
              e.preventDefault();
              if (hi < filtered.length && filtered[hi]) pick(filtered[hi]);
              else if (hasAddRow) create();
              e.target.blur();
            }
            if (e.key === "Tab") close();
          }} />
        <Icon name="chevD" size={14} />
      </div>
      {/* The popup sits inside Field's <label>, so a click on a row would be
          forwarded by the label to its control (our input) and reopen the list
          right after pick() closed it. preventDefault cancels that forwarding.
          This comment belongs here, in children position — inside the ternary's
          parens it would parse as an object literal, not a comment. */}
      {open ? (
        <div className="ss-pop" style={pos || { visibility: "hidden" }}
          onMouseDown={(e) => e.preventDefault()} onClick={(e) => e.preventDefault()}>
          <ul className="ss-list" role="listbox">
            {filtered.map((o, i) => (
              <li key={o} role="option" aria-selected={o === value}
                className={"ss-item" + (i === hi ? " hi" : "") + (o === value ? " sel" : "")}
                onMouseEnter={() => setHi(i)} onClick={() => pick(o)}>
                <span>{TV(o)}</span>
                {meta && meta[o] ? <span className="ss-meta">{meta[o]}</span> : null}
                {onEditRow ? (
                  <button type="button" className="ss-edit" aria-label={T("common.editItem", { name: o })}
                    onClick={(e) => { e.stopPropagation(); close(); onEditRow(o); }}>
                    <Icon name="edit" size={13} />
                  </button>
                ) : null}
                {o === value ? <Icon name="check" size={14} stroke={2.2} /> : null}
              </li>
            ))}
            {filtered.length === 0 && !hasAddRow ? <li className="ss-empty">{T("common.noMatches")}</li> : null}
            {hasAddRow ? (
              <li className={"ss-item add" + (hi === filtered.length ? " hi" : "")}
                onMouseEnter={() => setHi(filtered.length)} onClick={create}>
                <Icon name="plus" size={14} stroke={2.2} />
                <span>{(addLabel || T("common.add")) + (query ? " “" + query + "”" : "…")}</span>
              </li>
            ) : null}
          </ul>
        </div>
      ) : null}
    </div>
  );
}

Object.assign(window, { Field, Input, Select, SearchSelect });
