/* ============================================================
   GLOBAL SEARCH — command palette (Ctrl/Cmd+K)
   Searches the state payload already in memory. No server round trip.
   ============================================================ */

/** Flatten every module's records into one searchable list. */
function buildSearchIndex(state, idx){
  const out = [];

  state.components.forEach(c => out.push({
    kind:'Component', icon: typeIconFor(idx.type[c.typeId]),
    title: c.name, sub: [(idx.type[c.typeId]||{}).name, c.identifier].filter(Boolean).join(' · '),
    keywords: [c.identifier, c.vendor, ...(c.tags||[])].filter(Boolean).join(' '),
    go: (go)=>go({view:'component', id:c.id})
  }));

  state.people.forEach(p => out.push({
    kind:'Person', icon: I.users,
    title: p.name, sub: [p.jobTitle, p.department].filter(Boolean).join(' · '),
    keywords: [p.email, p.phone].filter(Boolean).join(' '),
    go: (go)=>go({view:'directory', tab:'people'})
  }));

  state.roles.forEach(r => out.push({
    kind:'Role', icon: I.users,
    title: r.name,
    sub: r.primaryPersonId ? 'Primary: ' + ((idx.person[r.primaryPersonId]||{}).name || '') : 'No primary assignee',
    keywords: r.description || '',
    go: (go)=>go({view:'directory', tab:'roles'})
  }));

  // Every link on a tool/service record — the primary one plus each extra —
  // is its own search entry, since picking one opens that link directly in a
  // new tab rather than landing on the tool's record page.
  state.tools.forEach(t => {
    const allLinks = [{label: t.primaryLabel || 'Production', url: t.primaryUrl, primary:true},
      ...(t.links||[])];
    allLinks.filter(l => l.url).forEach(l => out.push({
      kind:'Tool', icon: I.tool,
      title: l.primary ? t.name : `${t.name} — ${l.label}`,
      sub: l.primary ? [t.category, t.status].filter(Boolean).join(' · ') : l.label,
      keywords: [t.vendor, t.description, l.label, l.url].filter(Boolean).join(' '),
      go: ()=>window.open(l.url, '_blank', 'noopener,noreferrer')
    }));
  });

  state.documents.forEach(d => out.push({
    kind:'Document', icon: I.book,
    title: d.title, sub: [d.type, d.status].filter(Boolean).join(' · '),
    keywords: d.tags ? d.tags.join(' ') : '',
    go: (go)=>go({view:'documents'})
  }));

  state.requests.forEach(r => out.push({
    kind:'Request', icon: I.inbox,
    title: `${r.reference} ${r.title}`, sub: [r.status, 'P' + r.priority].filter(Boolean).join(' · '),
    keywords: r.description || '',
    go: (go)=>go({view:'request', id:r.id})
  }));

  state.changes.forEach(c => out.push({
    kind:'Change', icon: I.change,
    title: `${c.reference} ${c.title}`, sub: [c.changeType, c.status].filter(Boolean).join(' · '),
    keywords: c.description || '',
    go: (go)=>go({view:'change', id:c.id})
  }));

  state.reports.forEach(r => out.push({
    kind:'Report', icon: I.chart,
    title: r.name, sub: [r.type, r.status].filter(Boolean).join(' · '),
    keywords: r.distributionNotes || '',
    go: (go)=>go({view:'reports'})
  }));

  state.quickLinks.forEach(q => out.push({
    kind:'Quick link', icon: I[q.icon] || I.link,
    title: q.label, sub: q.groupLabel,
    keywords: q.description || '',
    go: (go)=>{
      if (/^https?:\/\//i.test(q.target)) window.open(q.target, '_blank', 'noopener,noreferrer');
      else { window.location.hash = q.target.replace(/^#/, ''); go(routeFromHash()); }
    }
  }));

  return out;
}

/** Case-insensitive, whole-string match score. Higher is better; null means no match. */
function matchScore(entry, term){
  const title = entry.title.toLowerCase();
  const hay = (entry.title + ' ' + entry.sub + ' ' + entry.keywords).toLowerCase();
  if (title.startsWith(term)) return 3;
  if (title.includes(term)) return 2;
  if (hay.includes(term)) return 1;
  return null;
}

function CommandPalette({state, idx, go, onClose}){
  const [q, setQ] = useState('');
  const [active, setActive] = useState(0);
  const inputRef = useRef(null);

  const index = useMemo(()=>buildSearchIndex(state, idx), [state, idx]);

  const results = useMemo(()=>{
    const term = q.trim().toLowerCase();
    if (!term) return [];
    return index
      .map(e => ({e, score: matchScore(e, term)}))
      .filter(x => x.score != null)
      .sort((a,b) => b.score - a.score)
      .slice(0, 30)
      .map(x => x.e);
  }, [index, q]);

  useEffect(()=>{ setActive(0); }, [q]);
  useEffect(()=>{ inputRef.current && inputRef.current.focus(); }, []);

  const choose = (entry) => {
    if (!entry) return;
    entry.go(go);
    onClose();
  };

  const onKeyDown = (e) => {
    if (e.key === 'Escape') { onClose(); return; }
    if (e.key === 'ArrowDown') { e.preventDefault(); setActive(a => Math.min(a + 1, results.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(a => Math.max(a - 1, 0)); }
    else if (e.key === 'Enter') { e.preventDefault(); choose(results[active]); }
  };

  // Group results by kind, preserving score order within each group.
  const groups = [];
  results.forEach(r => {
    let g = groups.find(g => g.kind === r.kind);
    if (!g) { g = {kind:r.kind, items:[]}; groups.push(g); }
    g.items.push(r);
  });

  return (
    <div className="cmdk-overlay" onMouseDown={(e)=>{ if (e.target === e.currentTarget) onClose(); }}>
      <div className="cmdk-panel" onMouseDown={e=>e.stopPropagation()}>
        <div className="cmdk-input">
          <I.search size={16}/>
          <input ref={inputRef} type="text" placeholder="Search components, people, requests, documents…"
            value={q} onChange={e=>setQ(e.target.value)} onKeyDown={onKeyDown}/>
          <kbd>Esc</kbd>
        </div>
        <div className="cmdk-list">
          {!q.trim() &&
            <div className="cmdk-empty">Start typing to search across every module.</div>}
          {q.trim() && results.length === 0 &&
            <div className="cmdk-empty">Nothing matches "{q}"</div>}
          {groups.map(g => (
            <div key={g.kind}>
              <div className="cmdk-group-label">{g.kind}</div>
              {g.items.map(item => {
                const i = results.indexOf(item);
                const Icon = item.icon;
                return (
                  <div key={i} className={'cmdk-item' + (i === active ? ' active' : '')}
                    onMouseEnter={()=>setActive(i)} onClick={()=>choose(item)}>
                    <span className="cmdk-item-icon"><Icon size={15}/></span>
                    <div style={{flex:1,minWidth:0}}>
                      <div className="truncate" style={{fontWeight:600,fontSize:13.5}}>{item.title}</div>
                      {item.sub && <div className="cell-sub truncate">{item.sub}</div>}
                    </div>
                    {item.kind === 'Tool' && <span className="cmdk-item-ext" title="Opens in a new tab">
                      <I.external size={13}/></span>}
                  </div>
                );
              })}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}
