/* global React */
// Shared components for Eastbrook Discovery.
// Exports to window at the bottom so other Babel files can use them.

const { useState, useEffect, useRef, useMemo } = React;

/* ─── tiny inline icons (stroke-based, no rounded corners on data shapes) ─── */
const Icon = {
  Search: (p) => (
    <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}>
      <circle cx="11" cy="11" r="6.5" /><path d="M20 20l-4.2-4.2" />
    </svg>
  ),
  X: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}>
      <path d="M5 5l14 14M19 5L5 19" />
    </svg>
  ),
  Chevron: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}>
      <path d="M9 6l6 6-6 6" />
    </svg>
  ),
  Down: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}>
      <path d="M6 9l6 6 6-6" />
    </svg>
  ),
  Book: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M5 4h10a3 3 0 013 3v13H8a3 3 0 01-3-3V4z" /><path d="M5 17h13" />
    </svg>
  ),
  Article: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M6 3h9l4 4v14H6z" /><path d="M14 3v5h5M8 12h9M8 16h9M8 8h3" />
    </svg>
  ),
  Globe: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3c3 3.5 3 14.5 0 18M12 3c-3 3.5-3 14.5 0 18" />
    </svg>
  ),
  Pin: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M12 21s-7-6.4-7-12a7 7 0 1114 0c0 5.6-7 12-7 12z" /><circle cx="12" cy="9" r="2.4" />
    </svg>
  ),
  Star: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M12 3l2.7 5.5 6.1.9-4.4 4.3 1 6-5.4-2.9L6.6 19.7l1-6L3.2 9.4l6.1-.9z" />
    </svg>
  ),
  StarFill: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" {...p}>
      <path d="M12 3l2.7 5.5 6.1.9-4.4 4.3 1 6-5.4-2.9L6.6 19.7l1-6L3.2 9.4l6.1-.9z" />
    </svg>
  ),
  Quote: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" {...p}>
      <path d="M6 17V9h6v8H6zm10 0V9h6v8h-6z" opacity=".15" />
      <path d="M5 17V11c0-2.2 1.4-4 4-4v2c-1.4 0-2 1-2 2v1h2v5H5zm10 0V11c0-2.2 1.4-4 4-4v2c-1.4 0-2 1-2 2v1h2v5h-4z" fill="currentColor" />
    </svg>
  ),
  Cite: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M4 6h16M4 12h16M4 18h10" />
    </svg>
  ),
  Sparkle: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" {...p}>
      <path d="M12 2l1.6 5.6L19 9.2l-5.4 1.6L12 16.4l-1.6-5.6L5 9.2l5.4-1.6L12 2zM19 14l.8 2.6 2.6.8-2.6.8L19 21l-.8-2.8-2.6-.8 2.6-.8L19 14z" />
    </svg>
  ),
  Filter: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M4 5h16l-6 8v6l-4 1v-7L4 5z" />
    </svg>
  ),
  Mic: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <rect x="9" y="3" width="6" height="12" rx="3" /><path d="M5 11a7 7 0 0014 0M12 19v3" />
    </svg>
  ),
  Newspaper: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M3 6h14v14H5a2 2 0 01-2-2V6zM17 9h4v9a2 2 0 01-2 2h-2" /><path d="M6 10h8M6 14h8M6 18h5" />
    </svg>
  ),
  Database: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <ellipse cx="12" cy="6" rx="7" ry="2.5" /><path d="M5 6v6c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5V6M5 12v6c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5v-6" />
    </svg>
  ),
  Cap: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M3 9l9-4 9 4-9 4-9-4zM7 11v5c2 1.5 8 1.5 10 0v-5M21 9v5" />
    </svg>
  ),
  Heart: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M12 20s-7-4.4-7-10a4 4 0 017-2.6A4 4 0 0119 10c0 5.6-7 10-7 10z" />
    </svg>
  ),
  Share: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <circle cx="6" cy="12" r="2.5" /><circle cx="18" cy="6" r="2.5" /><circle cx="18" cy="18" r="2.5" />
      <path d="M8 11l8-4M8 13l8 4" />
    </svg>
  ),
  Library: (p) => (
    <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M3 21V8l9-5 9 5v13M3 21h18M7 21V11M11 21V11M15 21V11M19 21V11" />
    </svg>
  ),
  Arrow: (p) => (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}>
      <path d="M5 12h14M13 6l6 6-6 6" />
    </svg>
  ),
};

