/* Reusable list controls: Filter / Sort / Columns popovers + row context menu. */
const { useState: useStateLCtl } = React;

function SortPopover({ options, sortKey, sortDir, onChange }) {
  const cur = options.find(o => o.k === sortKey);
  return (
    <Dropdown align="left" width={240} trigger={({ toggle }) => (
      <Button variant="ghost" size="md" icon="arrow-up-down" title={'Sort' + (cur ? ': ' + cur.label : '')} onClick={toggle} />
    )}>
      {({ close }) => (
        <React.Fragment>
          <div className="mn-dd-h">Sort by</div>
          <div className="mn-dd-list">
            {options.map(o => (
              <button key={o.k} className="mn-dd-item" style={{ alignItems: 'center' }} onClick={() => { onChange(o.k, sortKey === o.k && sortDir === 'asc' ? 'desc' : 'asc'); }}>
                <span style={{ flex: 1 }} className="mn-dd-item-tt">{o.label}</span>
                {sortKey === o.k ? <Icon name={sortDir === 'asc' ? 'arrow-up' : 'arrow-down'} size="sm" color="primary" /> : <Icon name="minus" size="sm" color="text-3" />}
              </button>
            ))}
          </div>
        </React.Fragment>
      )}
    </Dropdown>
  );
}

const COLUMNS_DND_CSS = '.mn-col-row{display:flex;align-items:center;gap:6px;padding:3px 4px;border-radius:var(--radi-sm)}.mn-col-row--over{box-shadow:inset 0 2px 0 var(--primary-main)}.mn-col-row--drag{opacity:.45}.mn-col-grip{cursor:grab;color:var(--text-3);display:inline-flex}.mn-col-grip:active{cursor:grabbing}';
// Columns popover: show/hide checkboxes. When `order`+`onReorder` are passed, non-pinned rows become drag-reorderable.
function ColumnsPopover({ columns, hidden, onToggle, onReset, order, onReorder }) {
  const [q, setQ] = useStateLCtl('');
  const [dragK, setDragK] = useStateLCtl(null);
  const [overK, setOverK] = useStateLCtl(null);
  const reorderable = !!(order && onReorder);
  const byK = {}; columns.forEach(c => { byK[c.k] = c; });
  const drop = (target) => { if (dragK && dragK !== target) { const a = order.filter(k => k !== dragK); const ti = a.indexOf(target); a.splice(ti < 0 ? a.length : ti, 0, dragK); onReorder(a); } setDragK(null); setOverK(null); };
  const row = (c, draggable) => (
    <div key={c.k} className={'mn-col-row' + (draggable && overK === c.k && dragK && dragK !== c.k ? ' mn-col-row--over' : '') + (dragK === c.k ? ' mn-col-row--drag' : '')}
         draggable={draggable || undefined}
         onDragStart={draggable ? (() => setDragK(c.k)) : undefined}
         onDragOver={draggable ? ((e) => { e.preventDefault(); if (overK !== c.k) setOverK(c.k); }) : undefined}
         onDrop={draggable ? (() => drop(c.k)) : undefined}
         onDragEnd={draggable ? (() => { setDragK(null); setOverK(null); }) : undefined}>
      {reorderable ? <span className="mn-col-grip" style={{ visibility: draggable ? 'visible' : 'hidden' }} title="Drag to reorder"><Icon name="grip-vertical" size="sm" stroke="2" /></span> : null}
      <label className="mn-check-row" style={{ flex: 1, opacity: c.locked ? 0.55 : 1, padding: 0, margin: 0 }}>
        <input type="checkbox" className="mn-checkbox" checked={!hidden.includes(c.k)} disabled={c.locked} onChange={() => onToggle(c.k)} />
        <span className="mn-check-lbl">{c.label}</span>
      </label>
    </div>
  );
  return (
    <Dropdown align="left" width={280} trigger={({ toggle }) => (
      <Button variant="ghost" size="md" icon="columns-3" title="Columns" onClick={toggle} />
    )}>
      {() => {
        const ql = q.trim().toLowerCase();
        const match = (c) => c && (!ql || c.label.toLowerCase().includes(ql));
        const pinned = reorderable ? columns.filter(c => c.pinned && match(c)) : [];
        const ordered = reorderable ? order.map(k => byK[k]).filter(match) : [];
        const flat = reorderable ? [] : columns.filter(match);
        const empty = reorderable ? (!pinned.length && !ordered.length) : !flat.length;
        return (
          <React.Fragment>
            <style>{COLUMNS_DND_CSS}</style>
            <div className="mn-dd-h">Columns{onReset ? <button type="button" className="mn-login-link" style={{ fontSize: 12 }} onClick={onReset}>Reset</button> : null}</div>
            <div style={{ padding: '8px 12px 4px' }}><Input fullWidth icon="search" placeholder="Find a column…" value={q} onChange={setQ} /></div>
            <div className="mn-dd-list" style={{ padding: '4px 12px 8px', maxHeight: 300, overflowY: 'auto' }}>
              {reorderable ? (
                <React.Fragment>
                  {pinned.map(c => row(c, false))}
                  {ordered.length ? <div className="mn-eyebrow" style={{ margin: '8px 0 4px' }}>{ql ? 'Matches (clear search to reorder)' : 'Drag to reorder'}</div> : null}
                  {ordered.map(c => row(c, !ql))}
                </React.Fragment>
              ) : flat.map(c => row(c, false))}
              {empty ? <div style={{ padding: 8, color: 'var(--text-3)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)' }}>No columns match.</div> : null}
            </div>
          </React.Fragment>
        );
      }}
    </Dropdown>
  );
}

function FilterPopover({ count, onClear, children, width = 300 }) {
  return (
    <Dropdown align="left" width={width} trigger={({ toggle }) => (
      <Button variant="ghost" size="md" icon="filter" onClick={toggle}>Filters{count ? ' · ' + count : ''}</Button>
    )}>
      {({ close }) => (
        <React.Fragment>
          <div className="mn-dd-h">Filters {count ? <button type="button" className="mn-login-link" style={{ fontSize: 12 }} onClick={() => { onClear(); }}>Clear all</button> : null}</div>
          <div style={{ padding: 12 }}>{typeof children === 'function' ? children({ close }) : children}</div>
        </React.Fragment>
      )}
    </Dropdown>
  );
}

function FilterCheckGroup({ label, options, selected, onToggle }) {
  return (
    <div style={{ marginBottom: 12 }}>
      <div className="mn-eyebrow" style={{ marginBottom: 6 }}>{label}</div>
      <div className="mn-stack" style={{ gap: 2 }}>
        {options.map(o => (
          <label key={o.value} className="mn-check-row" style={{ padding: '3px 0' }}>
            <input type="checkbox" className="mn-checkbox" checked={selected.includes(o.value)} onChange={() => onToggle(o.value)} />
            <span className="mn-check-lbl">{o.label}</span>
          </label>
        ))}
      </div>
    </div>
  );
}

function RowMenu({ items }) {
  return (
    <Dropdown align="right" width={200} trigger={({ toggle }) => (
      <span onClick={(e) => e.stopPropagation()}><IconButton icon="more-horizontal" size="sm" title="Row actions" onClick={toggle} /></span>
    )}>
      {({ close }) => (
        <div className="mn-dd-list" onClick={(e) => e.stopPropagation()}>
          {items.filter(Boolean).map((it, i) => (
            <button key={i} className="mn-dd-item" style={{ alignItems: 'center' }} onClick={() => { close(); it.onClick && it.onClick(); }}>
              <Icon name={it.icon} size="sm" color={it.danger ? 'error' : 'text-2'} />
              <span className="mn-dd-item-tt" style={{ color: it.danger ? 'var(--error-main)' : undefined }}>{it.label}</span>
            </button>
          ))}
        </div>
      )}
    </Dropdown>
  );
}

