/* ========================================================================
   SEARCH BAR — tokens-style bar: leading glyph (search ⇄ spinner), inline
   chip rail, input, Clear all. Suggestion popover with smart-parse rows,
   smart preview / error states. Port of 06-render.js search-bar section.
   ======================================================================== */

/* ---- preset preview (hover on Target / Persona chips) ------------------- */
function specToPseudoChips(type, spec) {
  const chips = [];
  if (type === 'target') {
    if (spec.type && spec.type.length) chips.push({ label:'Account type', op:'is any of', value: spec.type.join(', ') });
    if (spec.state && spec.state.length) chips.push({ label:'State', op:'is any of', value: spec.state.join(', ') });
    if (spec.region && spec.region.length) chips.push({ label:'Region', op:'is any of', value: spec.region.join(', ') });
    if (spec.budgetMin != null && spec.budgetMax != null)
      chips.push({ label:'Budget', op:'between', value: `$${spec.budgetMin}M – $${spec.budgetMax}M` });
    else if (spec.budgetMin != null) chips.push({ label:'Budget', op:'≥', value: `$${spec.budgetMin}M` });
    else if (spec.budgetMax != null) chips.push({ label:'Budget', op:'≤', value: `$${spec.budgetMax}M` });
    if (spec.populationMin != null && spec.populationMax != null)
      chips.push({ label:'Population', op:'between', value: `${spec.populationMin}K – ${spec.populationMax}K` });
    else if (spec.populationMin != null) chips.push({ label:'Population', op:'≥', value: `${spec.populationMin}K` });
    else if (spec.populationMax != null) chips.push({ label:'Population', op:'≤', value: `${spec.populationMax}K` });
    if (spec.themes && spec.themes.length) chips.push({ label:'Topics', op:'includes', value: spec.themes.join(', ') });
    if (spec.signalType && spec.signalType.length) chips.push({ label:'Signal type', op:'is any of', value: spec.signalType.join(', ') });
    if (spec.daysAgoMax != null) chips.push({ label:'Days ago', op:'≤', value: `${spec.daysAgoMax}` });
    if (spec.competitors && spec.competitors.length) chips.push({ label:'Vendors', op:'is any of', value: spec.competitors.join(', ') });
    if (spec.keywords && spec.keywords.length) chips.push({ label:'Keywords', op:'contains', value: spec.keywords.join(', ') });
    if (spec.keywordsNone && spec.keywordsNone.length) chips.push({ label:'Keywords', op:'does not contain', value: spec.keywordsNone.join(', ') });
    if (spec.dataSources && spec.dataSources.length) chips.push({ label:'Data source', op:'is any of', value: spec.dataSources.join(', ') });
  } else {
    if (spec.title && spec.title.length) chips.push({ label:'Title', op:'contains', value: spec.title.join(' / ') });
    if (spec.department && spec.department.length) chips.push({ label:'Department', op:'is any of', value: spec.department.join(', ') });
    if (spec.seniority && spec.seniority.length) chips.push({ label:'Seniority', op:'is any of', value: spec.seniority.join(', ') });
    if (spec.confidence && spec.confidence.length) chips.push({ label:'Confidence', op:'is any of', value: spec.confidence.join(', ') });
  }
  return chips;
}

function PresetPreview({ anchorRect, field, value }) {
  const ref = React.useRef(null);
  React.useLayoutEffect(() => {
    const el = ref.current;
    if (!el || !anchorRect) return;
    let left = anchorRect.left;
    if (left + el.offsetWidth > window.innerWidth - 12) left = Math.max(12, window.innerWidth - el.offsetWidth - 12);
    el.style.left = left + 'px';
    el.style.top = (anchorRect.bottom + 6) + 'px';
  });
  if (!Array.isArray(value) || value.length === 0) return null;
  const catalog = field.type === 'target' ? TARGETS : PERSONAS;
  const groups = value.map(name => ({ name, spec: catalog.find(s => s.name === name) })).filter(g => g.spec);
  if (!groups.length) return null;
  return (
    <div className="preset-preview" ref={ref}>
      {groups.map(({ name, spec }) => {
        const chips = specToPseudoChips(field.type, spec);
        return (
          <div className="pp-group" key={name}>
            <div className="pp-name">{name}</div>
            {chips.length === 0
              ? <div className="pp-empty">No filter criteria set.</div>
              : chips.map((c, i) => (
                  <span className="pp-chip" key={i}>
                    <span className="pp-field">{c.label}</span>
                    <span className="pp-op">{c.op}</span>
                    <span className="pp-val">{c.value}</span>
                  </span>
                ))}
          </div>
        );
      })}
    </div>
  );
}

