/* ctor-materials-library.jsx — вкладка «Библиотека» конструктора материалов:
   дерево категорий (по подвидам) → список позиций → инспектор позиции (§4.4) */
const { useState: mlUseState, useMemo: mlUseMemo, useEffect: mlUseEffect } = React;
const MD = window.MaterialsData;

const mlToolAdd = { fontFamily: 'inherit', fontSize: 12, fontWeight: 700, color: '#e8793a', background: 'transparent', border: 'none', cursor: 'pointer', padding: '3px 2px' };
const mlToolDel = { width: 22, height: 22, borderRadius: 6, border: 'none', background: 'transparent', color: '#c7beb2', cursor: 'pointer', fontSize: 16, lineHeight: 1, flexShrink: 0, fontFamily: 'inherit' };

/* ---------- мелкие чипы (общие для материального слоя) ---------- */
function MaterialKindChip({ kind, small }) {
  const m = MD.kindMeta(kind);
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: small ? '1px 8px' : '3px 10px', borderRadius: 980, background: m.bg, color: m.color, fontSize: small ? 10.5 : 11.5, fontWeight: 700, whiteSpace: 'nowrap' }}>
      <span style={{ width: 5, height: 5, borderRadius: '50%', background: m.color }}></span>{m.label}
    </span>);

}
function SubtypeChip({ id }) {
  const s = MD.subtypeById(id);if (!s) return null;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '3px 9px', borderRadius: 980, background: s.color + '14', color: s.color, fontSize: 11.5, fontWeight: 600 }}>
      <span>{s.icon}</span>{s.name}
    </span>);

}
function money(n) {return (n || 0).toLocaleString('ru-RU') + ' ₽';}
function plural(n, one, few, many) {
  const m10 = n % 10,m100 = n % 100;
  if (m10 === 1 && m100 !== 11) return one;
  if (m10 >= 2 && m10 <= 4 && (m100 < 10 || m100 >= 20)) return few;
  return many;
}

function matGalleryCount(m) {
  const MD = window.MaterialsData;
  return (MD.matGallery ? MD.matGallery(m) : []).filter((g) => g.url).length;
}

function MaterialGalleryThumb({ material, size, className }) {
  const sz = size || 52;
  const MD = window.MaterialsData;
  const cover = MD.matCoverUrl ? MD.matCoverUrl(material) : null;
  const count = matGalleryCount(material);
  const cls = 'mat-lib-thumb' + (className ? ' ' + className : '');
  if (!cover) {
    return <div className={cls + ' mat-lib-thumb--empty'} style={{ width: sz, height: sz }} aria-hidden="true" />;
  }
  return (
    <div className={cls} style={{ position: 'relative', width: sz, height: sz, flexShrink: 0 }}>
      <img src={cover} alt="" style={{ width: sz, height: sz, objectFit: 'cover', display: 'block' }}
        onError={(e) => { e.target.style.display = 'none'; }} />
      {count > 1 &&
      <span className="mat-lib-thumb-count">{count}</span>
      }
    </div>);
}

const ML_VIEW_KEY = 'ctor-mat-lib-view';
function mlLoadView() {
  try { const v = localStorage.getItem(ML_VIEW_KEY); if (v === 'tile' || v === 'list') return v; } catch (_) {}
  return 'list';
}

function MaterialLibraryViewSeg({ view, onChange }) {
  return (
    <div className="ctor-view-seg" role="group" aria-label="Вид отображения">
      <button type="button" className={view === 'list' ? 'is-on' : ''} onClick={() => onChange('list')}>Список</button>
      <button type="button" className={view === 'tile' ? 'is-on' : ''} onClick={() => onChange('tile')}>Плитка</button>
    </div>);
}

function materialLibraryMetaParts(m, ft, used) {
  const parts = [];
  if (m.sku) parts.push(m.sku);
  if (m.supplier) parts.push(m.supplier);
  if (m.consumptionRate != null) parts.push('расход ' + m.consumptionRate + ' ' + (m.consumptionUnit || ''));
  if (used > 0) parts.push(used + ' ' + plural(used, 'узел', 'узла', 'узлов'));
  const kind = MD.kindMeta(m.materialKind);
  if (kind && kind.label) parts.push(kind.label);
  if (ft && ft.name) parts.push(ft.name);
  return parts;
}

function MaterialLibraryKebab({ onOpen, onDuplicate, onRemove }) {
  return (
    <KebabMenu items={[
      { icon: pencilIcon, label: 'Настройки', onClick: onOpen },
      { icon: Icon.copy, label: 'Дублировать', onClick: onDuplicate },
      { icon: Icon.trash, label: 'Удалить', onClick: onRemove, danger: true }]
    } />);
}