/* ===========================================================================
   Generic, config-driven filter pattern — shared by Leads / Clients / Deals / Tasks.
   A `sections` config drives a right-side Drawer (type-specific controls), an
   active-filter Bar, and the predicate application. One pattern everywhere.

   section = {
     key, label,
     kind: 'pills' | 'people' | 'range' | 'daterange',
     options,            // [{ value, label, count, node?, user? }] (pills/people) — built per render with counts
     min, max, step, gradient, bands,   // range. bands: [{ label, from, to }]
     format,             // range: (n) => string (chip + readout)
     presets,            // daterange: [{ label, days }]
     match,              // (row, value) => bool. value = state[key]
   }
   state[key]: array (pills/people) | [min,max] (range) | { from, to } (daterange)
   =========================================================================== */
function filterEmpty(sections) {
  const s = {};
  sections.forEach(x => { s[x.key] = x.kind === 'range' ? [x.min != null ? x.min : 0, x.max != null ? x.max : 100] : x.kind === 'daterange' ? { from: '', to: '' } : []; });
  return s;
}
function sectionActive(sec, v) {
  if (v == null) return false;
  if (sec.kind === 'range') return v[0] > (sec.min != null ? sec.min : 0) || v[1] < (sec.max != null ? sec.max : 100);
  if (sec.kind === 'daterange') return !!(v.from || v.to);
  return v.length > 0;
}
function filterCount(sections, state) {
  return sections.reduce((a, sec) => {
    const v = state[sec.key];
    if (!sectionActive(sec, v)) return a;
    return a + (sec.kind === 'range' || sec.kind === 'daterange' ? 1 : v.length);
  }, 0);
}
function applyFilters(rows, sections, state) {
  let r = rows;
  sections.forEach(sec => { const v = state[sec.key]; if (sectionActive(sec, v) && sec.match) r = r.filter(row => sec.match(row, v)); });
  return r;
}
function createdPresetRange(days) {
  const now = window.MonolitData.NOW;
  const iso = (dt) => dt.toISOString().slice(0, 10);
  return { from: iso(new Date(now.getTime() - (days - 1) * 86400000)), to: iso(new Date(now)) };
}

function FilterPillBtn({ on, onClick, children, count }) {
  return <button type="button" onClick={onClick} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '4px 10px', borderRadius: 'var(--radi-full)', border: '1px solid ' + (on ? 'var(--primary-main)' : 'var(--border-1)'), background: on ? 'var(--primary-low)' : 'transparent', color: 'var(--text-1)', cursor: 'pointer', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)' }}>{children}{count != null ? <span className="mn-mono" style={{ fontSize: 10, color: 'var(--text-3)' }}>{count}</span> : null}</button>;
}

function FilterRange({ value, onChange, min = 0, max = 100, gradient, bands, format }) {
  const fmt = format || ((n) => String(n));
  const pct = (n) => ((n - min) / (max - min) * 100);
  const on = value[0] > min || value[1] < max;
  return (
    <div style={{ padding: '4px 2px 6px', width: '100%' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
        <input type="number" min={min} max={max} value={value[0]} onChange={(e) => { const r = e.target.value === '' ? min : +e.target.value; onChange([Math.max(min, Math.min(r, value[1])), value[1]]); }} style={{ flex: 1, minWidth: 0, height: 34, padding: '0 10px', textAlign: 'center', border: '1px solid var(--border-1)', borderRadius: 'var(--radi-sm)', font: 'var(--font-weight-medium) 13px/1 var(--font-mono)', color: 'var(--text-1)', background: 'var(--bg-1)', boxSizing: 'border-box' }} />
        <span style={{ color: 'var(--text-3)', flex: 'none' }}>–</span>
        <input type="number" min={min} max={max} value={value[1]} onChange={(e) => { const r = e.target.value === '' ? max : +e.target.value; onChange([value[0], Math.min(max, Math.max(r, value[0]))]); }} style={{ flex: 1, minWidth: 0, height: 34, padding: '0 10px', textAlign: 'center', border: '1px solid var(--border-1)', borderRadius: 'var(--radi-sm)', font: 'var(--font-weight-medium) 13px/1 var(--font-mono)', color: 'var(--text-1)', background: 'var(--bg-1)', boxSizing: 'border-box' }} />
        {on ? <button type="button" className="mn-login-link" style={{ fontSize: 11, background: 'none', border: 0, cursor: 'pointer', flex: 'none' }} onClick={() => onChange([min, max])}>Reset</button> : null}
      </div>
      <div className="mn-rangewrap">
        <div className="mn-rng-track" style={gradient ? { background: gradient } : null} />
        <div className="mn-rng-mask" style={{ left: 0, width: pct(value[0]) + '%' }} />
        <div className="mn-rng-mask" style={{ left: pct(value[1]) + '%', width: (100 - pct(value[1])) + '%' }} />
        <input type="range" min={min} max={max} value={value[0]} onChange={(e) => onChange([Math.min(+e.target.value, value[1]), value[1]])} aria-label="Minimum" />
        <input type="range" min={min} max={max} value={value[1]} onChange={(e) => onChange([value[0], Math.max(+e.target.value, value[0])])} aria-label="Maximum" />
      </div>
      {bands && bands.length ? (
        <div style={{ position: 'relative', height: 16, marginTop: 4 }}>
          {bands.map(b => { const c = b.from + (b.to - b.from) / 2; const sel = value[0] === b.from && value[1] === b.to; return <button key={b.label} type="button" onClick={() => onChange(sel ? [min, max] : [b.from, b.to])} style={{ position: 'absolute', left: pct(c) + '%', transform: 'translateX(-50%)', border: 0, background: 'none', cursor: 'pointer', fontSize: 11, fontWeight: sel ? 700 : 500, color: b.tone ? 'var(--' + b.tone + '-main)' : 'var(--text-2)', textDecoration: sel ? 'underline' : 'none', whiteSpace: 'nowrap' }}>{b.label}</button>; })}
        </div>
      ) : null}
    </div>
  );
}

function FilterPeople({ options, selected, onToggle }) {
  const [q, setQ] = useStateLCtl('');
  const ql = q.trim().toLowerCase();
  const list = options.filter(o => !ql || o.label.toLowerCase().includes(ql)).slice().sort((a, b) => (selected.includes(b.value) ? 1 : 0) - (selected.includes(a.value) ? 1 : 0));
  return (
    <div style={{ paddingBottom: 8 }}>
      {options.length > 6 ? <div style={{ padding: '0 8px 6px' }}><Input fullWidth icon="search" placeholder="Search people…" value={q} onChange={setQ} /></div> : null}
      <div className="mn-stack" style={{ gap: 1, maxHeight: 240, overflowY: 'auto' }}>
        {list.map(o => { const on = selected.includes(o.value); return (
          <button key={o.value} type="button" className="mn-filter-opt" onClick={() => onToggle(o.value)} style={{ background: on ? 'var(--primary-low)' : 'transparent' }}>
            {o.user ? <Avatar user={o.user} size={20} /> : <span className="mn-feed-icon" style={{ width: 20, height: 20 }}><Icon name="user-x" size="xs" stroke="2" /></span>}
            <span style={{ flex: 1, font: 'var(--font-weight-medium) 13px/16px var(--font-sans)', color: 'var(--text-1)' }}>{o.label}</span>
            <span className="mn-mono mn-muted" style={{ fontSize: 11 }}>{o.count}</span>
            {on ? <Icon name="check" size="sm" color="primary" /> : null}
          </button>
        ); })}
        {!list.length ? <div style={{ padding: '6px 8px', color: 'var(--text-3)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)' }}>No people match.</div> : null}
      </div>
    </div>
  );
}

function FilterDateRange({ value, onChange, presets }) {
  const inStyle = { width: '100%', padding: '7px 8px', border: '1px solid var(--border-1)', borderRadius: 'var(--radi-sm)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)', color: 'var(--text-1)', background: 'var(--bg-1)', boxSizing: 'border-box' };
  return (
    <div style={{ padding: '2px 8px 12px' }}>
      <div style={{ display: 'flex', gap: 8 }}>
        <div style={{ flex: 1 }}><div className="mn-eyebrow" style={{ marginBottom: 4 }}>From</div><input type="date" value={value.from || ''} max={value.to || undefined} onChange={(e) => onChange({ ...value, from: e.target.value })} style={inStyle} /></div>
        <div style={{ flex: 1 }}><div className="mn-eyebrow" style={{ marginBottom: 4 }}>To</div><input type="date" value={value.to || ''} min={value.from || undefined} onChange={(e) => onChange({ ...value, to: e.target.value })} style={inStyle} /></div>
      </div>
      {presets && presets.length ? <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 8 }}>{presets.map(p => { const r = createdPresetRange(p.days); const on = value.from === r.from && value.to === r.to; return <FilterPillBtn key={p.label} on={on} onClick={() => onChange(on ? { from: '', to: '' } : r)}>{p.label}</FilterPillBtn>; })}</div> : null}
    </div>
  );
}