/* ---- filter chip --------------------------------------------------------- */
function FilterChip({ f, onPreview }) {
  const st = fpState();
  const field = FIELD_BY_ID[f.fieldId];
  const op = (OPS[field.type] || []).find(o => o.id === f.op);
  const val = formatValue(field, f.op, f.value);
  const FIRST_CLASS = new Set(['Signals', 'Data Sources']);
  const related = field.entity !== st.entity && !FIRST_CLASS.has(field.group);
  const pending = !isFilterComplete(f);
  const showOp = field.type !== 'multi';
  const isPreset = field.type === 'target' || field.type === 'persona';
  return (
    <span
      className={cx('filter-chip', st.openFieldId === f.fieldId && 'accent', related && 'related', pending && 'pending')}
      data-id={f.id} data-field-id={f.fieldId}
      onClick={(e) => {
        if (onPreview) onPreview(null);
        const rm = e.target.closest('[data-remove]');
        if (rm) { removeFilter(f.id); return; }
        if (st.openFieldId === f.fieldId) { closeEditor(); return; }
        st.openFieldId = f.fieldId;
        st.editorAnchor = e.currentTarget;
        notify();
      }}
      onMouseEnter={isPreset && onPreview ? (e) => onPreview({ rect: e.currentTarget.getBoundingClientRect(), field, value: f.value }) : undefined}
      onMouseLeave={isPreset && onPreview ? () => onPreview(null) : undefined}
    >
      {related ? <span className="fc-via">via {ENTITY[field.entity].singular}</span> : null}
      <span className="fc-part fc-field">{displayLabel(field, st.entity)}</span>
      {showOp ? <span className="fc-part fc-op">{op ? op.label : f.op}</span> : null}
      <span className="fc-part fc-val">{val || <em>choose…</em>}</span>
      <span className="fc-part fc-x" data-remove={f.id}><Icon name="x" size={14} /></span>
    </span>
  );
}

/* ---- suggestion rows ------------------------------------------------------ */
function HighlightQ({ text, q }) {
  const ql = String(q || '').toLowerCase().trim();
  const t = String(text);
  if (!ql) return <span>{t}</span>;
  const i = t.toLowerCase().indexOf(ql);
  if (i < 0) return <span>{t}</span>;
  return <span>{t.slice(0, i)}<em>{t.slice(i, i + ql.length)}</em>{t.slice(i + ql.length)}</span>;
}

function SmartPreviewItem({ f }) {
  const conf = f.confidence ?? 1;
  const pct = Math.round(conf * 100);
  const tier = conf >= 0.85 ? 'high' : conf >= 0.7 ? 'mid' : 'low';
  const disabled = !!f._blocked;
  return (
    <div
      className={cx('sp-preview-item', `sp-pi-${tier}`, disabled && 'disabled')}
      data-helper-tip={disabled ? (f._blockedReason || '') : undefined}
    >
      <span className="sp-icon"><Icon name={f.icon} size={13} /></span>
      <span className="sp-body">
        <span className="sp-body-row">
          <span className="sp-type">{f.label}</span>
          <span className="sp-match"><strong>{f.display}</strong></span>
        </span>
        {disabled
          ? <span className="sp-why sp-why-blocked">{f._blockedReason || ''}</span>
          : (f.why ? <span className="sp-why">{f.why}</span> : null)}
      </span>
      <span className={cx('sp-confidence', `sp-conf-${tier}`)}>{pct}%</span>
    </div>
  );
}