function MaterialGalleryEditor({ material, onChange }) {
  const gallery = MD.matGallery(material);
  const [preview, setPreview] = mlUseState(null);
  const setGallery = (next) => onChange({ gallery: next, attachments: next });
  const patch = (id, p) => setGallery(gallery.map((g) => g.id === id ? { ...g, ...p } : g));
  const del = (id) => setGallery(gallery.filter((g) => g.id !== id));
  const add = () => setGallery([...gallery, MD.galleryItem({ label: 'Новое фото' })]);

  return (
    <InspectorSection title="Фото и сценарии" lead="Ракурсы товара, упаковка, монтаж на объекте. Первое фото с URL становится обложкой в списке.">
      {gallery.length > 0 && (
        <div className="ins-gallery-grid">
          {gallery.map((g) => (
            <div key={g.id} className="ins-gallery-card">
              <button type="button" onClick={() => g.url && setPreview(g)} title={g.url ? 'Открыть превью' : 'Добавьте URL'}
                className="ins-gallery-thumb" style={{ cursor: g.url ? 'zoom-in' : 'default' }}>
                {g.url ? (
                  <img src={g.url} alt={g.label || ''} onError={(e) => { e.target.style.opacity = 0.35; }} />
                ) : (
                  <span className="ins-gallery-thumb-placeholder" aria-hidden="true">📷</span>
                )}
              </button>
              <input value={g.label || ''} onChange={(e) => patch(g.id, { label: e.target.value })} placeholder="Подпись (напр. вид сбоку)"
                style={{ ...insStyles.field, fontSize: 11.5 }} />
              <input value={g.url || ''} onChange={(e) => patch(g.id, { url: e.target.value })} placeholder="URL фото (CDN, склад, каталог…)"
                style={{ ...insStyles.field, fontSize: 11.5 }} />
              <button type="button" onClick={() => del(g.id)} style={{ ...mlToolAdd, color: '#f04e62', alignSelf: 'flex-start' }}>Удалить фото</button>
            </div>
          ))}
        </div>
      )}
      <button type="button" onClick={add} style={{ ...ctorStyles.btnDashed, alignSelf: 'flex-start', fontSize: 12.5, marginTop: gallery.length ? 4 : 0 }}>{Icon.plus} Добавить фото</button>
      {preview &&
      <div onClick={() => setPreview(null)} role="dialog" aria-modal="true" aria-label="Просмотр фото"
        style={{ position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(26,23,20,.78)', display: 'grid', placeItems: 'center', padding: 24 }}>
        <div onClick={(e) => e.stopPropagation()} style={{ maxWidth: 'min(920px, 96vw)' }}>
          <img src={preview.url} alt={preview.label || ''} style={{ maxWidth: '100%', maxHeight: '80vh', borderRadius: 12, display: 'block', margin: '0 auto' }} />
          {preview.label && <div style={{ marginTop: 10, textAlign: 'center', color: '#fff', fontSize: 14, fontWeight: 600 }}>{preview.label}</div>}
          <div style={{ textAlign: 'center', marginTop: 12 }}>
            <button type="button" onClick={() => setPreview(null)} style={{ ...ctorStyles.btnGhost, padding: '8px 16px', fontSize: 13, color: '#fff', borderColor: 'rgba(255,255,255,.35)', background: 'rgba(255,255,255,.12)' }}>Закрыть</button>
          </div>
        </div>
      </div>
      }
    </InspectorSection>);
}

/* ============ ВКЛАДКА «БИБЛИОТЕКА» ============ */
function MaterialLibraryPanel({ library, setLibrary, fieldTypes, categories, bindings }) {
  const [catFilter, setCatFilter] = mlUseState(null); // categoryId | null (все)
  const [subFilter, setSubFilter] = mlUseState(null); // subtype id | null
  const [q, setQ] = mlUseState('');
  const [selId, setSelId] = mlUseState(null);
  const [inspectorOpenId, setInspectorOpenId] = mlUseState(null);
  const [viewMode, setViewMode] = mlUseState(mlLoadView);
  const setView = (mode) => {
    setViewMode(mode);
    try { localStorage.setItem(ML_VIEW_KEY, mode); } catch (_) {}
  };

  const pick = (id) => {
    setSelId(id);
    setInspectorOpenId(null);
  };
  const openInspector = (id) => {
    setSelId(id);
    setInspectorOpenId((cur) => (cur === id ? null : id));
  };
  const closeInspector = () => setInspectorOpenId(null);

  const update = (id, patch) => setLibrary((ls) => ls.map((m) => m.id === id ? { ...m, ...patch } : m));
  const remove = (id) => {
    const used = MD.usageOf(bindings, id).length;
    if (used && !confirm('Позиция привязана к ' + used + ' узлам каталога. Удалить? Привязки останутся «висящими».')) return;
    if (!used && !confirm('Удалить позицию из библиотеки?')) return;
    setLibrary((ls) => ls.filter((m) => m.id !== id));
    if (selId === id) setSelId(null);
    if (inspectorOpenId === id) setInspectorOpenId(null);
  };
  const add = (subtype, categoryId) => {
    const m = MD.blankMaterial(subtype, categoryId);
    setLibrary((ls) => [m, ...ls]);
    setSelId(m.id);
    setInspectorOpenId(m.id);
  };
  const duplicate = (id) => {
    const src = library.find((m) => m.id === id);if (!src) return;
    const c = MD.clone(src);c.id = MD.uid('mat');c.name = src.name + ' (копия)';c.slug = (src.slug || '') + '-copy';
    setLibrary((ls) => {const i = ls.findIndex((m) => m.id === id);const next = [...ls];next.splice(i + 1, 0, c);return next;});
    setSelId(c.id);
    setInspectorOpenId(c.id);
  };

  const counts = mlUseMemo(() => {
    const byCat = {},bySub = {};
    library.forEach((m) => {byCat[m.categoryId] = (byCat[m.categoryId] || 0) + 1;bySub[m.subtype] = (bySub[m.subtype] || 0) + 1;});
    return { byCat, bySub };
  }, [library]);

  const filtered = library.filter((m) => {
    if (subFilter && m.subtype !== subFilter) return false;
    if (catFilter && m.categoryId !== catFilter) return false;
    if (q) {const s = (m.name + ' ' + m.sku + ' ' + m.supplier).toLowerCase();if (!s.includes(q.toLowerCase())) return false;}
    return true;
  });
  const selected = library.find((m) => m.id === selId) || null;

  return (
    <div style={{ display: 'flex', gap: 18, alignItems: 'flex-start' }}>
      {/* ---- ДЕРЕВО КАТЕГОРИЙ ---- */}
      <div style={{ width: 244, flexShrink: 0, display: 'flex', flexDirection: 'column', gap: 4 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 7, background: '#f0ece5', borderRadius: 9, padding: '6px 10px', marginBottom: 6 }}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#8a817a" strokeWidth="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
          <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Поиск по библиотеке" style={{ border: 'none', outline: 'none', background: 'transparent', fontFamily: 'inherit', fontSize: 12.5, flex: 1, minWidth: 0 }} />
        </div>
        <button onClick={() => {setCatFilter(null);setSubFilter(null);}} style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 11px', borderRadius: 9, border: 'none', cursor: 'pointer', fontFamily: 'inherit',
          background: !catFilter && !subFilter ? '#1a1714' : 'transparent', color: !catFilter && !subFilter ? '#fff' : '#6b6259', fontSize: 13, fontWeight: 600
        }}>
          Вся библиотека <span style={{ fontSize: 11.5, opacity: .7, fontVariantNumeric: 'tabular-nums' }}>{library.length}</span>
        </button>
        {MD.SUBTYPES.map((s) => {
          const cats = categories.filter((c) => c.subtype === s.id);
          const subActive = subFilter === s.id && !catFilter;
          return (
            <div key={s.id} style={{ marginTop: 4 }}>
              <button onClick={() => {setSubFilter(s.id);setCatFilter(null);}} style={{
                display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '7px 11px', borderRadius: 9, border: 'none', cursor: 'pointer', fontFamily: 'inherit',
                background: subActive ? s.color + '18' : 'transparent', color: subActive ? s.color : '#1a1714', fontSize: 12.5, fontWeight: 700
              }}>
                <span>{s.icon}</span><span style={{ flex: 1, textAlign: 'left' }}>{s.name}</span>
                <span style={{ fontSize: 11, color: '#a89e92', fontVariantNumeric: 'tabular-nums' }}>{counts.bySub[s.id] || 0}</span>
              </button>
              <div style={{ paddingLeft: 8 }}>
                {cats.map((c) => {
                  const on = catFilter === c.id;
                  return (
                    <button key={c.id} onClick={() => {setCatFilter(c.id);setSubFilter(s.id);}} style={{
                      display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '6px 11px', borderRadius: 8, border: 'none', cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
                      background: on ? '#fff0e6' : 'transparent', color: on ? '#e8793a' : '#6b6259', fontSize: 12.5, fontWeight: on ? 600 : 500
                    }}>
                      <span style={{ width: 7, height: 7, borderRadius: '50%', background: c.color || (on ? '#e8793a' : '#cfc6ba'), flexShrink: 0 }}></span>
                      <span style={{ flex: 1, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</span>
                      <span style={{ fontSize: 11, color: '#bcb3a7', fontVariantNumeric: 'tabular-nums' }}>{counts.byCat[c.id] || 0}</span>
                    </button>);

                })}
              </div>
            </div>);

        })}
      </div>

      {/* ---- СПИСОК ПОЗИЦИЙ ---- */}
      <div className="mat-lib-main" style={{ flex: 1, minWidth: 0 }}>
        <div className="mat-lib-toolbar">
          <div className="mat-lib-toolbar-start">
            <span className="mat-lib-count">
              {filtered.length} {plural(filtered.length, 'позиция', 'позиции', 'позиций')}
              {catFilter && <span className="mat-lib-count-cat"> · {(MD.categoryById(categories, catFilter) || {}).name}</span>}
            </span>
            <MaterialLibraryViewSeg view={viewMode} onChange={setView} />
          </div>
          <button type="button" className="mat-lib-add" onClick={() => add(subFilter || 'material', catFilter || (categories.find((c) => c.subtype === (subFilter || 'material')) || {}).id)}>
            {Icon.plus} Позиция
          </button>
        </div>
        {filtered.length === 0 ? (
          <div className="mat-lib-empty">
            <div className="mat-lib-empty-title">Ничего не найдено</div>
            <p className="mat-lib-empty-hint">Измените фильтр или добавьте позицию.</p>
          </div>
        ) : viewMode === 'tile' ? (
          <div className="mat-lib-tiles">
            {filtered.map((m) => {
              const ft = MD.fieldTypeById(fieldTypes, m.fieldTypeId);
              const used = MD.usageOf(bindings, m.id).length;
              const active = m.id === selId;
              const inspectorOpen = inspectorOpenId === m.id;
              const meta = materialLibraryMetaParts(m, ft, used);
              return (
                <React.Fragment key={m.id}>
                  <article
                    className={'mat-lib-tile' + (active ? ' is-active' : '') + (inspectorOpen ? ' is-inspector' : '') + (m.isActive === false ? ' is-inactive' : '')}
                    onClick={() => pick(m.id)}
                    onDoubleClick={() => openInspector(m.id)}>
                    <div className="mat-lib-tile-top">
                      <MaterialGalleryThumb material={m} size={72} className="mat-lib-tile-thumb" />
                      <div className="mat-lib-tile-menu" onClick={(e) => e.stopPropagation()}>
                        <MaterialLibraryKebab
                          onOpen={() => openInspector(m.id)}
                          onDuplicate={() => duplicate(m.id)}
                          onRemove={() => remove(m.id)} />
                      </div>
                    </div>
                    <h3 className="mat-lib-tile-name">{m.name}</h3>
                    {meta.length > 0 && <p className="mat-lib-tile-meta">{meta.join(' · ')}</p>}
                    <div className="mat-lib-tile-price">
                      <span className="mat-lib-price-main">{money(m.unitPrice)}</span>
                      <span className="mat-lib-price-unit">за {m.unit}</span>
                    </div>
                  </article>
                  {inspectorOpen && selected && selected.id === m.id &&
                  <div className="mat-lib-inspector-wrap mat-lib-inspector-wrap--tile">
                    <MaterialInspector material={selected} fieldTypes={fieldTypes} categories={categories} bindings={bindings}
                      onChange={(patch) => update(selected.id, patch)} onClose={closeInspector} />
                  </div>
                  }
                </React.Fragment>);
            })}
          </div>
        ) : (
          <div className="mat-lib-list">
            {filtered.map((m) => {
              const ft = MD.fieldTypeById(fieldTypes, m.fieldTypeId);
              const used = MD.usageOf(bindings, m.id).length;
              const active = m.id === selId;
              const inspectorOpen = inspectorOpenId === m.id;
              const meta = materialLibraryMetaParts(m, ft, used);
              return (
                <React.Fragment key={m.id}>
                  <div
                    className={'mat-lib-row' + (active ? ' is-active' : '') + (inspectorOpen ? ' is-inspector' : '') + (m.isActive === false ? ' is-inactive' : '')}
                    onClick={() => pick(m.id)}
                    onDoubleClick={() => openInspector(m.id)}>
                    <MaterialGalleryThumb material={m} size={44} />
                    <div className="mat-lib-row-body">
                      <div className="mat-lib-row-name">{m.name}</div>
                      {meta.length > 0 && <div className="mat-lib-row-meta">{meta.join(' · ')}</div>}
                    </div>
                    <div className="mat-lib-row-price">
                      <span className="mat-lib-price-main">{money(m.unitPrice)}</span>
                      <span className="mat-lib-price-unit">за {m.unit}</span>
                    </div>
                    <div className="mat-lib-row-menu" onClick={(e) => e.stopPropagation()}>
                      <MaterialLibraryKebab
                        onOpen={() => openInspector(m.id)}
                        onDuplicate={() => duplicate(m.id)}
                        onRemove={() => remove(m.id)} />
                    </div>
                  </div>
                  {inspectorOpen && selected && selected.id === m.id &&
                  <div className="mat-lib-inspector-wrap">
                    <MaterialInspector material={selected} fieldTypes={fieldTypes} categories={categories} bindings={bindings}
                      onChange={(patch) => update(selected.id, patch)} onClose={closeInspector} />
                  </div>
                  }
                </React.Fragment>);
            })}
          </div>
        )}
      </div>
    </div>);

}

/* ============ ИНСПЕКТОР ПОЗИЦИИ — shared shell (InspectorShell + tabs) ============ */

function MaterialSubcontractorBlock({ m, onChange }) {
  const subs = window.loadSubs ? window.loadSubs() : [];
  if (!subs.length) return null;
  const supplierSubs = subs.filter((s) => (s.services || []).some((v) => v.kind === 'good'));
  const linked = subs.find((s) => s.id === m.subcontractorId);
  const companionServices = linked ? (linked.services || []).filter((v) => v.kind === 'service') : [];
  return (
    <InspectorSection title="Субподрядчик-поставщик" full>
      <FSelect label="Компания-поставщик товара"
        value={m.subcontractorId || ''}
        onChange={(v) => onChange({ subcontractorId: v || null, suggestService: v ? true : false })}
        options={supplierSubs.map((s) => ({ v: s.id, l: s.name }))} placeholder="— не привязан —" />
      {linked && (
        <InspectorCallout variant="success">
          <div style={{ fontWeight: 700, marginBottom: companionServices.length ? 8 : 0 }}>
            {linked.accredited ? 'Аккредитован' : 'На аккредитации'} · {linked.tax}% налог
          </div>
          {companionServices.length > 0 && (
            <React.Fragment>
              <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginBottom: 6 }}>При добавлении товара предложим монтаж этой компании:</div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 }}>
                {companionServices.map((sv) => (
                  <span key={sv.id} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11.5, fontWeight: 600, color: m.suggestService !== false ? 'var(--info)' : 'var(--text-tertiary)', background: m.suggestService !== false ? 'color-mix(in srgb, var(--info) 8%, var(--card))' : 'var(--secondary)', padding: '3px 10px', borderRadius: 980 }}>
                    {sv.label}
                  </span>
                ))}
              </div>
              <FToggle label="Предлагать монтаж при добавлении товара" value={m.suggestService !== false} onChange={(v) => onChange({ suggestService: v })} hint="Клиент сможет отказаться" />
            </React.Fragment>
          )}
        </InspectorCallout>
      )}
    </InspectorSection>
  );
}

