// ============================================================
// KENNISBANK · Blog detail shell + reusable building blocks
// ------------------------------------------------------------
// Every blog page does:
//   const BLOG = { slug, type, title, excerpt, date, author, ... };
//   const FAQ = [{ q, a }, ...];
//   <BlogShell blog={BLOG} body={<BlogBody/>} faq={FAQ} />
// The shell handles meta, cover, TOC, share, FAQ, CTA, footer.
// ============================================================

// ---- Slugify for stable h2 IDs ----------------------------------------------
function kbSlug(s) {
  return (s || '')
    .toString()
    .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

// ---- Wrap each h2 in BlogBody with an id, derived from text -----------------
// We grab the rendered DOM after mount and stamp ids + collect TOC.
function useExtractedToc(scopeRef) {
  const [toc, setToc] = React.useState([]);
  React.useEffect(() => {
    if (!scopeRef.current) return;
    const heads = Array.from(scopeRef.current.querySelectorAll('h2:not(.kbb-faq-title)'));
    const tocItems = heads.map((h, i) => {
      if (!h.id) h.id = kbSlug(h.textContent) || `section-${i + 1}`;
      return { id: h.id, text: h.textContent };
    });
    setToc(tocItems);
  }, [scopeRef]);
  return toc;
}

// ---- Scrollspy: highlight active TOC entry ---------------------------------
function useActiveHeading(toc, scopeRef) {
  const [active, setActive] = React.useState(toc[0]?.id);
  React.useEffect(() => {
    if (!toc.length || !scopeRef.current) return;
    const ids = toc.map(t => t.id);
    const els = ids.map(id => document.getElementById(id)).filter(Boolean);
    if (!els.length) return;
    const onScroll = () => {
      const offset = 140;
      let current = els[0].id;
      for (const el of els) {
        if (el.getBoundingClientRect().top - offset <= 0) current = el.id;
      }
      setActive(current);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [toc, scopeRef]);
  return active;
}

// ---- Initials helper -------------------------------------------------------
function kbInitials(name) {
  return (name || '')
    .split(/\s+/)
    .map(p => p[0])
    .filter(Boolean)
    .slice(0, 2)
    .join('')
    .toUpperCase();
}

// ---- Reading progress bar ---------------------------------------------------
function BlogProgress() {
  const fillRef = React.useRef(null);
  React.useEffect(() => {
    const onScroll = () => {
      const article = document.querySelector('.kbb-prose');
      if (!article || !fillRef.current) return;
      const r = article.getBoundingClientRect();
      const articleTop = r.top + window.scrollY;
      const articleHeight = r.height;
      const probe = window.scrollY + window.innerHeight * 0.55;
      const raw = (probe - articleTop) / articleHeight;
      const pct = Math.max(0, Math.min(1, raw));
      fillRef.current.style.transform = `scaleX(${pct})`;
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);
  return (
    <div className="kbb-progress" aria-hidden="true">
      <div className="kbb-progress-fill" ref={fillRef}></div>
    </div>
  );
}

// ---- Hero ------------------------------------------------------------------
function BlogHero({ blog }) {
  const t = KB_TYPES[blog.type] || KB_TYPES.blog;
  const read = blog.readingMinutes
    ? `${blog.readingMinutes} min ${blog.type === 'podcast' ? 'luisteren' : blog.type === 'video' ? 'kijken' : 'lezen'}`
    : null;
  return (
    <section className="kbb-hero">
      <div className="kbb-hero-inner">
        <div className="kbb-hero-meta">
          <span
            className="kb-type"
            style={{ '--chip-bg': t.bg, '--chip-color': t.color, '--chip-accent': t.accent }}
          >
            {t.label}
          </span>
          <span className="kb-meta-text">{kbFormatDate(blog.date)}</span>
          {read && (
            <>
              <span className="kb-dot-sep" aria-hidden="true"></span>
              <span className="kb-meta-text">{read}</span>
            </>
          )}
        </div>
        <h1>{blog.title}</h1>
        {blog.excerpt && <p className="lede">{blog.excerpt}</p>}
        {blog.author && (
          <div className="kbb-hero-author">
            <span className="avatar">{kbInitials(blog.author)}</span>
            <span className="who">
              <span className="name">{blog.author}</span>
              <span className="role">Repetive</span>
            </span>
          </div>
        )}
      </div>
    </section>
  );
}

// ---- Cover band ------------------------------------------------------------
function BlogCover({ blog }) {
  const t = KB_TYPES[blog.type] || KB_TYPES.blog;
  return (
    <section className="kbb-cover">
      <KbCover seed={blog.slug} {...(blog.cover || {})} aspect="21/8">
        <div className="kbb-cover-watermark">
          <span>{t.label}</span>
          <span>Repetive · Kennisbank</span>
        </div>
      </KbCover>
    </section>
  );
}

// ---- Sticky TOC ------------------------------------------------------------
function BlogToc({ toc, active }) {
  if (!toc.length) return <aside className="kbb-toc" aria-hidden="true" />;
  // Intercept clicks: with <base href="../"> on the blog detail page, bare
  // "#id" links resolve to the parent directory + hash, sending the user to a
  // 404. We handle the scroll manually so anchor navigation works regardless
  // of the base href.
  const onClick = (e, id) => {
    e.preventDefault();
    const el = document.getElementById(id);
    if (!el) return;
    const top = el.getBoundingClientRect().top + window.scrollY - 88;
    window.scrollTo({ top, behavior: 'smooth' });
    // Update the URL hash without triggering navigation. Use an absolute
    // pathname+hash because <base href="../"> would otherwise rewrite a bare
    // "#id" to <parent>/#id and send the user off the page.
    if (history.replaceState) {
      history.replaceState(null, '', location.pathname + location.search + '#' + id);
    }
  };
  return (
    <aside className="kbb-toc" aria-label="Inhoudsopgave">
      <div className="kbb-toc-label">In dit artikel</div>
      <ol>
        {toc.map(item => (
          <li key={item.id}>
            <a
              href={`#${item.id}`}
              className={active === item.id ? 'active' : ''}
              onClick={e => onClick(e, item.id)}
            >
              {item.text}
            </a>
          </li>
        ))}
      </ol>
    </aside>
  );
}

// ---- Share bar -------------------------------------------------------------
function BlogShare({ blog }) {
  const [copied, setCopied] = React.useState(false);
  const url = typeof window !== 'undefined' ? window.location.href : '';
  const copy = (e) => {
    e.preventDefault();
    const done = () => { setCopied(true); setTimeout(() => setCopied(false), 1800); };
    if (navigator.clipboard?.writeText) {
      navigator.clipboard.writeText(url).then(done).catch(done);
    } else { done(); }
  };
  const linkedin = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`;
  const mailto = `mailto:?subject=${encodeURIComponent(blog.title)}&body=${encodeURIComponent(`Misschien interessant: ${url}`)}`;
  return (
    <div className="kbb-share">
      <span className="kbb-share-label">Delen</span>
      <a className="kbb-share-btn" href={linkedin} target="_blank" rel="noopener">LinkedIn</a>
      <a className="kbb-share-btn" href={mailto}>E-mail</a>
      <button className={copied ? 'copied' : ''} onClick={copy} type="button">
        {copied ? '✓ link gekopieerd' : 'Kopieer link'}
      </button>
    </div>
  );
}

// ---- FAQ accordion ---------------------------------------------------------
function BlogFaq({ items }) {
  const [open, setOpen] = React.useState(0);
  if (!items || !items.length) return null;
  return (
    <section className="kbb-faq" id="vragen">
      <h2 className="kbb-faq-title">Veelgestelde vragen</h2>
      {items.map((item, i) => (
        <div className="kbb-faq-item" key={i} data-open={open === i ? 'true' : 'false'}>
          <button
            className="kbb-faq-q"
            type="button"
            aria-expanded={open === i}
            onClick={() => setOpen(open === i ? -1 : i)}
          >
            <span>{item.q}</span>
            <span className="kbb-faq-toggle" aria-hidden="true">+</span>
          </button>
          <div className="kbb-faq-a">
            <div className="kbb-faq-a-inner">{item.a}</div>
          </div>
        </div>
      ))}
    </section>
  );
}

// ---- Shell -----------------------------------------------------------------
function BlogShell({ blog, body, faq }) {
  const scopeRef = React.useRef(null);
  const toc = useExtractedToc(scopeRef);
  const active = useActiveHeading(toc, scopeRef);
  // Inject SEO + JSON-LD as soon as the shell mounts so search engines
  // and social unfurlers that execute JS see them in <head>.
  React.useEffect(() => {
    if (typeof window.kbInjectBlogSeo === 'function') window.kbInjectBlogSeo(blog);
    if (typeof window.kbInjectBlogFaqSeo === 'function') window.kbInjectBlogFaqSeo(faq);
  }, [blog, faq]);
  return (
    <>
      <BlogProgress />
      <HmTopribbon activeLabel="Kennisbank" homePrefix="/" />

      <div className="kbb-shell">
        <a href="Kennisbank.html" className="kbb-back">
          <span aria-hidden="true">←</span> Terug naar Kennisbank
        </a>
      </div>

      <BlogHero blog={blog} />
      <BlogCover blog={blog} />

      <div className="kbb-body">
        <BlogToc toc={toc} active={active} />
        <article className="kbb-prose" ref={scopeRef}>
          {body}
          <BlogShare blog={blog} />
          <BlogFaq items={faq} />
        </article>
      </div>

      <HmContact
        eyebrow="Hier verder over praten?"
        title={<>Geen verkooppraatje. <br/><span className="grad">Gewoon een gesprek.</span></>}
        subtitle="Een half uur met iemand uit ons team. We luisteren naar waar de meeste tijd verloren gaat, en delen wat we bij vergelijkbare bedrijven hebben gezien."
      />
      <HmFooter />
    </>
  );
}

Object.assign(window, { BlogShell, BlogProgress, BlogHero, BlogCover, BlogToc, BlogShare, BlogFaq, kbSlug });