function FilterBarPill({ children, onRemove, onClick }) {
  return <span onClick={onClick} title={onClick ? 'Edit filter' : undefined} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '3px 6px 3px 8px', borderRadius: 'var(--radi-full)', border: '1px solid var(--border-1)', background: 'var(--bg-1)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)', color: 'var(--text-1)', cursor: onClick ? 'pointer' : 'default' }}>{children}<button type="button" onClick={(e) => { e.stopPropagation(); onRemove && onRemove(); }} style={{ border: 0, background: 'none', cursor: 'pointer', display: 'inline-flex', color: 'var(--text-3)', padding: 0 }} title="Remove"><Icon name="x" size="xs" stroke="2" /></button></span>;
}

const FILTER_DRAWER_CSS = '.mn-filter-opt{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--radi-sm);cursor:pointer;width:100%;border:0;text-align:left}.mn-filter-opt:hover{background:var(--bg-2)}.mn-filter-sec-h{display:flex;align-items:center;gap:8px;width:100%;border:0;background:transparent;padding:12px 0 8px;cursor:pointer}.mn-rangewrap{position:relative;height:24px}.mn-rng-track{position:absolute;top:9px;left:0;right:0;height:6px;border-radius:3px;background:var(--bg-3)}.mn-rng-mask{position:absolute;top:9px;height:6px;background:var(--bg-3);opacity:.65;border-radius:3px}.mn-rangewrap input[type=range]{position:absolute;top:0;left:0;width:100%;height:24px;margin:0;background:transparent;-webkit-appearance:none;appearance:none;pointer-events:none}.mn-rangewrap input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;pointer-events:auto;width:16px;height:16px;border-radius:50%;background:var(--bg-1);border:2px solid var(--primary-main);cursor:pointer;box-shadow:0 1px 2px rgba(0,0,0,.25)}.mn-rangewrap input[type=range]::-moz-range-thumb{pointer-events:auto;width:16px;height:16px;border-radius:50%;background:var(--bg-1);border:2px solid var(--primary-main);cursor:pointer}.mn-rangewrap input[type=range]::-webkit-slider-runnable-track{background:transparent;border:0}.mn-rangewrap input[type=range]::-moz-range-track{background:transparent;border:0}.mn-facet-row--focus{background:var(--primary-low);border-radius:var(--radi-sm);box-shadow:0 0 0 6px var(--primary-low)}';

// Right-side filter drawer. `eyebrow`/`title` label it; `sections` is the config; staged + Apply.
function FilterDrawer({ eyebrow, title, sections, state, onApply, onClose }) {
  const [d, setD] = useStateLCtl(() => { const s = {}; sections.forEach(x => { s[x.key] = state[x.key]; }); return s; });
  const [q, setQ] = useStateLCtl('');
  const [open, setOpen] = useStateLCtl(() => { const o = {}; sections.forEach(x => { o[x.key] = true; }); return o; });
  const set = (key, v) => setD(s => ({ ...s, [key]: v }));
  const toggleArr = (key) => (v) => setD(s => ({ ...s, [key]: s[key].includes(v) ? s[key].filter(x => x !== v) : [...s[key], v] }));
  const total = filterCount(sections, d);
  const ql = q.trim().toLowerCase();
  const labelFor = (sec, v) => ((sec.options || []).find(o => o.value === v) || {}).label || v;
  const footer = (
    <div style={{ display: 'flex', width: '100%', justifyContent: 'space-between' }}>
      <Button variant="ghost" size="md" onClick={() => setD(filterEmpty(sections))} disabled={!total}>Clear all</Button>
      <Button variant="primary" size="md" icon="check" onClick={() => { onApply(d); onClose(); }}>Apply{total ? ' · ' + total : ''}</Button>
    </div>
  );
  return (
    <Drawer open onClose={onClose} width={400} eyebrow={eyebrow} title={title || 'Filters'} subtitle="Refine the list, then apply." footer={footer}>
      <style>{FILTER_DRAWER_CSS}</style>
      <div style={{ position: 'sticky', top: 0, background: 'var(--bg-1)', zIndex: 1, paddingBottom: 8 }}>
        <Input fullWidth icon="search" placeholder="Filter options…" value={q} onChange={setQ} />
        {total ? (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 10 }}>
            {sections.flatMap(sec => {
              const v = d[sec.key];
              if (!sectionActive(sec, v)) return [];
              if (sec.kind === 'range') return [<span key={sec.key} className="mn-chip mn-chip--md mn-chip--tag" style={{ gap: 4 }}>{(sec.format || String)(v[0])}–{(sec.format || String)(v[1])}<span onClick={() => set(sec.key, [sec.min != null ? sec.min : 0, sec.max != null ? sec.max : 100])} style={{ cursor: 'pointer', display: 'inline-flex' }}><Icon name="x" size="xs" stroke="2" /></span></span>];
              if (sec.kind === 'daterange') return [<span key={sec.key} className="mn-chip mn-chip--md mn-chip--tag" style={{ gap: 4 }}>{(v.from || '…') + ' – ' + (v.to || '…')}<span onClick={() => set(sec.key, { from: '', to: '' })} style={{ cursor: 'pointer', display: 'inline-flex' }}><Icon name="x" size="xs" stroke="2" /></span></span>];
              return v.map(val => <span key={sec.key + val} className="mn-chip mn-chip--md mn-chip--tag" style={{ gap: 4 }}>{labelFor(sec, val)}<span onClick={() => toggleArr(sec.key)(val)} style={{ cursor: 'pointer', display: 'inline-flex' }}><Icon name="x" size="xs" stroke="2" /></span></span>);
            })}
          </div>
        ) : null}
      </div>
      {sections.map(sec => {
        const v = d[sec.key];
        const isOpen = open[sec.key];
        const selCount = sec.kind === 'range' || sec.kind === 'daterange' ? (sectionActive(sec, v) ? 1 : 0) : v.length;
        if ((sec.kind === 'pills' || sec.kind === 'people') && !(sec.options || []).length) return null;
        const opts = (sec.kind === 'pills') ? (sec.options || []).filter(o => !ql || o.label.toLowerCase().includes(ql)) : (sec.options || []);
        return (
          <div key={sec.key} style={{ borderTop: '1px solid var(--border-1)' }}>
            <button type="button" className="mn-filter-sec-h" onClick={() => setOpen(o => ({ ...o, [sec.key]: !o[sec.key] }))}>
              <Icon name={isOpen ? 'chevron-down' : 'chevron-right'} size="sm" color="text-3" />
              <span className="mn-eyebrow" style={{ margin: 0, flex: 1, textAlign: 'left' }}>{sec.label}</span>
              {selCount ? <span className="mn-preset-c">{selCount}</span> : null}
            </button>
            {!isOpen ? null :
              sec.kind === 'range' ? <FilterRange value={v} onChange={(r) => set(sec.key, r)} min={sec.min} max={sec.max} gradient={sec.gradient} bands={sec.bands} format={sec.format} /> :
              sec.kind === 'daterange' ? <FilterDateRange value={v} onChange={(r) => set(sec.key, r)} presets={sec.presets} /> :
              sec.kind === 'people' ? <FilterPeople options={sec.options || []} selected={v} onToggle={toggleArr(sec.key)} /> :
              !opts.length ? <div style={{ padding: '2px 8px 10px', color: 'var(--text-3)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)' }}>No matches.</div> :
              (<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, padding: '2px 8px 12px' }}>
                {opts.map(o => { const on = v.includes(o.value); return o.node
                  ? <button key={o.value} type="button" onClick={() => toggleArr(sec.key)(o.value)} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, border: 0, background: 'none', padding: 0, cursor: 'pointer', borderRadius: 'var(--radi-full)', outline: on ? '2px solid var(--primary-main)' : 'none', outlineOffset: 2, opacity: on ? 1 : 0.5 }}>{o.node}<span className="mn-mono" style={{ fontSize: 10, color: 'var(--text-3)' }}>{o.count}</span></button>
                  : <FilterPillBtn key={o.value} on={on} onClick={() => toggleArr(sec.key)(o.value)} count={o.count}>{o.label}</FilterPillBtn>; })}
              </div>)}
          </div>
        );
      })}
    </Drawer>
  );
}

