// ─────────────────────────────────────────────────────
// ComboBuscador — desplegable en el que se escribe para filtrar (sustituye a un
// <select> largo). Reutilizable: lo usa Expediente › Materiales › Nueva línea
// (material del catálogo) y vale para cualquier lista de { id, label }.
//
//   <ComboBuscador value={id} items={[{ id, label, mono?, group? }]}
//     onChange={(item | null) => …} placeholder="…" vacio="— Texto libre —" allowEmpty
//     acciones={[{ key, label: (textoEscrito) => '…', onClick: (textoEscrito) => …, color? }]} />
//
// - Filtra por código (`mono`) y nombre, sin distinguir mayúsculas ni acentos;
//   varias palabras = todas tienen que aparecer ("cerr 5" encuentra CERR-SIMP-5-12).
// - Mantiene la agrupación (`group`) como cabeceras, en el orden en que llegan.
// - Teclado: ↑/↓ mueven, Enter elige, Escape cierra sin cambiar nada.
// - Borrar el texto y salir deja el valor vacío (si `allowEmpty`).
// - INC-001: con `acciones`, al pie de la lista salen opciones extra que reciben lo escrito
//   (p. ej. «Crear artículo en Artículos» o «añadir como comentario» para lo que no está).
// ─────────────────────────────────────────────────────

const cbNorm = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '');
const cbTexto = (it) => (it ? [it.mono, it.label].filter(Boolean).join(' · ') : '');
const CB_MAX = 300; // tope de filas pintadas: con más, se pide escribir para acotar

const ComboBuscador = ({ value, items, onChange, placeholder = 'Escribe para buscar…', vacio = '— Ninguno —', allowEmpty, disabled, style, acciones }) => {
  const sel = (items || []).find((it) => String(it.id) === String(value)) || null;
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const [idx, setIdx] = React.useState(0);
  const [escrito, setEscrito] = React.useState(false); // se ha tecleado desde que se abrió
  const inputRef = React.useRef(null);
  const listRef = React.useRef(null);

  // Filtrado: todas las palabras en código + nombre + grupo.
  const palabras = cbNorm(q).split(/\s+/).filter(Boolean);
  const filtrados = palabras.length
    ? (items || []).filter((it) => { const t = cbNorm(`${it.mono || ''} ${it.label || ''} ${it.group || ''}`); return palabras.every((p) => t.includes(p)); })
    : (items || []);
  const lista = filtrados.slice(0, CB_MAX);
  // Opciones navegables con el teclado: la vacía (si hay) + las filtradas.
  const opciones = allowEmpty && !palabras.length ? [null, ...lista] : lista;

  React.useEffect(() => { if (!open) { setQ(''); setEscrito(false); } }, [open]);
  // La fila activa siempre a la vista al moverse con las flechas.
  React.useEffect(() => {
    if (!open || !listRef.current) return;
    const el = listRef.current.querySelector('.cb-it.on');
    el && el.scrollIntoView({ block: 'nearest' });
  }, [idx, open]);

  const elegir = (it) => {
    onChange && onChange(it);
    setOpen(false);
    inputRef.current && inputRef.current.blur();
  };
  const abrir = () => {
    if (disabled) return;
    setOpen(true);
    const i = sel ? opciones.findIndex((o) => o && String(o.id) === String(sel.id)) : 0;
    setIdx(Math.max(0, i));
  };
  const teclas = (e) => {
    if (!open && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { e.preventDefault(); abrir(); return; }
    if (!open) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setIdx((i) => Math.min(i + 1, opciones.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setIdx((i) => Math.max(i - 1, 0)); }
    else if (e.key === 'Enter') { e.preventDefault(); if (opciones.length) elegir(opciones[Math.min(idx, opciones.length - 1)]); }
    else if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); setOpen(false); inputRef.current && inputRef.current.blur(); }
  };
  const salir = () => {
    // Texto borrado a mano = quitar la selección; si no, se descarta lo escrito.
    if (open && allowEmpty && sel && escrito && q.trim() === '') onChange && onChange(null);
    setOpen(false);
  };

  let grupoPrev;
  return (
    <div className="cb" style={style}>
      <input
        ref={inputRef}
        className="cb-input"
        disabled={disabled}
        value={open ? q : cbTexto(sel)}
        placeholder={open && sel ? cbTexto(sel) : (allowEmpty && !sel ? vacio : placeholder)}
        onFocus={abrir}
        onClick={() => !open && abrir()}
        onChange={(e) => { setQ(e.target.value); setEscrito(true); setIdx(0); if (!open) setOpen(true); }}
        onKeyDown={teclas}
        onBlur={salir}
        autoComplete="off"
        role="combobox"
        aria-expanded={open}
      />
      <span className="cb-caret" aria-hidden="true">▾</span>
      {open && (
        // onMouseDown preventDefault: el clic en la lista no roba el foco al input (si no, el blur la cerraría antes).
        <div className="cb-dd" ref={listRef} onMouseDown={(e) => e.preventDefault()} role="listbox">
          {opciones.length === 0 && <div className="cb-it muted">Sin resultados</div>}
          {opciones.map((it, i) => {
            const cab = it && it.group && it.group !== grupoPrev ? it.group : null;
            if (it) grupoPrev = it.group;
            return (
              <React.Fragment key={it ? it.id : '__vacio'}>
                {cab && <div className="cb-grp">{cab}</div>}
                <div
                  className={`cb-it ${i === idx ? 'on' : ''} ${it && sel && String(it.id) === String(sel.id) ? 'sel' : ''} ${it ? '' : 'muted'} ${it && it.group ? 'ind' : ''}`}
                  onMouseEnter={() => setIdx(i)}
                  onClick={() => elegir(it)}
                  role="option"
                >
                  {it ? <>{it.mono && <span className="mono">{it.mono}</span>}<span>{it.label}</span></> : vacio}
                </div>
              </React.Fragment>
            );
          })}
          {filtrados.length > lista.length && <div className="cb-it muted" style={{ fontSize: 11 }}>Mostrando {lista.length} de {filtrados.length} · escribe para acotar</div>}
          {(acciones || []).map((a, i) => (
            <div key={a.key || i} className="cb-it" style={{ borderTop: i === 0 ? '1px solid var(--line)' : 'none', color: a.color || 'var(--accent)', fontWeight: 600 }}
              onClick={() => { const t = q.trim(); setOpen(false); inputRef.current && inputRef.current.blur(); a.onClick(t); }}>
              {a.label(q.trim())}
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

window.ComboBuscador = ComboBuscador;