function MaterialAttachmentsFooter({ m, onChange, usage, bindings }) {
  const notes = m.matNotes || [];
  const checklist = m.matChecklist || [];
  const links = m.matLinks || [];
  const patchNote = (id, text) => onChange({ matNotes: notes.map((n) => n.id === id ? { ...n, text } : n) });
  const delNote = (id) => onChange({ matNotes: notes.filter((n) => n.id !== id) });
  const addNote = () => onChange({ matNotes: [...notes, { id: MD.uid('mn'), text: '', author: 'Super-admin' }] });
  const patchCheck = (id, patch) => onChange({ matChecklist: checklist.map((c) => c.id === id ? { ...c, ...patch } : c) });
  const delCheck = (id) => onChange({ matChecklist: checklist.filter((c) => c.id !== id) });
  const addCheck = () => onChange({ matChecklist: [...checklist, { id: MD.uid('mc'), text: '', done: false }] });
  const linkOptions = (window._ctorMaterialsLib || []).filter((x) => x.id !== m.id && !links.includes(x.id));
  const addLink = (id) => id && onChange({ matLinks: [...links, id] });
  const delLink = (id) => onChange({ matLinks: links.filter((l) => l !== id) });
  const libById = (id) => (window._ctorMaterialsLib || []).find((x) => x.id === id);

  return (
    <InspectorFooterBand title="Вложения и доступ">
      <InspectorFooterBlock title={'Заметки' + (notes.length ? ' · ' + notes.length : '')}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
          {notes.map((n) => (
            <div key={n.id} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <input value={n.text} onChange={(e) => patchNote(n.id, e.target.value)} placeholder="напр. распаковка дорогих декор. товаров…"
                style={{ ...insStyles.field, flex: 1, minWidth: 0 }} />
              <button type="button" onClick={() => delNote(n.id)} style={mlToolDel} aria-label="Удалить заметку">×</button>
            </div>
          ))}
          <button type="button" onClick={addNote} style={mlToolAdd}>+ Заметка</button>
        </div>
      </InspectorFooterBlock>

      <InspectorFooterBlock title={'Чек-лист' + (checklist.length ? ' · ' + checklist.filter((c) => c.done).length + '/' + checklist.length : '')}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
          {checklist.map((c) => (
            <div key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
              <button type="button" onClick={() => patchCheck(c.id, { done: !c.done })} title="Отметить"
                style={{ width: 17, height: 17, borderRadius: 5, flexShrink: 0, border: '1.5px solid ' + (c.done ? 'var(--success)' : 'var(--c-border)'), background: c.done ? 'var(--success)' : 'var(--card)', color: '#fff', cursor: 'pointer', display: 'grid', placeItems: 'center', fontSize: 10, padding: 0 }}>{c.done ? '✓' : ''}</button>
              <input value={c.text} onChange={(e) => patchCheck(c.id, { text: e.target.value })} placeholder="напр. привязать сценарий умного дома…"
                style={{ ...insStyles.field, flex: 1, minWidth: 0, textDecoration: c.done ? 'line-through' : 'none', color: c.done ? 'var(--text-tertiary)' : 'var(--foreground)' }} />
              <button type="button" onClick={() => delCheck(c.id)} style={mlToolDel} aria-label="Удалить пункт">×</button>
            </div>
          ))}
          <button type="button" onClick={addCheck} style={mlToolAdd}>+ Пункт чек-листа</button>
        </div>
      </InspectorFooterBlock>

      <InspectorFooterBlock title={'Связанные позиции' + (links.length ? ' · ' + links.length : '')}>
        {links.map((id) => {
          const lm = libById(id);
          return (
            <div key={id} style={{ display: 'flex', alignItems: 'center', gap: 7, padding: '5px 9px', background: 'var(--secondary)', borderRadius: 8, marginBottom: 5 }}>
              <span style={{ flex: 1, minWidth: 0, fontSize: 12.5, fontWeight: 600, color: lm ? 'var(--foreground)' : 'var(--destructive)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{lm ? lm.name : 'позиция удалена'}</span>
              <button type="button" onClick={() => delLink(id)} style={mlToolDel} aria-label="Отвязать">×</button>
            </div>
          );
        })}
        {linkOptions.length > 0 && (
          <select value="" onChange={(e) => { addLink(e.target.value); e.target.value = ''; }}
            style={{ ...insStyles.field, cursor: 'pointer' }}>
            <option value="">+ Связать с позицией…</option>
            {linkOptions.map((x) => <option key={x.id} value={x.id}>{x.name}</option>)}
          </select>
        )}
      </InspectorFooterBlock>

      <InspectorFooterBlock title={'Привязки к работам · ' + usage.length}>
        {usage.length === 0 ? (
          <div style={insStyles.hint}>Материал ещё не задан по умолчанию ни на одном узле каталога. Привязки настраиваются в инспекторе узла (вкладка «Иерархия») или на «Связях с работами».</div>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {usage.map((u, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '7px 11px', background: 'var(--secondary)', borderRadius: 9 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--foreground)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{nodeName(u.nodeId)}</div>
                  <div style={{ fontSize: 11, color: 'var(--text-secondary)' }}>{u.binding.defaultQty} {u.binding.unit} · {u.binding.qtyFormula === 'fixed' ? 'фикс.' : u.binding.qtyFormula}</div>
                </div>
                {u.binding.isRequired && <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--destructive)', background: 'color-mix(in srgb, var(--destructive) 10%, var(--card))', padding: '1px 7px', borderRadius: 980 }}>обяз.</span>}
              </div>
            ))}
          </div>
        )}
      </InspectorFooterBlock>

      <InspectorFooterBlock title="Видимость по ролям" className="ins-footer-block--wide">
        <VisibilityFlags value={m.visibility} onChange={(v) => onChange({ visibility: v })} />
        <div style={insStyles.hint}>Где позиция показывается в смете и интерфейсах ролей.</div>
      </InspectorFooterBlock>
    </InspectorFooterBand>
  );
}

function MaterialInspector({ material, fieldTypes, categories, bindings, onChange, onClose }) {
  const [section, setSection] = mlUseState('main');

  mlUseEffect(() => {
    setSection('main');
  }, [material && material.id]);

  if (!material) return null;

  const m = material;
  const ft = MD.fieldTypeById(fieldTypes, m.fieldTypeId);
  const sub = MD.subtypeById(m.subtype);
  const usage = MD.usageOf(bindings, m.id);
  const catsForSub = categories.filter((c) => c.subtype === m.subtype);
  const setType = (k, v) => onChange({ typeData: { ...(m.typeData || {}), [k]: v } });
  const marginPct = m.unitPrice > 0 && m.clientUnitPrice > 0 ? Math.round((1 - m.unitPrice / m.clientUnitPrice) * 100) : null;

  const sections = [
    { id: 'main', label: 'Основное' },
    { id: 'gallery', label: 'Галерея', badge: matGalleryCount(m) || null },
    { id: 'specs', label: 'Характеристики', hidden: !ft, badge: ft ? (ft.fields || []).length : null },
    { id: 'economy', label: 'Экономика' },
    { id: 'instructions', label: 'Инструкции' },
  ];

  const headBadges = (
    <React.Fragment>
      {sub && (
        <span style={{ fontSize: 11, fontWeight: 700, color: '#fff', background: sub.color, padding: '3px 9px', borderRadius: 980 }}>
          {sub.icon} {sub.name}
        </span>
      )}
      <MaterialKindChip kind={m.materialKind} small />
    </React.Fragment>
  );

  return (
    <InspectorShell
      key={m.id}
      head={<InspectorHead badges={headBadges} title={m.name} subtitle={m.sku || null} onClose={onClose} />}
      nav={<InspectorSectionNav sections={sections} active={section} onChange={setSection} ariaLabel="Разделы позиции материала" />}
      footer={<MaterialAttachmentsFooter m={m} onChange={onChange} usage={usage} bindings={bindings} />}
    >
      <div className="ins-tabpanel" role="tabpanel">
        {section === 'main' && (
          <div className="ins-tabpanel-grid">
            <InspectorSection title="Идентификация">
              <FText label="Название" value={m.name} onChange={(v) => onChange({ name: v })} />
              <InspectorFieldGrid cols={2}>
                <FText label="SKU / артикул" value={m.sku} onChange={(v) => onChange({ sku: v })} mono />
                <FText label="Поставщик" value={m.supplier} onChange={(v) => onChange({ supplier: v })} />
              </InspectorFieldGrid>
            </InspectorSection>
            <InspectorSection title="Классификация">
              <FSelect label="Подвид" value={m.subtype} onChange={(v) => onChange({ subtype: v, categoryId: (categories.find((c) => c.subtype === v) || {}).id })} options={MD.SUBTYPES.map((s) => ({ v: s.id, l: s.name }))} />
              <FSelect label="Категория" value={m.categoryId} onChange={(v) => onChange({ categoryId: v })} options={catsForSub.map((c) => ({ v: c.id, l: c.name }))} placeholder="—" />
              <FSeg label="Класс" value={m.materialKind} onChange={(v) => onChange({ materialKind: v })}
                options={[{ v: 'rough', l: 'Черновой', color: '#92620a' }, { v: 'finish', l: 'Чистовой', color: '#7c3aed' }]} />
              <FSelect label="Тип полей (схема характеристик)" value={m.fieldTypeId} onChange={(v) => onChange({ fieldTypeId: v })} options={fieldTypes.map((f) => ({ v: f.id, l: f.name }))} placeholder="Без характеристик" />
              <FToggle label="Активна" value={m.isActive !== false} onChange={(v) => onChange({ isActive: v })} hint="Неактивные не попадают в смету" />
            </InspectorSection>
            <MaterialSubcontractorBlock m={m} onChange={onChange} />
          </div>
        )}

        {section === 'gallery' && (
          <MaterialGalleryEditor material={m} onChange={onChange} />
        )}

        {section === 'specs' && ft && (
          <InspectorSection title={'Характеристики · ' + ft.name} plain>
            <InspectorFieldGrid cols={2}>
              {ft.fields.map((f) => {
                const val = (m.typeData || {})[f.key];
                if (f.type === 'select') return <FSelect key={f.key} label={f.label} value={val} onChange={(v) => setType(f.key, v)} options={(f.options || []).map((o) => ({ v: o, l: o }))} placeholder="—" />;
                if (f.type === 'number') return <FNum key={f.key} label={f.label} value={val} onChange={(v) => setType(f.key, v)} suffix={f.unit} step={0.01} />;
                return <FText key={f.key} label={f.label + (f.type === 'dim' ? ' (Ш×В×Г)' : '')} value={val} onChange={(v) => setType(f.key, v)} placeholder={f.type === 'dim' ? '799×299×219' : ''} />;
              })}
            </InspectorFieldGrid>
          </InspectorSection>
        )}

        {section === 'specs' && !ft && (
          <InspectorEmptyState title="Нет схемы характеристик" hint="Выберите тип полей во вкладке «Основное», чтобы задать параметры (краска, сплит, плёнка…)." />
        )}

        {section === 'economy' && (
          <div className="ins-tabpanel-grid">
            <InspectorSection title="Единицы и расход">
              <FSelect label="Единица" value={m.unit} onChange={(v) => onChange({ unit: v })} options={MD.UNITS.map((u) => ({ v: u, l: u }))} />
              <InspectorFieldGrid cols={2}>
                <FNum label="Фасовка" value={m.packageSize} onChange={(v) => onChange({ packageSize: v })} suffix={m.unit} step={0.01} />
                <FSelect label="Округление" value={m.roundingRule} onChange={(v) => onChange({ roundingRule: v })} options={MD.ROUNDING.map((r) => ({ v: r.id, l: r.label }))} />
              </InspectorFieldGrid>
              <InspectorFieldGrid cols={2}>
                <FNum label="Расход" value={m.consumptionRate} onChange={(v) => onChange({ consumptionRate: v })} suffix="" step={0.01} hint="на ед. работы" />
                <FSelect label="Ед. расхода" value={m.consumptionUnit} onChange={(v) => onChange({ consumptionUnit: v })} options={MD.CONSUMPTION_UNITS.map((u) => ({ v: u, l: u }))} />
              </InspectorFieldGrid>
            </InspectorSection>
            <InspectorSection title="Цены">
              <InspectorFieldGrid cols={2}>
                <FNum label="Закуп. цена" value={m.unitPrice} onChange={(v) => onChange({ unitPrice: v })} suffix="₽" step={10} />
                <FNum label="Цена клиенту" value={m.clientUnitPrice} onChange={(v) => onChange({ clientUnitPrice: v })} suffix="₽" step={10} />
              </InspectorFieldGrid>
              {marginPct != null && (
                <InspectorCallout variant="success">
                  Маржа {marginPct}% · +{money(m.clientUnitPrice - m.unitPrice)} за {m.unit}
                </InspectorCallout>
              )}
              <InspectorMetric label={'Цена клиенту за ' + (m.unit || 'ед.')} value={money(m.clientUnitPrice)} accent />
            </InspectorSection>
            <InspectorSection title="Закупка" full>
              <InspectorFieldGrid cols={2}>
                <FNum label="Срок поставки" value={m.leadTimeDays} onChange={(v) => onChange({ leadTimeDays: v })} suffix="дн" />
                <FSelect label="Триггер заказа" value={m.orderTrigger} onChange={(v) => onChange({ orderTrigger: v })} options={MD.ORDER_TRIGGERS.map((t) => ({ v: t.id, l: t.label }))} />
              </InspectorFieldGrid>
            </InspectorSection>
          </div>
        )}

        {section === 'instructions' && (
          <div className="ins-tabpanel-grid">
            <InspectorSection title="Технология монтажа" full>
              <FText label="Текст инструкции" value={m.instructions} onChange={(v) => onChange({ instructions: v })} placeholder="Как монтировать / наносить…" multiline />
            </InspectorSection>
            <InspectorSection title="Контроль исполнения">
              <FToggle label="Требует ознакомления" value={m.requiresAck} onChange={(v) => onChange({ requiresAck: v })} hint="Гейт «Ознакомлен» перед сдачей задачи" />
              <FToggle label="Согласование клиентом" value={m.requiresClientApproval} onChange={(v) => onChange({ requiresClientApproval: v })} hint="Для чистовых материалов и пресетов" />
            </InspectorSection>
          </div>
        )}
      </div>
    </InspectorShell>
  );
}

/* nodeName — подпись узла по id (читает живой каталог из ConstructorData) */
function nodeName(nodeId) {
  try {
    const cat = window.ConstructorData.loadCatalog();
    const map = MD.nodeLabelMap(cat);
    return map[nodeId] && map[nodeId].label || nodeId;
  } catch {return nodeId;}
}

Object.assign(window, { MaterialLibraryPanel, MaterialInspector, MaterialKindChip, SubtypeChip, MaterialGalleryThumb, MaterialGalleryEditor, materialMoney: money });