// Active-filter bar (chips per active value, removable) + Reset all. `state`/`onChange(key,value)`.
function FilterBar({ sections, state, onChange, onReset }) {
  const total = filterCount(sections, state);
  if (!total) return null;
  const toggleArr = (key, val) => { const cur = state[key]; onChange(key, cur.includes(val) ? cur.filter(x => x !== val) : [...cur, val]); };
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', marginBottom: 12 }}>
      <span className="mn-eyebrow" style={{ margin: '0 4px 0 0' }}>Filters</span>
      {sections.flatMap(sec => {
        const v = state[sec.key];
        if (!sectionActive(sec, v)) return [];
        if (sec.kind === 'range') return [<FilterBarPill key={sec.key} onRemove={() => onChange(sec.key, [sec.min != null ? sec.min : 0, sec.max != null ? sec.max : 100])}><span>{sec.label}: {(sec.format || String)(v[0])}–{(sec.format || String)(v[1])}</span></FilterBarPill>];
        if (sec.kind === 'daterange') return [<FilterBarPill key={sec.key} onRemove={() => onChange(sec.key, { from: '', to: '' })}><Icon name="calendar" size="xs" stroke="2" /><span>{(v.from || '…') + ' – ' + (v.to || '…')}</span></FilterBarPill>];
        return v.map(val => { const o = (sec.options || []).find(x => x.value === val) || {}; return <FilterBarPill key={sec.key + val} onRemove={() => toggleArr(sec.key, val)}>{o.user ? <Avatar user={o.user} size={16} /> : o.node ? o.node : null}<span>{o.label || val}</span></FilterBarPill>; });
      })}
      <button type="button" className="mn-login-link" style={{ fontSize: 12, background: 'none', border: 0, cursor: 'pointer', marginLeft: 2 }} onClick={onReset}>Reset all</button>
    </div>
  );
}

/* ---------- Numbered pagination (rendered OUTSIDE the horizontal-scroll wrap) ---------- */
function pageList(page, total) {
  const out = [];
  if (total <= 7) { for (let i = 1; i <= total; i++) out.push(i); return out; }
  if (page <= 4) { for (let i = 1; i <= 5; i++) out.push(i); out.push('…'); out.push(total); }
  else if (page >= total - 3) { out.push(1); out.push('…'); for (let i = total - 4; i <= total; i++) out.push(i); }
  else { out.push(1); out.push('…'); for (let i = page - 1; i <= page + 1; i++) out.push(i); out.push('…'); out.push(total); }
  return out;
}
const PAGINATION_CSS = '.mn-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 4px;border-top:1px solid var(--border-1);flex-wrap:wrap}.mn-pg-right{display:flex;align-items:center;gap:4px}.mn-pg-btn{min-width:32px;height:32px;padding:0 8px;border:1px solid var(--border-1);background:var(--bg-1);border-radius:var(--radi-sm);cursor:pointer;font:var(--font-weight-medium) 13px/1 var(--font-sans);color:var(--text-1);display:inline-flex;align-items:center;justify-content:center}.mn-pg-btn:hover:not(:disabled){background:var(--bg-2)}.mn-pg-btn:disabled{opacity:.4;cursor:default}.mn-pg-btn--on{background:var(--primary-low);border-color:var(--primary-main)}.mn-pg-ell{padding:0 4px;color:var(--text-3)}';
function Pagination({ total, page, pageSize, onPage, onPageSize, pageSizes }) {
  const sizes = pageSizes || [10, 25, 50, 100];
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  const cur = Math.min(page, totalPages);
  const from = total === 0 ? 0 : (cur - 1) * pageSize + 1;
  const to = Math.min(total, cur * pageSize);
  return (
    <div className="mn-pagination">
      <style>{PAGINATION_CSS}</style>
      <span className="mn-mono mn-muted" style={{ fontSize: 12 }}>Showing {from} to {to} of {total} entries</span>
      <div className="mn-pg-right">
        <Select size="sm" value={String(pageSize)} onChange={(v) => onPageSize(+v)} options={sizes.map(s => ({ value: String(s), label: String(s) + ' / page' }))} />
        <button type="button" className="mn-pg-btn" disabled={cur <= 1} onClick={() => onPage(cur - 1)} title="Previous"><Icon name="chevron-left" size="sm" /></button>
        {pageList(cur, totalPages).map((p, i) => p === '…'
          ? <span key={'e' + i} className="mn-pg-ell">…</span>
          : <button key={p} type="button" className={'mn-pg-btn' + (p === cur ? ' mn-pg-btn--on' : '')} onClick={() => onPage(p)}>{p}</button>)}
        <button type="button" className="mn-pg-btn" disabled={cur >= totalPages} onClick={() => onPage(cur + 1)} title="Next"><Icon name="chevron-right" size="sm" /></button>
      </div>
    </div>
  );
}

/* ===========================================================================
   Advanced query builder — condition groups. AND within a group, OR between
   groups (prd-leads-filters: "AND усередині групи, OR між групами"). NOT lives
   at the operator level (`is none of` / `is unknown`). Field-catalog driven so
   the same builder serves Leads / Clients / Deals.
   field = { key, label, type, options?, get(row), known(row), fmt?, match? }
   type  = 'enum' | 'multi' | 'people' | 'number' | 'date' | 'text' | 'flag'
   query = { groups: [ { conditions: [ { field, op, value } ] } ] }
   =========================================================================== */
