/* ctor-search.jsx — глобальный поиск по Конструктору (subproject 1 «Навигация», N3). Индекс строится
   лениво (по фокусу поля) из текущего состояния App + StaffStore/ctor_subs — ничего не хранится, только
   читает уже загруженные данные. Результат — переход в пункт меню/подвкладку (через onSelect) и, где панель
   умеет подсвечивать элемент, focusId. Префикс имён — cnv (общий top-level scope ctor-*.jsx). */
const { useState: cnvsUseState, useMemo: cnvsUseMemo, useRef: cnvsUseRef, useEffect: cnvsUseEffect } = React;

/* ============ индекс ============ */
function cnvBuildSearchIndex(src) {
  src = src || {};
  const idx = [];

  // каталог: этапы и работы (flattenCatalogNodes уже даёт selection-объект для HierarchyPanel)
  if (window.flattenCatalogNodes) {
    window.flattenCatalogNodes(src.catalog || []).forEach((n) => {
      idx.push({ id: 'cat:' + n.id, kind: 'catalog', label: n.name, hint: n.path, section: 'works', sub: null, focusId: n.id, selection: n.selection });
    });
  }
  // материалы библиотеки
  (src.matLibrary || []).forEach((m) => {
    idx.push({ id: 'mat:' + m.id, kind: 'material', label: m.name, hint: m.sku || 'Материал', section: 'mat-library', sub: null, focusId: m.id });
  });
  // шаблоны смет (пресеты) и пакеты услуг
  (src.presets || []).forEach((p) => {
    idx.push({ id: 'preset:' + p.id, kind: 'preset', label: p.name, hint: 'Тип ремонта', section: 'works', unit: 'templates', sub: null, focusId: p.id });
  });
  (src.packages || []).forEach((p) => {
    idx.push({ id: 'pkg:' + p.id, kind: 'package', label: p.name, hint: 'Пакет услуг', section: 'works', unit: 'templates', pane: 'packages', paneLabel: 'Пакеты услуг', sub: null, focusId: p.id });
  });
  // оси шаблона сметы: тип объекта, исходное состояние и комнаты (спеки 2026-10-05-object-types-design.md, 2026-10-05-template-axes-design.md)
  (src.objectRules || []).forEach((r) => {
    idx.push({ id: 'object:' + r.id, kind: 'object', label: r.name || 'Без названия', hint: 'Тип объекта', section: 'works', unit: 'templates', sub: null, focusId: r.id });
  });
  (src.stateRules || []).forEach((r) => {
    idx.push({ id: 'state:' + r.id, kind: 'state', label: r.name || 'Без названия', hint: 'Исходное состояние', section: 'tpl-state', sub: null, focusId: r.id });
  });
  (src.roomTypes || []).forEach((t) => {
    idx.push({ id: 'room:' + t.id, kind: 'room', label: t.name || 'Без названия', hint: 'Тип помещения', section: 'tpl-room', sub: null, focusId: t.id });
  });
  // автоправила: заготовки по анкете и перекрёстные работы — вкладки «Работ» (kind результата — вид «Автоправил»)
  (src.intakeRules || []).forEach((r) => {
    idx.push({ id: 'intake:' + r.id, kind: 'intake', label: r.label || r.key || 'Без названия', hint: 'Заготовка по анкете', section: 'works', unit: 'rules', pane: 'rules', paneLabel: 'Автоправила', sub: null, focusId: r.id });
  });
  (src.crossRules || []).forEach((r) => {
    idx.push({ id: 'cross:' + r.id, kind: 'cross', label: r.name || 'Без названия', hint: 'Перекрёстное правило', section: 'works', unit: 'rules', pane: 'rules', paneLabel: 'Автоправила', sub: null, focusId: r.id });
  });
  // чек-листы сдачи
  (src.checklists || []).forEach((c) => {
    idx.push({ id: 'cl:' + c.id, kind: 'checklist', label: c.name || 'Без названия', hint: 'Чек-лист сдачи', section: 'checklists', sub: null, focusId: c.id });
  });
  // штат
  try {
    const S = window.StaffStore;
    if (S) {
      S.list().forEach((p) => {
        if (!p.name) return;
        idx.push({ id: 'staff:' + p.id, kind: 'staff', label: p.name, hint: 'Штат', section: 'staff', sub: null, focusId: p.id });
      });
    }
  } catch (e) { /* ignore */ }
  // субподрядчики
  try {
    (window.loadSubs ? window.loadSubs() : []).forEach((s) => {
      idx.push({ id: 'sub:' + s.id, kind: 'sub', label: s.name, hint: 'Субподрядчик', section: 'subs', sub: null, focusId: s.id });
    });
  } catch (e) { /* ignore */ }

  return idx;
}

/* группа результата — «Пункт» или «Пункт · Подвкладка» из CtorNav; у пунктов шаблонов, экономики и бухгалтерии спереди
   название группы меню («Экономика · Цены и налоги»); у результатов-вкладок «Работ» (item.paneLabel) сзади вкладка:
   «Работы · Пакеты услуг», «Работы · Автоправила» */
