/* Pagina Veelgestelde vragen. Inhoud staat in faq-content.js. */

function bbFaqNorm(s) {
  return String(s).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}

/* Zoekterm oplichten en de link in het antwoord over privacybeleid plaatsen */
function bbFaqText(text, q, link, go, keyBase) {
  let nodes = [text];
  if (link) {
    const i = text.indexOf(link.word);
    if (i > -1) {
      nodes = [
        text.slice(0, i),
        <a key={keyBase + "-l"} href={window.bbHref(link.route)} onClick={(e) => { e.preventDefault(); go(link.route); }}>{link.word}</a>,
        text.slice(i + link.word.length)
      ];
    }
  }
  const needle = bbFaqNorm(q).trim();
  if (!needle) return nodes;
  const out = [];
  nodes.forEach((n, ni) => {
    if (typeof n !== "string") { out.push(n); return; }
    const hay = bbFaqNorm(n);
    let pos = 0, hit = hay.indexOf(needle, 0), guard = 0;
    while (hit > -1 && guard < 40) {
      out.push(n.slice(pos, hit));
      out.push(<mark key={keyBase + "-m" + ni + "-" + hit} className="bb-faq__hit">{n.slice(hit, hit + needle.length)}</mark>);
      pos = hit + needle.length;
      hit = hay.indexOf(needle, pos);
      guard++;
    }
    out.push(n.slice(pos));
  });
  return out;
}

function FaqItem({ item, open, onToggle, query, go, index }) {
  const bodyRef = useRef(null);
  const [h, setH] = useState(0);
  useEffect(() => {
    const measure = () => { if (bodyRef.current) setH(bodyRef.current.scrollHeight); };
    measure();
    // Het antwoord wordt ook opnieuw gemeten zodra de eigen lettertypen geladen zijn
    // en zodra de tekst zelf van hoogte verandert, bijvoorbeeld omdat een regel bij
    // een smal scherm gaat afbreken. Zonder die tweede meting wordt de hoogte bepaald
    // met het uitwijklettertype en valt de laatste regel buiten het kader.
    let ro = null;
    if (window.ResizeObserver && bodyRef.current) {
      ro = new ResizeObserver(measure);
      ro.observe(bodyRef.current);
    }
    if (document.fonts && document.fonts.ready) document.fonts.ready.then(measure);
    window.addEventListener("resize", measure);
    return () => {
      window.removeEventListener("resize", measure);
      if (ro) ro.disconnect();
    };
  }, [item.id, query]);

  return (
    <li className={"bb-faq__item" + (open ? " is-open" : "")}>
      <h3 className="bb-faq__qwrap">
        <button
          type="button"
          className="bb-faq__q"
          id={"faq-q-" + item.id}
          aria-expanded={open}
          aria-controls={"faq-a-" + item.id}
          onClick={onToggle}
        >
          <span className="bb-faq__num" aria-hidden="true">{index}</span>
          <span className="bb-faq__qtext">{bbFaqText(item.q, query, null, go, "q-" + item.id)}</span>
          <span className="bb-faq__sign" aria-hidden="true"><i></i><i></i></span>
        </button>
      </h3>
      <div
        className="bb-faq__aouter"
        id={"faq-a-" + item.id}
        role="region"
        aria-labelledby={"faq-q-" + item.id}
        style={{ height: open ? h : 0 }}
      >
        <div className="bb-faq__a" ref={bodyRef}>
          <p>{bbFaqText(item.a, query, item.link, go, "a-" + item.id)}</p>
        </div>
      </div>
    </li>
  );
}

