// Home, Category, Article pages.
const { useEffect: useEffectP, useState: useStateP, useMemo: useMemoP } = React;

// Hook: re-render when the content store changes (article add/edit/delete).
function useContentVersion() {
  const [v, setV] = useStateP(0);
  useEffectP(() => {
    const on = () => setV(x => x + 1);
    window.addEventListener("kol:content", on);
    return () => window.removeEventListener("kol:content", on);
  }, []);
  return v;
}

function Home() {
  const latest = useMemoP(() => {
    const all = [];
    window.CATEGORIES.forEach((c) => {
      c.articles.forEach((a) => all.push({ cat: c, art: a }));
    });
    return all.sort((a, b) => (b.art.updated || "").localeCompare(a.art.updated || "")).slice(0, 6);
  }, []);

  const [latestSession, setLatestSession] = useStateP(
    () => (window.KOL_SESSIONS || window.SESSION_NOTES_DEFAULT || [])[0] || null
  );
  useEffectP(() => {
    const on = (e) => setLatestSession((e.detail || [])[0] || null);
    window.addEventListener("kol:sessions", on);
    return () => window.removeEventListener("kol:sessions", on);
  }, []);

  return (
    <>
      <section className="hero">
        <div>
          <div className="hero-eyebrow">{window.SITE.group}</div>
          <h1>Our <em>open-source</em><br />knowledge repo.</h1>
          <p className="hero-lede">Tutorials, how-to&rsquo;s and catch-up notes for everything covered in our sessions.</p>
          <div className="hero-meta">
            <span><b>{window.CATEGORIES.length}</b> CATEGORIES</span>
            <span><b>{(window.TOOLS || []).length}</b> TOOLS</span>
            <span><b>{(window.SESSION_NOTES_DEFAULT || []).length}+</b> SESSIONS</span>
          </div>
        </div>
        <div className="hero-art">
          <img
            src="https://images.unsplash.com/photo-1514557718210-26e452f8fab0?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTJ8fGxvbmRvbiUyMHNreWxpbmV8ZW58MHx8MHx8fDA%3D"
            alt="London skyline"
            style={{ width: "100%", height: "100%", objectFit: "cover", objectPosition: "center 60%" }}
          />
        </div>
      </section>

      <div className="section-head">
        <h2>Latest entries</h2>
        <a href="#/tools" className="more">All tools →</a>
      </div>
      <div className="cards">
        {latest.map(({ cat, art }) =>
        <a key={art.id} href={`#/c/${cat.id}/${art.id}`} className="card">
            <div className="card-eyebrow">{cat.label} · {cat.title}</div>
            <h3>{art.title}</h3>
            <p>{art.lede}</p>
            <div className="card-foot">
              <span>UPDATED {fmtDate(art.updated)}</span>
              <span className="dot"></span>
              <span>{art.readMin} MIN</span>
            </div>
          </a>
        )}
      </div>

      <div className="section-head">
        <h2>Browse by category</h2>
        <a href="#/about" className="more">About →</a>
      </div>
      <div className="cards">
        {window.CATEGORIES.map((c) =>
        <a key={c.id} href={`#/c/${c.id}`} className="card">
            <div className="card-eyebrow">{c.label}</div>
            <h3>{c.title}</h3>
            <p>{c.summary}</p>
            <div className="card-foot">
              <span>{c.articles.length} {c.articles.length === 1 ? "ENTRY" : "ENTRIES"}</span>
            </div>
          </a>
        )}
      </div>

      {latestSession ?
      <>
          <div className="section-head">
            <h2>From the last session</h2>
            <a href="#/sessions" className="more">All sessions →</a>
          </div>
          <a href="#/sessions" className="card" style={{ display: "block", border: "1px solid var(--rule)", borderRadius: "var(--radius)", padding: "22px 24px" }}>
            <div className="card-eyebrow">SESSION {String(latestSession.n).padStart(2, "0")} · {fmtDate(latestSession.date)}</div>
            <h3 style={{ fontSize: 20, margin: "6px 0 6px" }}>{latestSession.title}</h3>
            <p style={{ fontSize: 14.5 }}>{latestSession.body.slice(0, 240)}{latestSession.body.length > 240 ? "…" : ""}</p>
          </a>
        </> :
      null}
    </>);

}

function Category({ cat }) {
  useContentVersion();
  const [editing, setEditing] = useStateP(false);
  return (
    <>
      <div className="crumbs">
        <a href="#/">Home</a><span>/</span>
        <span style={{ color: "var(--ink)" }}>{cat.title}</span>
      </div>
      <header style={{ padding: "8px 0 28px", borderBottom: "1px solid var(--rule)", marginBottom: 28 }}>
        <div style={{ fontFamily: "var(--font-mono)", fontSize: 11, letterSpacing: "0.08em", color: "var(--ink-3)", textTransform: "uppercase", marginBottom: 10 }}>
          {cat.label} · {cat.articles.length} {cat.articles.length === 1 ? "ENTRY" : "ENTRIES"}
        </div>
        <h1 style={{ fontFamily: "var(--font-serif)", fontWeight: 400, fontSize: 44, margin: "0 0 10px", letterSpacing: "-0.015em", lineHeight: 1.05 }}>{cat.title}</h1>
        <p style={{ color: "var(--ink-2)", margin: 0, maxWidth: "60ch", fontSize: 16 }}>{cat.summary}</p>
      </header>
      <div style={{ display: "flex", gap: 8, marginBottom: 18 }}>
        {!editing ? <button className="btn" onClick={() => setEditing(true)}>+ New article</button> : null}
      </div>
      {editing ? (
        <window.KOL_PAGES.ArticleEditor
          cat={cat}
          mode="new"
          onClose={() => setEditing(false)}
          onSaved={(art, newCatId) => { setEditing(false); location.hash = `#/c/${newCatId || cat.id}/${art.id}`; }}
        />
      ) : null}
      <div className="cards">
        {[...cat.articles].sort((a, b) => (b.updated || "").localeCompare(a.updated || "")).map((a) =>
        <a key={a.id} href={`#/c/${cat.id}/${a.id}`} className="card">
            <div className="card-eyebrow">{cat.title}</div>
            <h3>{a.title}</h3>
            <p>{a.lede}</p>
            <div className="card-foot">
              <span>UPDATED {fmtDate(a.updated)}</span>
              <span className="dot"></span>
              <span>{a.readMin} MIN</span>
            </div>
          </a>
        )}
      </div>
    </>);

}