const CNVS_PREFIXED_GROUPS = ['templates', 'economy', 'accounting'];
function cnvGroupLabel(item) {
  const N = window.CtorNav;
  const sec = N && N.SECTIONS.find((s) => s.id === item.section);
  if (!sec) return item.section;
  let label = sec.label;
  if (CNVS_PREFIXED_GROUPS.indexOf(sec.group) !== -1) {
    const g = N.GROUPS.find((x) => x.id === sec.group);
    if (g) label = g.label + ' · ' + sec.label;
  }
  if (item.paneLabel) label = label + ' · ' + item.paneLabel;
  if (item.sub && sec.subs) {
    const subMeta = sec.subs.find((x) => x.id === item.sub);
    return subMeta ? label + ' · ' + subMeta.label : label;
  }
  return label;
}

function cnvHighlight(text, q) {
  text = text || '';
  if (!q) return text;
  const i = text.toLowerCase().indexOf(q.toLowerCase());
  if (i < 0) return text;
  return (
    <React.Fragment>
      {text.slice(0, i)}
      <mark style={{ background: 'var(--primary-soft)', color: 'var(--primary)', borderRadius: 3, padding: '0 1px' }}>{text.slice(i, i + q.length)}</mark>
      {text.slice(i + q.length)}
    </React.Fragment>
  );
}

/* ============ CtorSearch ============ */
function CtorSearch({ source, onSelect, visible }) {
  const [index, setIndex] = cnvsUseState([]);
  const [q, setQ] = cnvsUseState('');
  const [open, setOpen] = cnvsUseState(false);
  const wrapRef = cnvsUseRef(null);

  cnvsUseEffect(() => {
    if (!open) return;
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDown);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDown); document.removeEventListener('keydown', onKey); };
  }, [open]);

  // строится заново при каждом фокусе поля (не на каждое нажатие клавиши) — дёшево (пара сотен записей),
  // зато не залипает на устаревших данных, если пользователь правил каталог/материалы между поисками.
  // visible — unit, доступные человеку по правам (CtorRights): в чужих пунктах результатов нет (пункт меню → его unit)
  const ensureIndex = () => setIndex(cnvBuildSearchIndex(source).filter((it) => !visible || visible.indexOf(it.unit || (window.CtorNav ? window.CtorNav.unitOf(it.section) : it.section)) !== -1));

  const query = q.trim();
  const results = cnvsUseMemo(() => {
    if (!query) return [];
    const ql = query.toLowerCase();
    return index.filter((it) => it.label.toLowerCase().indexOf(ql) >= 0 || (it.hint || '').toLowerCase().indexOf(ql) >= 0).slice(0, 20);
  }, [query, index]);

  const grouped = cnvsUseMemo(() => {
    const order = [];
    const map = {};
    results.forEach((it) => {
      const g = cnvGroupLabel(it);
      if (!map[g]) { map[g] = []; order.push(g); }
      map[g].push(it);
    });
    return order.map((g) => [g, map[g]]);
  }, [results]);

  const pick = (it) => {
    setOpen(false);
    setQ('');
    onSelect(it);
  };

  return (
    <div ref={wrapRef} style={ctorStyles.sidebarSearchWrap}>
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"
        style={{ position: 'absolute', left: 11, top: '50%', transform: 'translateY(-50%)', color: 'var(--text-tertiary)', pointerEvents: 'none' }}>
        <circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line>
      </svg>
      <input
        value={q}
        onFocus={() => { ensureIndex(); setOpen(true); }}
        onChange={(e) => { setQ(e.target.value); setOpen(true); }}
        placeholder="Поиск по Конструктору"
        aria-label="Поиск по Конструктору"
        style={{ ...ctorStyles.input, paddingLeft: 32 }}
      />
      {open && query && (
        <div style={{ ...ctorStyles.dropdown, top: 'calc(100% + 6px)', left: 0, right: 'auto', width: '100%', minWidth: 260, maxHeight: 420, overflowY: 'auto' }}>
          {!results.length && <div style={{ padding: '14px 12px', fontSize: 13, color: 'var(--text-tertiary)' }}>Ничего не найдено</div>}
          {grouped.map(([label, items]) => (
            <div key={label} style={{ marginBottom: 4 }}>
              <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '.05em', padding: '6px 10px 3px' }}>{label}</div>
              {items.map((it) => (
                <button key={it.id} type="button" onClick={() => pick(it)}
                  style={{ ...ctorStyles.dropdownItem, flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
                  <span>{cnvHighlight(it.label, query)}</span>
                  {it.hint && <span style={{ fontSize: 11, color: 'var(--text-tertiary)', fontWeight: 500 }}>{it.hint}</span>}
                </button>
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Object.assign(window, { CtorSearch, cnvBuildSearchIndex });