function FaqPage({ go }) {
  const data = window.BB_FAQ;
  const bel = bbBellink();
  const [query, setQuery] = useState("");
  const [cat, setCat] = useState("alle");
  const vragenRef = useRef(null);

  /* Kies je een filter, dan brengen wij je naar het begin van de vragen die daarbij
     horen. De pagina springt naar de bovenkant van de eerste vraag, net onder de
     balken die bovenaan blijven plakken. Wij meten die balken op het moment zelf,
     want hun hoogte verschilt per schermbreedte, en op een telefoon plakt de
     filterbalk niet en telt hij dus niet mee. */
  const naarVragen = () => {
    requestAnimationFrame(() => {
      /* Wij rekenen vanaf de eerste vraaggroep zelf, niet vanaf het blok eromheen:
         dat blok heeft ruimte aan de bovenkant, en dan landt de pagina te hoog.
         Levert het filter geen vragen op, dan is er alleen de melding, en die
         gebruiken wij dan. */
      const el = document.querySelector(".bb-faq .bb-faq__group")
        || document.querySelector(".bb-faq__empty")
        || vragenRef.current;
      if (!el) return;
      let marge = 15;
      [".bb-header", ".bb-faq__filter"].forEach((sel) => {
        const b = document.querySelector(sel);
        if (b && getComputedStyle(b).position === "sticky") marge += b.getBoundingClientRect().height;
      });
      const top = Math.max(0, el.getBoundingClientRect().top + window.pageYOffset - marge);
      window.scrollTo(0, top);
      /* Nog een correctie erna. De filterbalk gaat pas plakken zodra hij de menubalk
         raakt, en op een landing die daar net onder blijft zag je een streepje van
         de lichtgroene achtergrond tussen de twee balken. Staat er nog ruimte
         tussen, dan schuiven wij precies dat stukje verder. */
      requestAnimationFrame(() => {
        const balk = document.querySelector(".bb-header");
        const filter = document.querySelector(".bb-faq__filter");
        if (!balk || !filter || getComputedStyle(filter).position !== "sticky") return;
        const gat = filter.getBoundingClientRect().top - balk.getBoundingClientRect().bottom;
        if (gat > 0.5) window.scrollTo(0, window.pageYOffset + gat + 1);
      });
    });
  };

  const kiesCat = (id) => { setCat(id); naarVragen(); };
  const [open, setOpen] = useState(() => ({ [data.groups[0].items[0].id]: true }));
  const searchRef = useRef(null);

  const needle = bbFaqNorm(query).trim();
  const groups = data.groups
    .filter((g) => cat === "alle" || cat === g.id)
    .map((g) => Object.assign({}, g, {
      items: needle
        ? g.items.filter((it) => bbFaqNorm(it.q + " " + it.a + " " + (it.keywords || "")).includes(needle))
        : g.items
    }))
    .filter((g) => g.items.length);

  const shown = groups.reduce((n, g) => n + g.items.length, 0);
  const total = data.groups.reduce((n, g) => n + g.items.length, 0);
  const visibleIds = groups.reduce((a, g) => a.concat(g.items.map((i) => i.id)), []);
  const allOpen = visibleIds.length > 0 && visibleIds.every((id) => open[id]);

  const toggleAll = () => {
    const next = Object.assign({}, open);
    visibleIds.forEach((id) => { next[id] = !allOpen; });
    setOpen(next);
  };

  useEffect(() => {
    if (!needle) return;
    setOpen((p) => {
      const next = Object.assign({}, p);
      visibleIds.forEach((id) => { next[id] = true; });
      return next;
    });
  }, [needle, cat]);

  const chips = [{ id: "alle", label: "Alle vragen", n: total }].concat(
    data.groups.map((g) => ({ id: g.id, label: g.short, n: g.items.length, icon: g.icon }))
  );

  return (
    <main id="main" tabIndex="-1" className="bb-faqpage" data-screen-label="FAQ">
      <header className="bb-faq__head">
        <Container>
          <div className="bb-faq__headgrid">
            <div>
              <FadeUp><h1>{data.title}</h1></FadeUp>
              <FadeUp delay={90}><p className="bb-faq__lead">{data.lead}</p></FadeUp>
              <FadeUp delay={160}>
                <div className="bb-faq__search">
                  <span className="bb-faq__searchicon" aria-hidden="true"><Icon name="search" size={20} /></span>
                  <label className="bb-faq__srlabel" htmlFor="faq-search">Zoek in de vragen</label>
                  <input
                    id="faq-search"
                    ref={searchRef}
                    type="search"
                    value={query}
                    onChange={(e) => setQuery(e.target.value)}
                    placeholder="Zoek op een woord, bijvoorbeeld veilig of gratis"
                    autoComplete="off"
                  />
                  {query && (
                    <button type="button" className="bb-faq__clear" onClick={() => { setQuery(""); if (searchRef.current) searchRef.current.focus(); }} aria-label="Zoekterm wissen">
                      <Icon name="close" size={18} />
                    </button>
                  )}
                </div>
              </FadeUp>
            </div>
            <FadeUp delay={220} className="bb-faq__headfig">
              <img src="assets/photos/steppende-kinderen.jpg" alt="Twee jongeren steppen samen door de buurt." />
            </FadeUp>
          </div>
        </Container>
      </header>

      {/* De filterbalk zit in een eigen laag over de volle breedte, zodat hij bij het
          scrollen onder de menubalk kan blijven plakken. Zie .bb-faq__filter. */}
      <div className="bb-faq__filter">
        <Container>
          <div className="bb-faq__toolbar">
            <div className="bb-faq__chips" role="group" aria-label="Filter op doelgroep">
              {chips.map((c) => (
                <button
                  key={c.id}
                  type="button"
                  className={"bb-faq__chip" + (cat === c.id ? " is-on" : "")}
                  aria-pressed={cat === c.id}
                  onClick={() => kiesCat(c.id)}
                >
                  {c.icon && <Icon name={c.icon} size={17} />}
                  {c.label}
                  <span className="bb-faq__chipn">{c.n}</span>
                </button>
              ))}
            </div>
            <div className="bb-faq__tools">
              <p className="bb-faq__count" aria-live="polite">
                {needle
                  ? shown === 0 ? "Geen vragen gevonden" : shown + (shown === 1 ? " vraag gevonden" : " vragen gevonden")
                  : shown + " van " + total + " vragen"}
              </p>
              {visibleIds.length > 1 && (
                <button type="button" className="bb-faq__toggleall" onClick={toggleAll}>
                  {allOpen ? "Alles sluiten" : "Alles openen"}
                </button>
              )}
            </div>
          </div>
        </Container>
      </div>

      <div className="bb-faq" ref={vragenRef}>
        <Container>
          {shown === 0 ? (
            <div className="bb-faq__empty">
              <p className="bb-faq__emptytitle">Hier hebben we nog geen antwoord op staan.</p>
              <p>Stel je vraag rechtstreeks aan Sabrina, je krijgt altijd antwoord van een mens.</p>
              <div className="bb-faq__emptyrow">
                <Button variant="primary" onClick={() => go("contact")}>Stel je vraag <Icon name="arrowRight" /></Button>
                <Button variant="secondary" onClick={() => { setQuery(""); setCat("alle"); }}>Alle vragen tonen</Button>
              </div>
            </div>
          ) : (
            groups.map((g, gi) => (
              <React.Fragment key={g.id}>
                {gi > 0 && (
                  <div className="bb-faq__band" aria-hidden="true">
                    <img src="assets/photos/voetballend.jpg" alt="" />
                  </div>
                )}
                <section className="bb-faq__group" aria-labelledby={"faq-g-" + g.id}>
                  <div className="bb-faq__grouphead">
                    <span className="bb-faq__groupicon" aria-hidden="true"><Icon name={g.icon} size={20} /></span>
                    <h2 id={"faq-g-" + g.id}>{g.title}</h2>
                    <span className="bb-faq__groupn">{g.items.length}</span>
                  </div>
                  <ul className="bb-faq__list">
                    {g.items.map((it, i) => (
                      <FaqItem
                        key={it.id}
                        item={it}
                        index={i + 1}
                        open={!!open[it.id]}
                        onToggle={() => setOpen((p) => Object.assign({}, p, { [it.id]: !p[it.id] }))}
                        query={query}
                        go={go}
                      />
                    ))}
                  </ul>
                </section>
              </React.Fragment>
            ))
          )}

          <section className="bb-faq__cta" aria-labelledby="faq-cta">
            <div className="bb-faq__ctatext">
              <h2 id="faq-cta">{data.cta}</h2>
              <p>Sabrina Costermans, initiatiefnemer Buiten Buddies</p>
            </div>
            <div className="bb-faq__ctarow">
              <Button variant="yellow" onClick={() => go("contact")}>Stuur een bericht <Icon name="arrowRight" /></Button>
              <a className="bb-faq__ctalink" href="mailto:info@buitenbuddies.org"><Icon name="mail" size={18} /> info@buitenbuddies.org</a>
              <a className="bb-faq__ctalink" href={bel.href} target={bel.extern ? "_blank" : undefined} rel={bel.extern ? "noopener noreferrer" : undefined} aria-label={bel.label}><Icon name="phone" size={18} /> 06 18 39 15 32</a>
            </div>
          </section>

          <nav className="bb-faq__next" aria-label="Verder lezen">
            <a href={window.bbHref("hoe-werkt-het")} onClick={(e) => { e.preventDefault(); go("hoe-werkt-het"); }}>
              <span className="t">Hoe werkt het?</span>
              <span className="d">Van aanmelding tot buiten afspreken in zeven stappen</span>
              <span className="a" aria-hidden="true"><Icon name="arrowRight" size={18} /></span>
            </a>
            <a href={window.bbHref("meedoen")} onClick={(e) => { e.preventDefault(); go("meedoen"); }}>
              <span className="t">Meedoen</span>
              <span className="d">Meld je kind aan of word groepsbeheerder in jouw buurt</span>
              <span className="a" aria-hidden="true"><Icon name="arrowRight" size={18} /></span>
            </a>
            <a href={window.bbHref("privacybeleid")} onClick={(e) => { e.preventDefault(); go("privacybeleid"); }}>
              <span className="t">Privacybeleid</span>
              <span className="d">Welke gegevens we verwerken en hoe lang we ze bewaren</span>
              <span className="a" aria-hidden="true"><Icon name="arrowRight" size={18} /></span>
            </a>
          </nav>
        </Container>
      </div>
    </main>
  );
}

Object.assign(window, { FaqPage, FaqItem });