const ADV_OPS = {
  enum:   [['in', 'is any of'], ['nin', 'is none of'], ['known', 'is known'], ['nknown', 'is unknown']],
  multi:  [['hany', 'has any of'], ['hnone', 'has none of'], ['known', 'is known'], ['nknown', 'is unknown']],
  people: [['in', 'is any of'], ['nin', 'is none of'], ['nknown', 'is unknown']],
  number: [['between', 'between'], ['gt', 'greater than'], ['lt', 'less than'], ['eq', 'equals'], ['known', 'is known'], ['nknown', 'is unknown']],
  date:   [['between', 'from–to'], ['lastn', 'in the last N days'], ['known', 'is known'], ['nknown', 'is unknown']],
  text:   [['contains', 'contains'], ['is', 'is'], ['known', 'is known'], ['nknown', 'is unknown']],
  flag:   [['is', 'is']],
};
const advNoValue = (op) => op === 'known' || op === 'nknown';
const advWide = (type, op) => ((type === 'enum' || type === 'multi' || type === 'people') && !advNoValue(op)) || (type === 'number' && op === 'between');
function advDefaultOp(type) { return ADV_OPS[type][0][0]; }
function advDefaultValue(type, op) {
  if (advNoValue(op)) return null;
  if (type === 'number') return op === 'between' ? [0, 100] : 0;
  if (type === 'date') return op === 'lastn' ? 7 : { from: '', to: '' };
  if (type === 'text') return '';
  if (type === 'flag') return null;
  return [];
}
function advEmpty() { return { groups: [{ conditions: [] }] }; }
// A condition counts only when it carries a usable value (empty multi-select / blank text = inactive no-op).
function advCondActive(c) {
  if (!c) return false;
  if (c.op === 'known' || c.op === 'nknown') return true;
  const v = c.value;
  if (v == null) return false;
  if (Array.isArray(v)) return v.length > 0;
  if (typeof v === 'object') return !!(v.from || v.to);
  return v !== '';
}
function advCount(query) { return ((query && query.groups) || []).reduce((a, g) => a + (g.conditions || []).filter(advCondActive).length, 0); }
function advActive(query) { return advCount(query) > 0; }
function advOpLabel(type, op) { const e = (ADV_OPS[type] || []).find(x => x[0] === op); return e ? e[1] : op; }
function advValLabel(field, c) {
  if (advNoValue(c.op)) return '';
  const t = field.type, v = c.value;
  if (t === 'flag') return ((field.options || []).find(o => o.value === v) || {}).label || v || '…';
  if (t === 'enum' || t === 'multi' || t === 'people') { const lbl = (val) => ((field.options || []).find(o => o.value === val) || {}).label || val; const arr = (v || []).map(lbl); return arr.length > 2 ? arr.slice(0, 2).join(', ') + ' +' + (arr.length - 2) : (arr.join(', ') || '…'); }
  if (t === 'number') return c.op === 'between' ? v[0] + '–' + v[1] : String(v);
  if (t === 'date') return c.op === 'lastn' ? v + ' days' : (v.from || '…') + ' – ' + (v.to || '…');
  return v ? '“' + v + '”' : '…';
}
// Per-condition predicate over a fields-by-key map (built once by the caller).
function matchAdvCond(row, c, byKey) {
  const f = byKey[c.field]; if (!f || !advCondActive(c)) return true;
  if (c.op === 'known') return !!f.known(row);
  if (c.op === 'nknown') return !f.known(row);
  if (f.type === 'flag') return f.match ? f.match(row, c.value) : true;
  const v = f.get(row);
  switch (c.op) {
    case 'in': return (c.value || []).includes(v);
    case 'nin': return !!f.known(row) && !(c.value || []).includes(v);
    case 'hany': return (v || []).some(x => (c.value || []).includes(x));
    case 'hnone': return (v || []).length > 0 && !(v || []).some(x => (c.value || []).includes(x));
    case 'contains': return String(v == null ? '' : v).toLowerCase().includes(String(c.value || '').toLowerCase());
    case 'is': return String(v == null ? '' : v).toLowerCase() === String(c.value || '').toLowerCase();
    case 'between':
      if (f.type === 'date') { const t = new Date(v).getTime(); const fr = c.value.from ? new Date(c.value.from).getTime() : -Infinity; const to = c.value.to ? new Date(c.value.to).getTime() + 86400000 : Infinity; return v != null && t >= fr && t < to; }
      return v != null && v >= c.value[0] && v <= c.value[1];
    case 'lastn': { if (v == null) return false; const since = window.MonolitData.NOW.getTime() - (c.value * 86400000); return new Date(v).getTime() >= since; }
    case 'gt': return v != null && v > c.value;
    case 'lt': return v != null && v < c.value;
    case 'eq': return v != null && String(v) === String(c.value);
    default: return true;
  }
}
// OR between groups, AND within a group; empty groups are ignored.
function matchAdvQuery(row, query, byKey) {
  const groups = ((query && query.groups) || []).map(g => ({ not: !!g.not, conds: (g.conditions || []).filter(advCondActive) })).filter(g => g.conds.length);
  if (!groups.length) return true;
  return groups.some(g => { const m = g.conds.every(c => matchAdvCond(row, c, byKey)); return g.not ? !m : m; });
}
function applyAdvQuery(rows, query, fields) {
  if (!advActive(query)) return rows;
  const byKey = {}; fields.forEach(f => { byKey[f.key] = f; });
  return rows.filter(r => matchAdvQuery(r, query, byKey));
}
// Flatten a query into chip descriptors for the active-filter strip.
function advChips(query, fields) {
  const byKey = {}; fields.forEach(f => { byKey[f.key] = f; });
  const out = [];
  ((query && query.groups) || []).forEach((g, gi) => (g.conditions || []).forEach((c, ci) => {
    if (!advCondActive(c)) return;
    const f = byKey[c.field]; if (!f) return;
    out.push({ gi, ci, field: c.field, label: f.label, op: advOpLabel(f.type, c.op), val: advValLabel(f, c), not: !!g.not, icon: f.icon });
  }));
  return out;
}
// Plain-English readout of the whole query — shown as a preview in the builder.
function advToEnglish(query, fields) {
  const byKey = {}; fields.forEach(f => { byKey[f.key] = f; });
  const parts = ((query && query.groups) || []).map(g => {
    const cs = (g.conditions || []).filter(advCondActive);
    if (!cs.length) return null;
    const inner = cs.map(c => { const f = byKey[c.field]; if (!f) return ''; const v = advValLabel(f, c); return f.label + ' ' + advOpLabel(f.type, c.op) + (v ? ' ' + v : ''); }).join(' AND ');
    return (g.not ? 'NOT ' : '') + '(' + inner + ')';
  }).filter(Boolean);
  return parts.join('  OR  ');
}
// Group-0 helpers — the faceted "Filters" tab edits the first group (one condition per field).
function advSimpleEditable(query) {
  const gs = (query && query.groups) || [];
  if (gs.length > 1) return false;
  const g = gs[0] || { conditions: [] };
  if (g.not) return false;
  const seen = {}; for (const c of (g.conditions || [])) { if (seen[c.field]) return false; seen[c.field] = 1; }
  return true;
}
function advGet(query, field) { const g = (query && query.groups && query.groups[0]) || { conditions: [] }; return (g.conditions || []).find(c => c.field === field); }
function advSet(query, field, op, value) {
  const gs = (query && query.groups && query.groups.length) ? query.groups.map(g => ({ not: g.not, conditions: g.conditions.slice() })) : [{ conditions: [] }];
  const g0 = gs[0]; const i = g0.conditions.findIndex(c => c.field === field); const cond = { field, op, value };
  if (i >= 0) g0.conditions[i] = cond; else g0.conditions.push(cond);
  return { groups: gs };
}
function advRemove(query, field) { const gs = ((query && query.groups) || [{ conditions: [] }]).map(g => ({ not: g.not, conditions: g.conditions.filter(c => c.field !== field) })); return { groups: gs.length ? gs : [{ conditions: [] }] }; }