/* ─── Resource type → glyph ─── */
function TypeGlyph({ type, size = 14 }) {
  const map = {
    article: Icon.Article, book: Icon.Book, ebook: Icon.Book,
    thesis: Icon.Cap, newspaper: Icon.Newspaper, dataset: Icon.Database,
    video: Icon.Database,
  };
  const I = map[type] || Icon.Article;
  return <I width={size} height={size} />;
}

const TYPE_LABEL = {
  article: "Article", book: "Book", ebook: "E-book",
  thesis: "Thesis", newspaper: "News", dataset: "Dataset", video: "Audio / Video",
};

/* ─── Header bar ─── */
function HeaderBar({ query, setQuery, onSubmit, route, goto, savedCount, accentInk }) {
  const [open, setOpen] = useState(false);
  const inputRef = useRef(null);

  function handleSubmit(e) {
    e?.preventDefault?.();
    setOpen(false);
    onSubmit?.(query);
  }

  return (
    <header style={{
      borderBottom: "1px solid var(--hairline)",
      background: "var(--paper)",
      position: "sticky", top: 0, zIndex: 50,
    }}>
      {/* Top utility row */}
      <div style={{
        display: "flex", justifyContent: "space-between", alignItems: "center",
        padding: "8px 32px", fontSize: 12, color: "var(--muted)",
        borderBottom: "1px solid var(--hairline)",
      }}>
        <div style={{ display: "flex", gap: 18 }}>
          <span>Eastbrook University</span>
          <span style={{ color: "var(--hairline-2)" }}>|</span>
          <a href="#" style={linkSm}>Hours &amp; Locations</a>
          <a href="#" style={linkSm}>Ask a Librarian</a>
          <a href="#" style={linkSm}>Interlibrary Loan</a>
        </div>
        <div style={{ display: "flex", gap: 18 }}>
          <a href="#" style={linkSm}>Help</a>
          <a href="#" style={linkSm}>Sign in</a>
        </div>
      </div>

      {/* Brand + search row */}
      <div style={{
        display: "grid", gridTemplateColumns: "minmax(280px,1fr) minmax(380px, 720px) minmax(220px,1fr)",
        alignItems: "center", padding: "18px 32px", gap: 24,
      }}>
        <button
          onClick={() => goto("home")}
          style={{
            background: "transparent", border: 0, padding: 0, cursor: "default",
            display: "flex", alignItems: "center", gap: 12, textAlign: "left",
          }}
        >
          <span style={{
            width: 38, height: 38, display: "grid", placeItems: "center",
            background: accentInk, color: "var(--accent-ink)",
          }}>
            <Icon.Library width={20} height={20} />
          </span>
          <span style={{ display: "flex", flexDirection: "column", lineHeight: 1.1 }}>
            <span className="serif" style={{ fontSize: 19, fontWeight: 600, letterSpacing: "-0.01em" }}>
              Eastbrook
            </span>
            <span className="smallcaps" style={{ marginTop: 2, color: "var(--muted)" }}>
              University Libraries
            </span>
          </span>
        </button>

        {/* Search */}
        <form onSubmit={handleSubmit} style={{ position: "relative" }}>
          <div style={{
            display: "grid", gridTemplateColumns: "auto 1fr auto auto",
            alignItems: "center",
            border: "1.5px solid var(--ink)",
            background: "var(--surface)",
            height: 48,
          }}>
            <div style={{ padding: "0 14px", color: "var(--ink)" }}>
              <Icon.Search width={18} height={18} />
            </div>
            <input
              ref={inputRef}
              type="text"
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              onFocus={() => setOpen(true)}
              onBlur={() => setTimeout(() => setOpen(false), 120)}
              placeholder="Search books, articles, databases, archives…"
              style={{
                background: "transparent", border: 0, outline: "none",
                fontFamily: "var(--font-sans)", fontSize: 15, color: "var(--ink)",
                padding: "0 4px",
              }}
            />
            <button type="button" title="Voice search" style={iconBtnGhost}>
              <Icon.Mic />
            </button>
            <button type="submit" style={{
              background: accentInk, color: "var(--accent-ink)",
              border: 0, height: "100%", padding: "0 22px",
              fontFamily: "var(--font-sans)", fontSize: 13, letterSpacing: "0.04em",
              fontWeight: 600, textTransform: "uppercase",
            }}>
              Search
            </button>
          </div>

          {/* Search dropdown */}
          {open && (
            <div style={{
              position: "absolute", left: 0, right: 0, top: "calc(100% + 6px)",
              background: "var(--surface)", border: "1px solid var(--hairline-2)",
              boxShadow: "0 18px 40px -16px rgba(28,24,20,0.18)",
              padding: "8px 0", zIndex: 60,
            }}>
              <div className="smallcaps" style={{ padding: "8px 16px 4px" }}>Recent searches</div>
              {window.LD_DATA.SUGGESTIONS.slice(0, 3).map((s) => (
                <button key={s} onMouseDown={(e) => { e.preventDefault(); setQuery(s); handleSubmit(); }} style={suggestBtn}>
                  <Icon.Search width={13} height={13} style={{ opacity: 0.5 }} />
                  <span>{s}</span>
                </button>
              ))}
              <div className="smallcaps" style={{ padding: "12px 16px 4px" }}>Suggested</div>
              {window.LD_DATA.SUGGESTIONS.slice(3).map((s) => (
                <button key={s} onMouseDown={(e) => { e.preventDefault(); setQuery(s); handleSubmit(); }} style={suggestBtn}>
                  <Icon.Sparkle width={13} height={13} style={{ opacity: 0.6, color: "var(--accent)" }} />
                  <span>{s}</span>
                </button>
              ))}
              <div style={{ borderTop: "1px solid var(--hairline)", padding: "8px 16px", display: "flex", justifyContent: "space-between", color: "var(--muted)", fontSize: 12 }}>
                <span>Press <span className="mono">↵</span> to search across all collections</span>
                <a href="#" style={{ color: "var(--accent)", textDecoration: "none" }}>Advanced search →</a>
              </div>
            </div>
          )}
        </form>

        {/* Right nav */}
        <nav style={{ display: "flex", justifyContent: "flex-end", gap: 22, alignItems: "center" }}>
          <NavLink active={route === "home"} onClick={() => goto("home")}>Browse</NavLink>
          <NavLink active={route === "collections-lobby" || route === "collections-discover"} onClick={() => goto("collections-lobby")}>Collections</NavLink>
          <NavLink>Databases</NavLink>
          <NavLink>
            Saved
            {savedCount > 0 && (
              <span style={savedBadge}>{savedCount}</span>
            )}
          </NavLink>
          <NavLink>My Account</NavLink>
        </nav>
      </div>
    </header>
  );
}