function slugify(s) {
  return String(s).toLowerCase().trim().
  replace(/[^\w\s-]/g, "").
  replace(/\s+/g, "-").
  replace(/-+/g, "-");
}

// Render an article body. Supports `markdown:` (preferred) or `body:` (HTML).
// Returns { html, toc } where toc is auto-extracted from h2/h3 headings.
function renderBody(article) {
  let html = "";
  if (article.markdown) {
    if (window.marked) {
      window.marked.setOptions({ gfm: true, breaks: false });
      html = window.marked.parse(article.markdown);
    } else {
      html = "<pre>" + article.markdown.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c]) + "</pre>";
    }
  } else {
    html = article.body || "";
  }
  // Inject ids on h2/h3 and harvest TOC.
  const tmp = document.createElement("div");
  tmp.innerHTML = html;
  const toc = [];
  const used = new Set();
  tmp.querySelectorAll("h2, h3").forEach((h) => {
    let id = h.id || slugify(h.textContent || "");
    let i = 2;
    let final = id;
    while (used.has(final)) {final = id + "-" + i++;}
    used.add(final);
    h.id = final;
    toc.push({ h: h.tagName === "H3" ? 3 : 2, id: final, text: h.textContent || "" });
  });
  return { html: tmp.innerHTML, toc: article.sections?.length ? article.sections : toc };
}

function Article({ cat, article }) {
  useContentVersion();
  const [editing, setEditing] = useStateP(false);
  // Re-pull fresh copy from store after edits.
  const fresh = cat.articles.find(a => a.id === article.id) || article;
  const { html, toc } = useMemoP(() => renderBody(fresh), [fresh.id, fresh.updated, fresh.markdown, fresh.body, fresh.title]);
  const [activeId, setActiveId] = useStateP(toc?.[0]?.id || "");

  async function onDelete() {
    const ok = await window.requestDelete(`article \u201C${fresh.title}\u201D`);
    if (!ok) return;
    window.KOL_STORE.deleteArticle(cat.id, fresh.id);
    location.hash = `#/c/${cat.id}`;
  }

  useEffectP(() => {
    window.scrollTo({ top: 0, behavior: "instant" });
  }, [article.id]);

  useEffectP(() => {
    if (!toc?.length) return;
    const obs = new IntersectionObserver((entries) => {
      const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
      if (visible.length) setActiveId(visible[0].target.id);
    }, { rootMargin: "-80px 0px -70% 0px" });
    toc.forEach((s) => {
      const el = document.getElementById(s.id);
      if (el) obs.observe(el);
    });
    return () => obs.disconnect();
  }, [article.id, toc]);

  return (
    <div className="article-layout">
      <article className="article">
        <div className="crumbs">
          <a href="#/">Home</a><span>/</span>
          <a href={`#/c/${cat.id}`}>{cat.title}</a><span>/</span>
          <span style={{ color: "var(--ink)" }}>{fresh.title}</span>
        </div>
        <h1>{fresh.title}</h1>
        <p className="article-lede">{fresh.lede}</p>
        <div className="article-meta">
          <span><b>UPDATED</b> {fmtDate(fresh.updated)}</span>
          <span><b>READ</b> {fresh.readMin} MIN</span>
          <span><b>CATEGORY</b> {cat.title}</span>
          <span style={{ marginLeft: "auto", display: "flex", gap: 8 }}>
            <button className="btn ghost" style={{ fontSize: 11, padding: "4px 10px" }} onClick={() => setEditing(e => !e)}>{editing ? "Close editor" : "Edit"}</button>
            <button className="btn ghost" style={{ fontSize: 11, padding: "4px 10px", color: "var(--danger)", borderColor: "color-mix(in oklab, var(--danger), transparent 70%)" }} onClick={onDelete}>Delete</button>
          </span>
        </div>
        {editing ? (
          <window.KOL_PAGES.ArticleEditor
            cat={cat}
            article={fresh}
            mode="edit"
            onClose={() => setEditing(false)}
            onSaved={(art, newCatId) => { setEditing(false); if (newCatId && newCatId !== cat.id) location.hash = `#/c/${newCatId}/${art.id}`; }}
          />
        ) : null}
        <div dangerouslySetInnerHTML={{ __html: html }} />
      </article>
      {toc?.length ?
      <aside className="toc">
          <div className="toc-label">On this page</div>
          {toc.map((s) =>
        <a key={s.id} href={`#${s.id}`} className={(s.h === 3 ? "h3 " : "") + (activeId === s.id ? "active" : "")}>{s.text}</a>
        )}
        </aside> :
      null}
    </div>);

}

window.KOL_PAGES.Home = Home;
window.KOL_PAGES.Category = Category;
window.KOL_PAGES.Article = Article;