const ADV_CSS = '.mn-adv-group{border:1px solid var(--border-1);border-radius:var(--radi-md);padding:10px 10px 8px}.mn-adv-or{display:flex;align-items:center;gap:8px;margin:10px 0}.mn-adv-or::before,.mn-adv-or::after{content:"";flex:1;height:1px;background:var(--border-1)}.mn-adv-or span{font:var(--font-weight-medium) 11px/1 var(--font-sans);color:var(--text-3);letter-spacing:.04em}.mn-adv-cond{display:grid;grid-template-columns:42px minmax(0,1fr) minmax(0,1fr) minmax(0,1.5fr) 24px;gap:6px;align-items:center;margin-bottom:8px}.mn-adv-val-cell{min-width:0;display:flex;align-items:center}.mn-adv-and{font:var(--font-weight-medium) 11px/1 var(--font-sans);color:var(--text-3);min-width:34px;text-transform:uppercase}.mn-adv-valwrap{flex:1 1 130px;min-width:120px;display:flex;align-items:center}.mn-adv-valwrap--wide{flex-basis:100%;flex-wrap:wrap;gap:6px}.mn-adv-num,.mn-adv-date{padding:6px 8px;border:1px solid var(--border-1);border-radius:var(--radi-sm);font:var(--font-weight-medium) 12px/16px var(--font-sans);color:var(--text-1);background:var(--bg-1);box-sizing:border-box}.mn-adv-num{width:68px}.mn-adv-add{display:inline-flex;align-items:center;gap:4px;border:0;background:none;cursor:pointer;color:var(--secondary-main);font:var(--font-weight-medium) 12px/16px var(--font-sans);padding:2px 0}.mn-adv-orbtn{display:inline-flex;align-items:center;gap:5px;border:1px dashed var(--border-1);background:none;cursor:pointer;color:var(--text-2);font:var(--font-weight-medium) 12px/16px var(--font-sans);padding:7px 12px;border-radius:var(--radi-sm);margin-top:8px}.mn-adv-orbtn:hover{background:var(--bg-2);color:var(--text-1)}.mn-adv-empty{color:var(--text-3);font:var(--font-weight-medium) 12px/16px var(--font-sans);padding:2px 0 8px}';

// Compact multi-select: token box + floating checkbox dropdown (portaled, fixed-positioned → on top, no drawer clipping).
function MultiSelect({ options, value, onChange, placeholder }) {
  const [open, setOpen] = useStateLCtl(false);
  const [q, setQ] = useStateLCtl('');
  const [rect, setRect] = useStateLCtl(null);
  const ref = React.useRef(null);
  const sel = value || [];
  const labelOf = (v) => ((options.find(o => o.value === v) || {}).label || v);
  React.useEffect(() => {
    if (!open) return;
    const place = () => { if (ref.current) setRect(ref.current.getBoundingClientRect()); };
    place();
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target) && !(e.target.closest && e.target.closest('.mn-ms-pop'))) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    window.addEventListener('scroll', place, true);
    window.addEventListener('resize', place);
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => { window.removeEventListener('scroll', place, true); window.removeEventListener('resize', place); document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
  }, [open]);
  const ql = q.trim().toLowerCase();
  const list = options.filter(o => !ql || o.label.toLowerCase().includes(ql)).slice().sort((a, b) => (sel.includes(b.value) ? 1 : 0) - (sel.includes(a.value) ? 1 : 0));
  const w = rect ? Math.max(rect.width, 220) : 220;
  const up = rect ? (window.innerHeight - rect.bottom < 280) : false;
  const pos = rect ? (up ? { bottom: window.innerHeight - rect.top + 4 } : { top: rect.bottom + 4 }) : {};
  const panel = (open && rect) ? ReactDOM.createPortal(
    <div className="mn-ms-pop" style={Object.assign({ position: 'fixed', left: rect.left, width: w, zIndex: 1200 }, pos)}>
      {options.length > 7 ? <div style={{ padding: '0 0 6px' }}><Input fullWidth icon="search" placeholder="Search…" value={q} onChange={setQ} /></div> : null}
      {sel.length ? <div className="mn-ms-head"><span className="mn-mono mn-muted" style={{ fontSize: 11 }}>{sel.length} selected</span><button type="button" className="mn-login-link" style={{ fontSize: 12 }} onClick={() => onChange([])}>Clear</button></div> : null}
      <div className="mn-ms-list">
        {list.map(o => { const on = sel.includes(o.value); return (
          <label key={o.value} className="mn-ms-opt">
            <input type="checkbox" className="mn-checkbox" checked={on} onChange={() => onChange(on ? sel.filter(x => x !== o.value) : [...sel, o.value])} />
            <span style={{ flex: 1, font: 'var(--font-weight-medium) 13px/16px var(--font-sans)' }}>{o.label}</span>
            {o.count != null ? <span className="mn-mono mn-muted" style={{ fontSize: 11 }}>{o.count}</span> : null}
          </label>
        ); })}
        {!list.length ? <div style={{ padding: 8, color: 'var(--text-3)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)' }}>No matches.</div> : null}
      </div>
    </div>, document.body) : null;
  return (
    <div style={{ width: '100%' }} ref={ref}>
      <button type="button" className="mn-ms-box" onClick={() => setOpen(o => !o)}>
        {sel.length
          ? <span className="mn-ms-tokens">{sel.slice(0, 8).map(v => <span key={v} className="mn-ms-tok">{labelOf(v)}<span className="mn-ms-x" onClick={(e) => { e.stopPropagation(); onChange(sel.filter(x => x !== v)); }}><Icon name="x" size="xs" stroke="2" /></span></span>)}{sel.length > 8 ? <span className="mn-mono mn-muted" style={{ fontSize: 11, alignSelf: 'center' }}>+{sel.length - 8}</span> : null}</span>
          : <span style={{ flex: 1, color: 'var(--text-3)', font: 'var(--font-weight-medium) 13px/1 var(--font-sans)' }}>{placeholder || 'Select…'}</span>}
        <Icon name="chevron-down" size="sm" color="text-3" />
      </button>
      {panel}
    </div>
  );
}

function AdvCondValue({ field, c, onChange, compact }) {
  const t = field.type, op = c.op;
  if (advNoValue(op)) return <span style={{ color: 'var(--text-3)', font: 'var(--font-weight-medium) 12px/16px var(--font-sans)' }}>{compact ? '—' : 'no value needed'}</span>;
  if (t === 'flag') return <Select size="md" value={c.value || ''} onChange={onChange} options={(field.options || []).map(o => ({ value: o.value, label: o.label }))} placeholder="Choose…" />;
  if (t === 'enum' || t === 'multi' || t === 'people') {
    return <MultiSelect options={field.options || []} value={c.value || []} onChange={onChange} placeholder="Select…" />;
  }
  if (t === 'number') {
    if (op === 'between') {
      const val = Array.isArray(c.value) ? c.value : [field.min != null ? field.min : 0, field.max != null ? field.max : 100];
      if (field.slider && !compact) return <FilterRange value={val} onChange={onChange} min={field.min != null ? field.min : 0} max={field.max != null ? field.max : 100} gradient={field.gradient} bands={field.bands} format={field.format} />;
      return <span style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}><input className="mn-adv-num" type="number" value={val[0]} onChange={(e) => onChange([+e.target.value, val[1]])} /><span style={{ color: 'var(--text-3)' }}>–</span><input className="mn-adv-num" type="number" value={val[1]} onChange={(e) => onChange([val[0], +e.target.value])} /></span>;
    }
    return <input className="mn-adv-num" type="number" value={c.value} onChange={(e) => onChange(+e.target.value)} />;
  }
  if (t === 'date') {
    if (op === 'lastn') return <span style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}><input className="mn-adv-num" type="number" min="1" value={c.value} onChange={(e) => onChange(+e.target.value)} /><span style={{ color: 'var(--text-3)', fontSize: 12 }}>days</span></span>;
    return <span style={{ display: 'flex', gap: 6, alignItems: 'center', width: '100%' }}><input className="mn-adv-date" style={{ flex: 1, minWidth: 0 }} type="date" value={c.value.from || ''} onChange={(e) => onChange({ ...c.value, from: e.target.value })} /><span style={{ color: 'var(--text-3)' }}>–</span><input className="mn-adv-date" style={{ flex: 1, minWidth: 0 }} type="date" value={c.value.to || ''} onChange={(e) => onChange({ ...c.value, to: e.target.value })} /></span>;
  }
  return <Input fullWidth value={c.value || ''} onChange={onChange} placeholder="Value…" />;
}