function NavLink({ children, active, onClick }) {
  return (
    <button onClick={onClick} style={{
      background: "transparent", border: 0, padding: "6px 0",
      borderBottom: active ? "2px solid var(--ink)" : "2px solid transparent",
      color: "var(--ink)", fontSize: 14, fontWeight: 500, cursor: "default",
      display: "inline-flex", alignItems: "center", gap: 6,
    }}>
      {children}
    </button>
  );
}

const linkSm = { color: "var(--muted)", textDecoration: "none", fontSize: 12 };
const iconBtnGhost = {
  background: "transparent", border: 0, padding: "0 10px",
  color: "var(--muted)", height: 32, display: "grid", placeItems: "center", cursor: "default",
};
const suggestBtn = {
  display: "flex", alignItems: "center", gap: 10, width: "100%",
  background: "transparent", border: 0, padding: "8px 16px", textAlign: "left",
  fontSize: 14, color: "var(--ink)", cursor: "default",
};
const savedBadge = {
  display: "inline-grid", placeItems: "center", minWidth: 18, height: 18,
  background: "var(--accent)", color: "var(--accent-ink)", borderRadius: 999,
  fontSize: 11, padding: "0 6px", marginLeft: 4, fontFamily: "var(--font-mono)",
};

/* ─── Pill ─── */
function Pill({ children, tone = "neutral", style }) {
  const tones = {
    neutral: { bg: "var(--tag)", fg: "var(--ink-2)" },
    ghost:   { bg: "transparent", fg: "var(--muted)", border: "1px solid var(--hairline-2)" },
    good:    { bg: "transparent", fg: "var(--good)", border: "1px solid var(--good)" },
    warn:    { bg: "transparent", fg: "var(--warn)", border: "1px solid var(--warn)" },
    accent:  { bg: "var(--accent-soft)", fg: "var(--accent)" },
  };
  const t = tones[tone] || tones.neutral;
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 5,
      padding: "2px 8px", background: t.bg, color: t.fg, border: t.border || "1px solid transparent",
      fontSize: 11.5, fontWeight: 500, letterSpacing: "0.01em",
      fontFamily: "var(--font-sans)",
      ...style,
    }}>
      {children}
    </span>
  );
}

