---
// Algolia DocSearch — top-of-sidebar search box that opens the standard
// DocSearch modal on click or `/` / Cmd+K. Mirrors the Docusaurus-era
// integration removed in commit b8e0eda; same env vars and indexName
// (`cocoindex`) so the existing crawler config keeps working.
//
// The button itself is rendered server-side and styled to match the
// design system (cream background, hairline border, mono caps row);
// the modal markup + listeners come from `@docsearch/js`, which we
// import dynamically inside a client `<script>` so the search bundle
// loads only when the page actually carries the integration.

// Same env var names as the Docusaurus era (commit b8e0eda removed
// these along with the integration). Visible to `import.meta.env`
// because astro.config.mjs adds `COCOINDEX_` to vite.envPrefix.
// Values inline into the client `<script>` below via `define:vars`.
// The Algolia "search-only API key" is public by design — the
// index is read-only with that key.
const APP_ID  = (import.meta.env.COCOINDEX_DOCS_ALGOLIA_APP_ID  ?? '') as string;
const API_KEY = (import.meta.env.COCOINDEX_DOCS_ALGOLIA_API_KEY ?? '') as string;
// Keep in sync with the crawler config
// (PR #1551 renamed it from `cocoindex`).
const INDEX   = 'cocoindex';
---

<div class="search-wrap">
  <button
    class="search-btn"
    type="button"
    data-search-btn
    aria-label="Search documentation"
  >
    <svg class="ic" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <circle cx="11" cy="11" r="7"></circle>
      <path d="m20 20-3.5-3.5"></path>
    </svg>
    <span class="lbl">Search docs</span>
    <kbd class="kbd"><span class="cmd">⌘</span>K</kbd>
  </button>
</div>

<!--
  Two scripts here, on purpose:

  1. The first one is `is:inline` + `define:vars` — that combination
     emits the values directly into the rendered HTML so they survive
     to the browser. It just stamps `window.__COCOINDEX_DOCSEARCH__`
     and exits.
  2. The second one is a plain `<script>` block. Astro routes plain
     scripts through Vite, which means bare module specifiers like
     `@docsearch/js` resolve correctly. (A `define:vars` script is
     treated as inline and skips Vite — that's why the earlier version
     hit `Failed to resolve module specifier '@docsearch/js'`.) Astro
     also dedupes plain scripts across the page, so a Search rendered
     in both the sidebar and the mobile drawer still registers exactly
     one click handler.
-->
<script is:inline define:vars={{ APP_ID, API_KEY, INDEX }}>
  window.__COCOINDEX_DOCSEARCH__ = { APP_ID, API_KEY, INDEX };
</script>

<script>
  import docsearch from '@docsearch/js';
  import '@docsearch/css';

  type DocSearchConfig = { APP_ID: string; API_KEY: string; INDEX: string };

  declare global {
    interface Window {
      __COCOINDEX_DOCSEARCH__?: DocSearchConfig;
      __docsSearchInit?: boolean;
    }
  }

  if (!window.__docsSearchInit) {
    window.__docsSearchInit = true;

    const cfg = window.__COCOINDEX_DOCSEARCH__ ?? { APP_ID: '', API_KEY: '', INDEX: '' };
    const hasKeys = !!cfg.APP_ID && !!cfg.API_KEY;

    // Find DocSearch's own button inside our hidden host. DocSearch
    // renders synchronously after init(), but a requestAnimationFrame
    // retry covers any future async-render change.
    const fireDocSearch = (host: HTMLElement) => {
      const tryClick = (tries: number) => {
        const btn = host.querySelector<HTMLElement>('.DocSearch-Button');
        if (btn) { btn.click(); return; }
        if (tries <= 0) {
          console.warn('[CocoIndex docs] DocSearch button not found after init.');
          return;
        }
        requestAnimationFrame(() => tryClick(tries - 1));
      };
      tryClick(8);
    };

    let bootP: Promise<HTMLElement | null> | null = null;
    const boot = (): Promise<HTMLElement | null> => {
      if (!hasKeys) {
        console.warn(
          '[CocoIndex docs] Algolia search disabled — set ' +
          'COCOINDEX_DOCS_ALGOLIA_APP_ID and ' +
          'COCOINDEX_DOCS_ALGOLIA_API_KEY in docs/.env to enable.',
        );
        return Promise.resolve(null);
      }
      if (bootP) return bootP;
      bootP = Promise.resolve().then(() => {
        let host = document.getElementById('docsSearchHost');
        if (!host) {
          host = document.createElement('div');
          host.id = 'docsSearchHost';
          // Off-screen instead of `display: none` so DocSearch's button
          // still registers layout/clicks.
          host.style.cssText = 'position:absolute;left:-9999px;top:0;width:1px;height:1px;overflow:hidden;';
          document.body.appendChild(host);
        }
        docsearch({
          appId: cfg.APP_ID,
          apiKey: cfg.API_KEY,
          indexName: cfg.INDEX,
          container: host,
        });
        return host;
      }).catch((err: unknown) => {
        console.error('[CocoIndex docs] DocSearch failed to load:', err);
        return null;
      });
      return bootP;
    };

    // Delegated click — covers buttons added by re-rendered layouts.
    document.addEventListener('click', (e) => {
      const target = e.target as HTMLElement | null;
      const btn = target?.closest?.('[data-search-btn]');
      if (!btn) return;
      boot().then((host) => { if (host) fireDocSearch(host); });
    });

    // Pre-warm on idle so the first click feels instant. DocSearch's
    // own listeners (Cmd+K, /) only register after this runs.
    if (hasKeys) {
      const pre = () => { boot(); };
      if ('requestIdleCallback' in window) {
        (window as Window & { requestIdleCallback: (cb: () => void, opts?: { timeout: number }) => void })
          .requestIdleCallback(pre, { timeout: 1500 });
      } else {
        setTimeout(pre, 600);
      }
    }
  }
</script>