const FILTER_CSS = '.mn-adv-hint{font:var(--font-weight-medium) 12px/1.5 var(--font-sans);color:var(--text-3);margin:0 0 12px}.mn-seg{display:flex;gap:4px;background:var(--bg-2);border-radius:var(--radi-md);padding:3px;margin-bottom:14px}.mn-seg-b{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;border:0;background:transparent;border-radius:var(--radi-sm);padding:8px;font:var(--font-weight-medium) 13px/1 var(--font-sans);color:var(--text-2);cursor:pointer}.mn-seg-b--on{background:var(--bg-1);color:var(--text-1);box-shadow:0 1px 2px rgba(0,0,0,.06)}.mn-adv-ghead{display:flex;align-items:center;gap:8px;margin-bottom:8px}.mn-adv-gnum{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background:var(--secondary-low);color:var(--secondary-main);font:var(--font-weight-medium) 11px/1 var(--font-sans);flex:none}.mn-not{width:34px;height:18px;border-radius:9px;border:0;background:var(--bg-3);position:relative;cursor:pointer;flex:none;padding:0}.mn-not--on{background:var(--error-main)}.mn-not-dot{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--bg-1);transition:left .12s}.mn-not--on .mn-not-dot{left:18px}.mn-adv-preview{font:var(--font-weight-medium) 12px/1.5 var(--font-sans);color:var(--text-2);background:var(--bg-2);border-radius:var(--radi-sm);padding:8px 10px;margin-bottom:12px;display:flex;gap:7px;align-items:flex-start}.mn-facet-row{display:grid;grid-template-columns:108px 116px minmax(0,1fr) 28px;gap:8px;align-items:start;padding:9px 0;border-top:1px solid var(--border-1)}.mn-facet-lab{display:flex;align-items:center;gap:7px;min-height:36px;font:var(--font-weight-medium) 13px/1.2 var(--font-sans);color:var(--text-1)}.mn-facet-val{display:flex;align-items:center;flex-wrap:wrap;gap:6px;min-height:34px}.mn-ms-box{display:flex;align-items:center;gap:6px;width:100%;min-height:36px;padding:6px 10px;border:1px solid var(--border-1);border-radius:var(--radi-sm);background:var(--bg-1);cursor:pointer;text-align:left}.mn-ms-box:focus-within,.mn-ms-box:hover{border-color:var(--primary-main)}.mn-ms-box:hover{border-color:var(--border-2,var(--border-1))}.mn-ms-tokens{display:flex;flex-wrap:wrap;gap:4px;flex:1}.mn-ms-tok{display:inline-flex;align-items:center;gap:4px;font:var(--font-weight-medium) 11px/1.4 var(--font-sans);background:var(--primary-low);color:var(--primary-low-on);border-radius:var(--radi-sm);padding:3px 4px 3px 7px}.mn-ms-x{display:inline-flex;cursor:pointer;opacity:.65}.mn-ms-x:hover{opacity:1}.mn-ms-pop{background:var(--bg-1);border:1px solid var(--border-1);border-radius:var(--radi-md);padding:6px;box-shadow:0 10px 28px rgba(0,0,0,.16)}.mn-ms-list{max-height:220px;overflow-y:auto}.mn-ms-head{display:flex;align-items:center;justify-content:space-between;padding:2px 6px 6px}.mn-ms-opt{display:flex;align-items:center;gap:9px;padding:5px 6px;border-radius:6px;cursor:pointer}.mn-ms-opt:hover{background:var(--bg-2)}.mn-count-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--primary-main);color:#fff;font:var(--font-weight-medium) 12px/1 var(--font-sans)}.mn-qf-row{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 14px;padding:0 0 12px;border-bottom:1px solid var(--border-1)}.mn-active-strip{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:0 0 12px}.mn-rangewrap{position:relative;height:24px;width:100%}.mn-rng-track{position:absolute;top:9px;left:0;right:0;height:6px;border-radius:3px;background:var(--bg-3)}.mn-rng-mask{position:absolute;top:9px;height:6px;background:var(--bg-1);opacity:.72;border-radius:3px}.mn-rangewrap input[type=range]{position:absolute;top:0;left:0;width:100%;height:24px;margin:0;background:transparent;-webkit-appearance:none;appearance:none;pointer-events:none}.mn-rangewrap input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;pointer-events:auto;width:18px;height:18px;border-radius:50%;background:var(--bg-1);border:2px solid var(--primary-main);cursor:pointer;box-shadow:0 1px 3px rgba(0,0,0,.2)}.mn-rangewrap input[type=range]::-moz-range-thumb{pointer-events:auto;width:18px;height:18px;border-radius:50%;background:var(--bg-1);border:2px solid var(--primary-main);cursor:pointer}.mn-rangewrap input[type=range]::-webkit-slider-runnable-track{background:transparent;border:0}.mn-rangewrap input[type=range]::-moz-range-track{background:transparent;border:0}.mn-facet-row--focus{background:var(--primary-low);border-radius:var(--radi-sm);box-shadow:0 0 0 6px var(--primary-low)}';

// AND/OR/NOT builder — controlled (edits `query` via onChange). NOT toggles per-group exclusion.
function AdvancedBuilder({ fields, query, onChange }) {
  const byKey = {}; fields.forEach(f => { byKey[f.key] = f; });
  const fld = (key) => byKey[key] || fields[0];
  const d = query;
  const mut = (fn) => { const g = d.groups.map(x => ({ not: x.not, conditions: x.conditions.slice() })); fn(g); onChange({ groups: g.length ? g : [{ conditions: [] }] }); };
  const setCond = (gi, ci, patch) => mut(g => { g[gi].conditions[ci] = { ...g[gi].conditions[ci], ...patch }; });
  const changeField = (gi, ci, key) => { const f = fld(key); const op = advDefaultOp(f.type); setCond(gi, ci, { field: key, op, value: advDefaultValue(f.type, op) }); };
  const changeOp = (gi, ci, op) => { const f = fld(d.groups[gi].conditions[ci].field); setCond(gi, ci, { op, value: advDefaultValue(f.type, op) }); };
  const addCond = (gi) => mut(g => { const f = fields[0]; const op = advDefaultOp(f.type); g[gi].conditions.push({ field: f.key, op, value: advDefaultValue(f.type, op) }); });
  const removeCond = (gi, ci) => mut(g => { g[gi].conditions.splice(ci, 1); for (let i = g.length - 1; i > 0; i--) if (!g[i].conditions.length) g.splice(i, 1); });
  const toggleNot = (gi) => mut(g => { g[gi].not = !g[gi].not; });
  const delGroup = (gi) => { let g = d.groups.filter((_, i) => i !== gi); if (!g.length) g = [{ conditions: [] }]; onChange({ groups: g }); };
  const addGroup = () => onChange({ groups: [...d.groups, { conditions: [] }] });
  const english = advToEnglish(d, fields);
  return (
    <React.Fragment>
      <p className="mn-adv-hint">AND within a group · OR between groups · flip the switch to exclude a group (NOT).</p>
      {english ? <div className="mn-adv-preview"><Icon name="eye" size="sm" /><span>{english}</span></div> : null}
      {d.groups.map((g, gi) => (
        <React.Fragment key={gi}>
          {gi > 0 ? <div className="mn-adv-or"><span>OR</span></div> : null}
          <div className="mn-adv-group">
            <div className="mn-adv-ghead">
              <span className="mn-adv-gnum">{gi + 1}</span>
              <span style={{ flex: 1, font: 'var(--font-weight-medium) 12px/1 var(--font-sans)' }}>Group {gi + 1}</span>
              <button type="button" className={'mn-not' + (g.not ? ' mn-not--on' : '')} onClick={() => toggleNot(gi)} title="Exclude this group (NOT)" aria-label="Exclude this group"><span className="mn-not-dot" /></button>
              <span style={{ font: 'var(--font-weight-medium) 11px/1 var(--font-sans)', color: g.not ? 'var(--error-main)' : 'var(--text-3)' }}>NOT</span>
              <IconButton icon="trash" size="sm" title="Delete group" onClick={() => delGroup(gi)} />
            </div>
            {g.conditions.length ? g.conditions.map((c, ci) => { const f = fld(c.field); return (
              <div key={ci} className="mn-adv-cond">
                <span className="mn-adv-and">{ci === 0 ? 'Where' : 'And'}</span>
                <Select size="md" value={c.field} onChange={(v) => changeField(gi, ci, v)} options={fields.map(x => ({ value: x.key, label: x.label }))} />
                <Select size="md" value={c.op} onChange={(v) => changeOp(gi, ci, v)} options={(ADV_OPS[f.type] || []).map(o => ({ value: o[0], label: o[1] }))} />
                <span className="mn-adv-val-cell"><AdvCondValue field={f} c={c} compact onChange={(val) => setCond(gi, ci, { value: val })} /></span>
                <IconButton icon="x" size="sm" title="Remove condition" onClick={() => removeCond(gi, ci)} />
              </div>
            ); }) : <div className="mn-adv-empty">No conditions yet — add one below.</div>}
            <button type="button" className="mn-adv-add" onClick={() => addCond(gi)}><Icon name="plus" size="xs" stroke="2" />Add condition</button>
          </div>
        </React.Fragment>
      ))}
      <button type="button" className="mn-adv-orbtn" onClick={addGroup}><Icon name="plus" size="xs" stroke="2" />Add group (OR)</button>
    </React.Fragment>
  );
}