function SmartPreviewPopover() {
  useAppState();
  const r = searchSmartResult;
  const allFilters = (r && r.filters) || [];
  const blockContacts = state.entity === 'signals' || hasSignalFilter();
  const blockSignals = state.entity === 'contacts' || hasContactFilter();
  const annotated = allFilters.map(f => {
    const ent = FIELD_BY_ID[f.fieldId]?.entity;
    const blockedReason =
      (blockContacts && ent === 'contacts')
        ? (hasSignalFilter() ? 'Not available with a Signal filter applied' : 'Not available while browsing Signals')
      : (blockSignals && ent === 'signals')
        ? (hasContactFilter() ? 'Not available with a Contact filter applied' : 'Not available while browsing Contacts')
      : null;
    return Object.assign({}, f, blockedReason ? { _blocked: true, _blockedReason: blockedReason } : null);
  });
  const liveCount = annotated.filter(f => !f._blocked).length;
  const blockedCount = annotated.length - liveCount;
  const allBlocked = annotated.length > 0 && liveCount === 0;
  const sourceLabel = r && r.source === 'lexical' ? 'lexical' : r && r.source === 'lexical-fallback' ? 'lexical only' : 'AI';
  let titleText;
  if (annotated.length === 0) titleText = 'No filters detected';
  else if (allBlocked) titleText = `Detected ${annotated.length} filter${annotated.length === 1 ? '' : 's'} — none apply here`;
  else titleText = `Detected ${liveCount} filter${liveCount === 1 ? '' : 's'}${blockedCount ? ` · ${blockedCount} unavailable` : ''}`;
  return (
    <div className="sp-preview">
      <div className="sp-preview-head">
        <span className="sp-preview-glyph"><Icon name="wand-sparkles" size={13} /></span>
        <span className="sp-preview-title">{titleText}</span>
        <span className="sp-preview-source">{sourceLabel}</span>
      </div>
      {annotated.length === 0
        ? <div className="sp-empty">Nothing matched. Try simpler terms, or press Esc.</div>
        : <div className="sp-preview-list">{annotated.map((f, i) => <SmartPreviewItem f={f} key={i} />)}</div>}
      {allBlocked ? <div className="sp-error-hint">These filters aren't available from the current toggle. Switch toggles or refine your query.</div> : null}
      {r && r.leftover ? <div className="sp-error-hint">Unmapped: "{r.leftover}" — will be ignored.</div> : null}
      <div className="sp-preview-actions">
        <button className="sp-preview-cancel" type="button" onMouseDown={(e) => { e.preventDefault(); cancelSmartParse(); notify(); }}>Cancel (Esc)</button>
        <button className="sp-preview-apply" type="button" disabled={liveCount === 0} onMouseDown={(e) => { e.preventDefault(); applySmartResult(); }}>
          Apply {liveCount || ''} <span className="sp-kbd">↵</span>
        </button>
      </div>
    </div>
  );
}

function SmartErrorPopover() {
  useAppState();
  const all = (searchSmartResult && searchSmartResult.filters) || [];
  const blockContacts = state.entity === 'signals' || hasSignalFilter();
  const blockSignals = state.entity === 'contacts' || hasContactFilter();
  const filters = (blockContacts || blockSignals)
    ? all.filter(f => {
        const ent = FIELD_BY_ID[f.fieldId]?.entity;
        if (blockContacts && ent === 'contacts') return false;
        if (blockSignals && ent === 'signals') return false;
        return true;
      })
    : all;
  return (
    <div className="sp-preview">
      <div className="sp-preview-head">
        <span className="sp-preview-glyph sp-error-glyph"><Icon name="triangle-alert" size={13} /></span>
        <span className="sp-preview-title">AI parse failed</span>
        <span className="sp-preview-source">error</span>
      </div>
      <div className="sp-error-body">{searchSmartError || 'Unknown error'}</div>
      {filters.length
        ? <React.Fragment>
            <div className="sp-preview-list">{filters.map((f, i) => <SmartPreviewItem f={f} key={i} />)}</div>
            <div className="sp-error-hint">Lexical matches above — apply these, or cancel and refine.</div>
          </React.Fragment>
        : <div className="sp-error-hint">No lexical matches either. Try a simpler query.</div>}
      <div className="sp-preview-actions">
        <button className="sp-preview-cancel" type="button" onMouseDown={(e) => { e.preventDefault(); cancelSmartParse(); notify(); }}>Cancel</button>
        {filters.length
          ? <button className="sp-preview-apply" type="button" onMouseDown={(e) => { e.preventDefault(); applySmartResult(); }}>Apply {filters.length} <span className="sp-kbd">↵</span></button>
          : null}
      </div>
    </div>
  );
}

/* ---- the bar --------------------------------------------------------------- */
function SearchBar({ value, setValue, hero }) {
  useAppState();
  const [focused, setFocused] = React.useState(false);
  const [hi, setHi] = React.useState(0);
  const [preview, setPreview] = React.useState(null); // { rect, field, value }
  const inputRef = React.useRef(null);
  const wrapRef = React.useRef(null);

  // Expose clear/focus hooks for the smart-parse module + sidebar reset.
  React.useEffect(() => {
    const api = {
      clearInput: () => setValue(''),
      focus: () => inputRef.current && inputRef.current.focus(),
      setQuery: (q) => { setValue(q); if (inputRef.current) inputRef.current.focus(); },
    };
    window.__searchBar = api;
    window.__clearSearchInput = api.clearInput;
    return () => { if (window.__searchBar === api) { window.__searchBar = null; window.__clearSearchInput = null; } };
  }, [setValue]);

  const trimmed = String(value || '').trim();
  // A query change invalidates in-flight smart parse state.
  React.useEffect(() => {
    if (searchSmartMode !== 'idle' && trimmed !== searchSmartQuery) {
      cancelSmartParse();
      notify();
    }
  }, [trimmed]);

  const committed = state.filters.filter(isFilterComplete);
  const parsing = searchSmartMode === 'parsing';

  // Build suggestions (idle mode only).
  let items = [];
  if (searchSmartMode === 'idle' && trimmed) {
    items = buildSearchSuggestions(value);
    if (smartTokenize(value).length >= 2) {
      items = [{ isSmart: true, icon: 'wand-sparkles', label: 'Smart parse', match: 'Interpret entire query with AI', q: trimmed }, ...items];
    }
  }
  let hiIdx = hi;
  const firstEnabled = items.findIndex(it => !it.disabled);
  if (firstEnabled >= 0 && (hiIdx >= items.length || hiIdx < 0 || items[hiIdx]?.disabled)) hiIdx = firstEnabled;

  const showSuggestions = searchSmartMode === 'idle' && focused && trimmed && items.length > 0;
  const showSmart = searchSmartMode === 'preview' || searchSmartMode === 'error';

  const pick = (idx) => {
    const it = items[idx];
    if (!it || it.disabled) return;
    if (it.isSmart) { runSmartParse(value); return; }
    commitSearchSuggestion(it);
    setValue('');
    setHi(0);
    notify();
    setTimeout(() => inputRef.current && inputRef.current.focus(), 0);
  };

  const onKeyDown = (e) => {
    if (searchSmartMode === 'parsing') {
      if (e.key === 'Escape') { e.preventDefault(); cancelSmartParse(); notify(); }
      else if (e.key === 'Enter') e.preventDefault();
      return;
    }
    if (searchSmartMode === 'preview' || searchSmartMode === 'error') {
      if (e.key === 'Escape') { e.preventDefault(); cancelSmartParse(); notify(); }
      else if (e.key === 'Enter' || e.key === 'Tab') {
        e.preventDefault();
        if (searchSmartResult && searchSmartResult.filters.length) applySmartResult();
      }
      return;
    }
    if (e.key === 'Escape') { setValue(''); return; }
    if (!items.length) return;
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      let next = hiIdx + 1;
      while (next < items.length && items[next]?.disabled) next++;
      if (next < items.length) setHi(next);
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      let prev = hiIdx - 1;
      while (prev >= 0 && items[prev]?.disabled) prev--;
      if (prev >= 0) setHi(prev);
    } else if (e.key === 'Enter' || e.key === 'Tab') {
      e.preventDefault();
      pick(hiIdx);
    }
  };

  return (
    <div className={cx('search-bar-wrap', hero && 'search-bar-wrap--hero')} ref={wrapRef}>
      <div className="search-bar">
        <div className={cx('sb-tokens', parsing && 'parsing')}>
          <span className="sb-leading">
            <Icon name="search" className="sb-leading-search" size={20} />
            <Icon name="loader-circle" className="sb-leading-spinner" size={20} />
          </span>
          <div className="sb-tokens-row" id="sb-tokens-row">
            {committed.length > 0 ? (
              <div className="chip-rail chip-rail--inline" data-role="chip-rail">
                {committed.map(f => <FilterChip f={f} key={f.id} onPreview={setPreview} />)}
              </div>
            ) : null}
            <input
              ref={inputRef}
              className="sb-input" type="text" autoComplete="off"
              placeholder="Type your search here…"
              value={value}
              onChange={(e) => { setHi(0); setValue(e.target.value); }}
              onFocus={() => setFocused(true)}
              onBlur={() => setTimeout(() => setFocused(false), 140)}
              onKeyDown={onKeyDown}
            />
          </div>
          {committed.length > 0 ? (
            <button
              className="sb-clear-all" type="button"
              onClick={(e) => { e.preventDefault(); e.stopPropagation(); clearAll(); inputRef.current && inputRef.current.focus(); }}
            >Clear all</button>
          ) : null}
        </div>
      </div>
      {(showSuggestions || showSmart) ? (
        <div className="search-popover">
          {showSmart
            ? (searchSmartMode === 'preview' ? <SmartPreviewPopover /> : <SmartErrorPopover />)
            : items.map((it, i) => {
                const active = i === hiIdx;
                if (it.isSmart) {
                  return (
                    <div className={cx('sp-item', 'sp-smart', active && 'active')} key="smart"
                      onMouseEnter={() => setHi(i)}
                      onMouseDown={(e) => { e.preventDefault(); pick(i); }}>
                      <span className="sp-icon"><Icon name={it.icon} size={13} /></span>
                      <span className="sp-body">
                        <span className="sp-body-row">
                          <span className="sp-type">AI</span>
                          <span className="sp-match"><strong>{it.label}</strong> · <span className="sp-q">{it.q}</span></span>
                        </span>
                      </span>
                      <span className="sp-trail">↵</span>
                    </div>
                  );
                }
                return (
                  <div
                    key={i}
                    className={cx('sp-item', active && 'active', it.disabled && 'disabled')}
                    data-helper-tip={it.disabled ? (it.disabledReason || '') : undefined}
                    onMouseEnter={it.disabled ? undefined : () => setHi(i)}
                    onMouseDown={(e) => { e.preventDefault(); if (!it.disabled) pick(i); }}
                  >
                    <span className="sp-icon"><Icon name={it.icon} size={13} /></span>
                    <span className="sp-body">
                      <span className="sp-body-row">
                        <span className="sp-type">{it.label}</span>
                        <span className="sp-match">{it.isFallback ? it.match : <HighlightQ text={it.match} q={value} />}</span>
                      </span>
                    </span>
                    <span className="sp-trail">{it.disabled ? '' : '↵'}</span>
                  </div>
                );
              })}
        </div>
      ) : null}
      {preview ? <PresetPreview anchorRect={preview.rect} field={preview.field} value={preview.value} /> : null}
    </div>
  );
}

Object.assign(window, { SearchBar, FilterChip, PresetPreview, SmartPreviewPopover, SmartErrorPopover, specToPseudoChips, HighlightQ, SmartPreviewItem });