/* ─── Facet group (collapsible) ─── */
function FacetGroup({ title, facet, selected, onToggle, defaultOpen = true }) {
  const [open, setOpen] = useState(defaultOpen);
  return (
    <div style={{ borderBottom: "1px solid var(--hairline)", padding: "14px 0" }}>
      <button onClick={() => setOpen(!open)} style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        width: "100%", background: "transparent", border: 0, padding: 0,
        textAlign: "left", cursor: "default",
      }}>
        <span className="smallcaps" style={{ color: "var(--ink)" }}>{title}</span>
        <span style={{ color: "var(--muted)", transform: open ? "rotate(180deg)" : "none", transition: "transform .15s" }}>
          <Icon.Down />
        </span>
      </button>
      {open && (
        <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 4 }}>
          {facet.map((f) => {
            const isOn = selected.includes(f.id);
            return (
              <button key={f.id} onClick={() => onToggle(f.id)} style={{
                display: "grid", gridTemplateColumns: "16px 1fr auto", alignItems: "center", gap: 10,
                background: "transparent", border: 0, padding: "5px 0", cursor: "default", textAlign: "left",
                color: "var(--ink)",
              }}>
                <span style={{
                  width: 14, height: 14, border: "1.5px solid " + (isOn ? "var(--accent)" : "var(--hairline-2)"),
                  background: isOn ? "var(--accent)" : "transparent",
                  display: "grid", placeItems: "center", color: "var(--accent-ink)",
                  fontSize: 10, lineHeight: 1,
                }}>{isOn ? "✓" : ""}</span>
                <span style={{ fontSize: 13.5 }}>{f.label}</span>
                <span className="mono" style={{ fontSize: 11.5, color: "var(--muted)" }}>{fmt(f.count)}</span>
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

function fmt(n) {
  return n >= 1000 ? (n / 1000).toFixed(n >= 10000 ? 0 : 1) + "k" : String(n);
}

/* ─── Result card ─── */
function ResultCard({ item, layout, onOpen, onSave, saved, query }) {
  const isShelf = item.access === "shelf";
  const accessLabel = isShelf
    ? (item.availability && item.availability.startsWith("Available") ? "Available on shelf" : "Checked out")
    : (item.access === "ebook" ? "Read e-book online" : "Online — full text");
  const accessTone = isShelf && !item.availability?.startsWith("Available") ? "warn" : "good";

  return (
    <article style={{
      display: "grid",
      gridTemplateColumns: layout === "card" ? "1fr" : "44px 1fr 220px",
      gap: layout === "card" ? 14 : 22,
      padding: layout === "card" ? "20px" : "22px 4px",
      borderBottom: layout === "card" ? "none" : "1px solid var(--hairline)",
      border: layout === "card" ? "1px solid var(--hairline)" : undefined,
      background: layout === "card" ? "var(--surface)" : "transparent",
    }}>
      {/* Index / type */}
      {layout !== "card" && (
        <div style={{
          display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 6,
          color: "var(--muted)",
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 5, color: "var(--ink)" }}>
            <TypeGlyph type={item.type} />
          </div>
          <span className="smallcaps" style={{ fontSize: 9.5, color: "var(--muted)" }}>{TYPE_LABEL[item.type]}</span>
        </div>
      )}

      {/* Main body */}
      <div style={{ display: "flex", flexDirection: "column", gap: 8, minWidth: 0 }}>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center" }}>
          {layout === "card" && <Pill tone="neutral"><TypeGlyph type={item.type} size={11} />{TYPE_LABEL[item.type]}</Pill>}
          {item.peerReviewed && <Pill tone="ghost">Peer-reviewed</Pill>}
          {item.openAccess && <Pill tone="accent">Open access</Pill>}
        </div>

        <h3
          onClick={() => onOpen(item.id)}
          className="serif"
          style={{
            margin: 0, fontSize: 21, lineHeight: 1.25, color: "var(--ink)",
            cursor: "default", letterSpacing: "-0.005em",
            textWrap: "pretty",
          }}
        >
          <Highlight text={item.title} query={query} />
        </h3>

        <div style={{ fontSize: 13.5, color: "var(--ink-2)" }}>
          {item.authors.join("; ")}
          {item.year ? ` · ${item.year}` : ""}
          {item.venue ? <> · <em style={{ fontFamily: "var(--font-serif)", fontStyle: "italic" }}>{item.venue}</em></> : null}
          {item.vol ? <span>, {item.vol}</span> : null}
          {item.pages ? <span>, {item.pages}</span> : null}
        </div>

        {item.abstract && (
          <p style={{ margin: "4px 0 0", fontSize: 13.5, color: "var(--muted)", lineHeight: 1.55, maxWidth: "62ch" }}>
            <Highlight text={item.abstract} query={query} />
          </p>
        )}

        <div style={{ display: "flex", alignItems: "center", gap: 18, marginTop: 4, fontSize: 12.5, color: "var(--muted)" }}>
          {item.doi && <span className="mono" style={{ fontSize: 11.5 }}>doi: {item.doi}</span>}
          {item.callNumber && <span className="mono" style={{ fontSize: 11.5 }}>{item.callNumber}</span>}
          {typeof item.cited === "number" && (
            <span style={{ display: "inline-flex", alignItems: "center", gap: 5 }}>
              <Icon.Quote width={12} height={12} /> Cited by {item.cited}
            </span>
          )}
        </div>

        <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
          <button onClick={() => onOpen(item.id)} style={btnPrimary}>View details <Icon.Chevron /></button>
          <button onClick={() => onSave(item.id)} style={btnGhost}>
            {saved ? <><Icon.StarFill /> Saved</> : <><Icon.Star /> Save</>}
          </button>
          <button style={btnGhost}><Icon.Cite /> Cite</button>
          <button style={btnGhost}><Icon.Share /> Share</button>
        </div>
      </div>

      {/* Right rail — availability */}
      {layout !== "card" && (
        <div style={{ display: "flex", flexDirection: "column", gap: 8, alignItems: "flex-start" }}>
          <Pill tone={accessTone}>
            <span style={{
              width: 6, height: 6, borderRadius: 999,
              background: accessTone === "good" ? "var(--good)" : "var(--warn)",
              display: "inline-block",
            }} />
            {accessLabel}
          </Pill>
          {isShelf && (
            <>
              <div style={{ fontSize: 12.5, color: "var(--ink-2)" }}>
                <div className="mono" style={{ fontSize: 11.5, color: "var(--ink)" }}>{item.callNumber}</div>
                <div style={{ color: "var(--muted)", marginTop: 2 }}>{item.location}</div>
              </div>
              <button style={btnTiny}><Icon.Pin width={11} height={11} /> Map shelf</button>
            </>
          )}
          {!isShelf && item.doi && (
            <div style={{ fontSize: 12, color: "var(--muted)" }}>
              Via <span style={{ color: "var(--accent)" }}>JSTOR · ScienceDirect</span>
            </div>
          )}
        </div>
      )}
    </article>
  );
}

function Highlight({ text, query }) {
  if (!query) return <>{text}</>;
  const terms = query.split(/\s+/).filter(t => t.length > 2);
  if (!terms.length) return <>{text}</>;
  const re = new RegExp("(" + terms.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") + ")", "gi");
  const parts = String(text).split(re);
  return (
    <>{parts.map((p, i) => re.test(p)
      ? <mark key={i} style={{ background: "var(--accent-soft)", color: "var(--accent)", padding: "0 1px" }}>{p}</mark>
      : <span key={i}>{p}</span>)}</>
  );
}

const btnPrimary = {
  display: "inline-flex", alignItems: "center", gap: 6,
  background: "var(--ink)", color: "var(--paper)", border: 0,
  padding: "8px 14px", fontSize: 13, fontWeight: 500, cursor: "default",
  fontFamily: "var(--font-sans)",
};
const btnGhost = {
  display: "inline-flex", alignItems: "center", gap: 6,
  background: "transparent", color: "var(--ink-2)", border: "1px solid var(--hairline-2)",
  padding: "8px 12px", fontSize: 13, fontWeight: 500, cursor: "default",
  fontFamily: "var(--font-sans)",
};
const btnTiny = {
  display: "inline-flex", alignItems: "center", gap: 5,
  background: "transparent", color: "var(--ink)", border: "1px solid var(--hairline-2)",
  padding: "5px 8px", fontSize: 12, fontWeight: 500, cursor: "default",
  fontFamily: "var(--font-sans)",
};

/* ─── AI Assistant panel ─── */
function AIAssistant({ query, onOpenItem, onClose }) {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const scrollRef = useRef(null);

  useEffect(() => {
    if (query) {
      setMessages([{
        role: "assistant",
        text: `I can help you explore "${query}" across the Eastbrook collections. Here's what I'm seeing in the top results — ask me to summarise a paper, find counter-arguments, or build a starter reading list.`,
        suggestions: [
          "Summarise the top 3 articles",
          "Build me a starter reading list",
          "Find recent open-access work",
          "Suggest related search terms",
        ],
      }]);
    }
  }, [query]);

  useEffect(() => {
    scrollRef.current?.scrollTo({ top: 9e9, behavior: "smooth" });
  }, [messages, busy]);

  async function send(text) {
    const t = (text ?? input).trim();
    if (!t) return;
    setInput("");
    setMessages((m) => [...m, { role: "user", text: t }]);
    setBusy(true);
    try {
      const ctx = window.LD_DATA.RESULTS.slice(0, 5).map((r, i) =>
        `[${i + 1}] ${r.title} — ${r.authors.join(", ")} (${r.year}, ${r.venue})`).join("\n");
      const prompt =
        `You are a calm, scholarly research librarian for Eastbrook University.\n` +
        `User query: "${query || "(none)"}"\n\n` +
        `Top results in the catalogue:\n${ctx}\n\n` +
        `User asks: "${t}"\n\n` +
        `Reply in 3–5 short sentences. When useful, refer to results by their number e.g. [1]. ` +
        `End with one or two follow-up questions on their own lines prefixed with "→ ".`;
      const reply = await window.claude.complete(prompt);
      const lines = reply.split("\n").map(l => l.trim()).filter(Boolean);
      const followUps = lines.filter(l => l.startsWith("→")).map(l => l.replace(/^→\s*/, ""));
      const body = lines.filter(l => !l.startsWith("→")).join(" ");
      setMessages((m) => [...m, { role: "assistant", text: body, suggestions: followUps }]);
    } catch (e) {
      setMessages((m) => [...m, { role: "assistant", text: "Sorry — I couldn't reach the assistant just now. Try again in a moment." }]);
    } finally {
      setBusy(false);
    }
  }

  return (
    <aside style={{
      display: "flex", flexDirection: "column",
      background: "var(--surface)", border: "1px solid var(--hairline)",
      height: "100%", minHeight: 0,
    }}>
      <div style={{
        padding: "14px 16px", borderBottom: "1px solid var(--hairline)",
        display: "flex", alignItems: "center", justifyContent: "space-between",
        background: "var(--paper-2)",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ color: "var(--accent)" }}><Icon.Sparkle width={16} height={16} /></span>
          <span className="serif" style={{ fontSize: 16, fontWeight: 600 }}>Research Assistant</span>
          <Pill tone="ghost" style={{ fontSize: 10 }}>Beta</Pill>
        </div>
        <button onClick={onClose} style={iconBtnGhost} title="Close">
          <Icon.X />
        </button>
      </div>

      <div ref={scrollRef} style={{
        flex: 1, overflowY: "auto", padding: 16, display: "flex", flexDirection: "column", gap: 14,
      }}>
        {messages.map((m, i) => (
          <div key={i} style={{
            alignSelf: m.role === "user" ? "flex-end" : "flex-start",
            maxWidth: "92%",
          }}>
            {m.role === "assistant" && (
              <div className="smallcaps" style={{ fontSize: 9.5, color: "var(--muted)", marginBottom: 4 }}>
                Assistant
              </div>
            )}
            <div style={{
              fontFamily: m.role === "assistant" ? "var(--font-serif)" : "var(--font-sans)",
              fontSize: m.role === "assistant" ? 15 : 14,
              lineHeight: 1.55,
              color: "var(--ink)",
              background: m.role === "user" ? "var(--paper-2)" : "transparent",
              padding: m.role === "user" ? "8px 12px" : 0,
            }}>
              {m.text}
            </div>
            {m.suggestions?.length > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 6, marginTop: 10 }}>
                {m.suggestions.map((s, j) => (
                  <button key={j} onClick={() => send(s)} style={{
                    display: "inline-flex", alignItems: "center", gap: 6, justifyContent: "space-between",
                    background: "transparent", border: "1px solid var(--hairline-2)", color: "var(--ink-2)",
                    padding: "6px 10px", fontSize: 12.5, textAlign: "left", cursor: "default",
                    fontFamily: "var(--font-sans)",
                  }}>
                    <span>{s}</span>
                    <Icon.Arrow width={12} height={12} />
                  </button>
                ))}
              </div>
            )}
          </div>
        ))}
        {busy && (
          <div style={{ display: "flex", alignItems: "center", gap: 6, color: "var(--muted)", fontSize: 12.5 }}>
            <Dot /> <Dot delay=".15s" /> <Dot delay=".3s" /> <span>Reading…</span>
          </div>
        )}
      </div>

      <form onSubmit={(e) => { e.preventDefault(); send(); }} style={{
        borderTop: "1px solid var(--hairline)", padding: 12,
        display: "grid", gridTemplateColumns: "1fr auto", gap: 8,
      }}>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask about these results…"
          style={{
            background: "var(--paper)", border: "1px solid var(--hairline-2)",
            padding: "8px 10px", fontSize: 13, color: "var(--ink)",
            fontFamily: "var(--font-sans)", outline: "none",
          }}
        />
        <button type="submit" style={btnPrimary}>Ask</button>
      </form>
    </aside>
  );
}

function Dot({ delay = "0s" }) {
  return (
    <span style={{
      width: 5, height: 5, borderRadius: 999, background: "var(--muted)",
      display: "inline-block",
      animation: "ld-bounce 1s infinite ease-in-out",
      animationDelay: delay,
    }} />
  );
}

// Inject keyframes once
if (!document.getElementById("ld-keyframes")) {
  const s = document.createElement("style");
  s.id = "ld-keyframes";
  s.textContent = `
    @keyframes ld-bounce { 0%, 80%, 100% { opacity: .25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
    @keyframes ld-fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
    @keyframes ld-shimmer { from { background-position: -200px 0; } to { background-position: 200px 0; } }
    .ld-fade { animation: ld-fade .25s ease-out both; }
    mark { background: var(--accent-soft); color: var(--accent); }
  `;
  document.head.appendChild(s);
}

Object.assign(window, {
  Icon, TypeGlyph, TYPE_LABEL,
  HeaderBar, Pill, FacetGroup, ResultCard, AIAssistant,
  fmt, btnPrimary, btnGhost, btnTiny,
});