// Faceted "Filters" tab — one optional condition per field over group 0. Read-only summary when the query is complex.
function FacetedFilters({ fields, query, onChange, onGoAdvanced, focusField }) {
  const ffRef = React.useRef(null);
  React.useEffect(() => {
    if (!focusField || !ffRef.current) return;
    const row = ffRef.current.querySelector('[data-field="' + focusField + '"]');
    if (!row) return;
    row.scrollIntoView({ block: 'center' });
    row.classList.add('mn-facet-row--focus');
    const t = setTimeout(() => row.classList.remove('mn-facet-row--focus'), 1500);
    return () => clearTimeout(t);
  }, [focusField]);
  if (!advSimpleEditable(query)) {
    return (
      <div>
        <div className="mn-adv-preview"><Icon name="git-branch" size="sm" /><span>This filter uses OR / NOT logic — edit it in Advanced to keep it intact.</span></div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, margin: '0 0 14px' }}>
          {advChips(query, fields).map(ch => <span key={ch.gi + '-' + ch.ci} className="mn-chip mn-chip--md mn-chip--tag">{ch.not ? 'NOT · ' : ''}{ch.label} {ch.op}{ch.val ? ' ' + ch.val : ''}</span>)}
        </div>
        <Button variant="secondary" size="md" icon="sliders-horizontal" onClick={onGoAdvanced}>Edit in Advanced</Button>
      </div>
    );
  }
  const commit = (f, op, val) => {
    const has = (op === 'known' || op === 'nknown') ? true : Array.isArray(val) ? val.length > 0 : (val && typeof val === 'object') ? !!(val.from || val.to) : (val !== '' && val != null);
    onChange(has ? advSet(query, f.key, op, val) : advRemove(query, f.key));
  };
  return (
    <div ref={ffRef}>
      {fields.map(f => {
        const c = advGet(query, f.key);
        const op = c ? c.op : advDefaultOp(f.type);
        const value = c ? c.value : advDefaultValue(f.type, op);
        return (
          <div key={f.key} className="mn-facet-row" data-field={f.key}>
            <span className="mn-facet-lab">{f.icon ? <Icon name={f.icon} size="sm" color="text-3" /> : null}{f.label}</span>
            <Select size="md" value={op} onChange={(v) => onChange(advSet(query, f.key, v, advDefaultValue(f.type, v)))} options={(ADV_OPS[f.type] || []).map(o => ({ value: o[0], label: o[1] }))} />
            <span className="mn-facet-val">{advNoValue(op) ? <span style={{ color: 'var(--text-3)', font: 'var(--font-weight-medium) 12px/34px var(--font-sans)' }}>no value needed</span> : <AdvCondValue field={f} c={{ op, value }} onChange={(val) => commit(f, op, val)} />}</span>
            {c ? <IconButton icon="x" size="sm" title="Clear" onClick={() => onChange(advRemove(query, f.key))} /> : <span />}
          </div>
        );
      })}
    </div>
  );
}

// Two-tab filter workbench: "Filters" (faceted) + "Advanced" (builder) over ONE staged query; explicit Apply + live preview count.
function LeadFilterDrawer({ eyebrow, title, fields, query, onApply, onClose, countFor, total, quickDefs, quickActive, quickCount, onToggleQuick, focusField }) {
  const [d, setD] = useStateLCtl(() => JSON.parse(JSON.stringify(query && query.groups && query.groups.length ? query : advEmpty())));
  const [tab, setTab] = useStateLCtl(() => advSimpleEditable(query) ? 'filters' : 'advanced');
  const n = advCount(d);
  const preview = countFor ? countFor(d) : null;
  const chips = advChips(d, fields);
  const removeChip = (gi, ci) => setD(s => { const g = s.groups.map(x => ({ not: x.not, conditions: x.conditions.slice() })); g[gi].conditions.splice(ci, 1); let gg = g.filter((grp, i) => grp.conditions.length || i === 0); if (!gg.length) gg = [{ conditions: [] }]; return { groups: gg }; });
  const footer = (
    <div style={{ display: 'flex', width: '100%', justifyContent: 'space-between', alignItems: 'center' }}>
      <span className="mn-mono mn-muted" style={{ fontSize: 12 }}>{preview != null ? preview + (total != null ? ' of ' + total : '') + ' · preview' : ''}</span>
      <span style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
        <Button variant="ghost" size="md" onClick={onClose}>Cancel</Button>
        <Button variant="primary" size="md" icon="check" onClick={() => { onApply(d); onClose(); }}>Apply{n ? ' · ' + n : ''}</Button>
      </span>
    </div>
  );
  return (
    <Drawer open onClose={onClose} width={560} eyebrow={eyebrow} title={<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>{title || 'Filters'}{n ? <span className="mn-count-badge">{n}</span> : null}</span>} footer={footer}>
      <style>{ADV_CSS + FILTER_CSS}</style>
      {chips.length ? (
        <div className="mn-active-strip">
          <span className="mn-eyebrow" style={{ margin: '0 2px 0 0', alignSelf: 'center' }}>Active</span>
          {chips.map(ch => <FilterBarPill key={'s' + ch.gi + '-' + ch.ci} onRemove={() => removeChip(ch.gi, ch.ci)}><span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>{ch.icon ? <Icon name={ch.icon} size="xs" color="text-3" /> : null}{ch.not ? <span style={{ color: 'var(--error-main)' }}>NOT </span> : null}{ch.label} <span style={{ color: 'var(--text-3)' }}>{ch.op}</span>{ch.val ? ' ' + ch.val : ''}</span></FilterBarPill>)}
        </div>
      ) : null}
      <div className="mn-seg">
        <button type="button" className={'mn-seg-b' + (tab === 'filters' ? ' mn-seg-b--on' : '')} onClick={() => setTab('filters')}><Icon name="filter" size="sm" /> Filters</button>
        <button type="button" className={'mn-seg-b' + (tab === 'advanced' ? ' mn-seg-b--on' : '')} onClick={() => setTab('advanced')}><Icon name="sliders-horizontal" size="sm" /> Advanced</button>
      </div>
      {tab === 'filters'
        ? <FacetedFilters fields={fields} query={d} onChange={setD} onGoAdvanced={() => setTab('advanced')} focusField={focusField} />
        : <AdvancedBuilder fields={fields} query={d} onChange={setD} />}
    </Drawer>
  );
}

Object.assign(window, { SortPopover, ColumnsPopover, FilterPopover, FilterCheckGroup, RowMenu, FilterDrawer, FilterBar, FilterBarPill, FilterPillBtn, filterEmpty, filterCount, applyFilters, createdPresetRange, Pagination, LeadFilterDrawer, advEmpty, advCount, advActive, applyAdvQuery, advChips, advToEnglish, advSimpleEditable, advGet, advSet, advRemove });
