/* blog-views.jsx — the Writing (blog) views, styled after Medium's reading experience.
   MULTILINGUAL: each post has post.langs = { ms|en|ja: {title, excerpt, html, readingMinutes} }.
   The site language (window.SITE_LANG) picks which text to show; falls back to the
   original language (post.origLang) when a translation isn't available.
   Depends on Reveal (views.jsx), i18n.js (t / topicName / useSiteLang), Comments. */

const { useEffect: useBE, useMemo: useBMemo, useState: useBState, useRef: useBRef } = React;

const AUTHOR = {
  name: "Muhamad Firdaus Ali",
  avatar: "assets/avatar.png?v=2",
};

const LANG_LABEL = { ms: "Bahasa Melayu", en: "English", ja: "日本語" };
const LANG_SHORT = { ms: "MS", en: "EN", ja: "JA" };

/* pick the best language block for a post given the desired site language */
function pick(post, lang) {
  const langs = post.langs || {};
  const used = langs[lang] ? lang : (langs[post.origLang] ? post.origLang : Object.keys(langs)[0]);
  const block = langs[used] || {};
  return { ...block, usedLang: used, available: Object.keys(langs) };
}

/* localized month-name date */
function fmtDate(iso, lang) {
  const L = lang || window.SITE_LANG || "en";
  const [y, m, d] = (iso || "").split("-").map(Number);
  if (!y) return iso || "";
  if (L === "ja") return `${y}年${m}月${d}日`;
  const monthsEN = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  const monthsMS = ["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"];
  const mo = (L === "ms" ? monthsMS : monthsEN)[(m || 1) - 1];
  return L === "ms" ? `${d} ${mo} ${y}` : `${mo} ${d}, ${y}`;
}

/* ---------- Writing index: Medium-style feed of story cards ---------- */
function StoryCard({ post, onOpen, lang }) {
  const [thumbReady, setThumbReady] = useBState(false);
  const [thumbFailed, setThumbFailed] = useBState(false);
  const v = pick(post, lang);
  useBE(() => {
    if (!post.cover) return;
    setThumbReady(false); setThumbFailed(false);
    const img = new Image();
    img.onload = () => setThumbReady(true);
    img.onerror = () => setThumbFailed(true);  // don't shimmer forever on a broken image
    img.src = post.cover;
  }, [post.cover]);

  const cat = post.categories && post.categories[0];
  return (
    <article className="story-card" onClick={() => onOpen(post.slug)}>
      <div className="story-main">
        <div className="story-byline">
          <img className="story-avatar" src={AUTHOR.avatar} alt="" loading="lazy" />
          <span className="story-author">{AUTHOR.name}</span>
        </div>
        <h2 className="story-title">{v.title}</h2>
        {v.excerpt && <p className="story-dek">{v.excerpt}</p>}
        <div className="story-foot">
          <span className="story-date">{fmtDate(post.date, lang)}</span>
          <span className="story-dot">·</span>
          <span className="story-read">{v.readingMinutes} {window.t("min_read", lang)}</span>
          {cat && cat !== "Uncategorized" && (
            <span className="story-cat">{window.topicName(cat, lang)}</span>
          )}
          {v.usedLang !== lang && <span className="story-lang">{LANG_SHORT[v.usedLang]}</span>}
        </div>
      </div>
      {post.cover && !thumbFailed && (
        <div className={`story-thumb ${thumbReady ? "is-ready" : "is-loading"}`}
          style={thumbReady ? { backgroundImage: `url("${post.cover}")` } : undefined}
          aria-hidden="true" />
      )}
    </article>
  );
}

function BlogView({ onOpen, onTopic }) {
  const lang = useSiteLang();
  const posts = (window.BLOG && window.BLOG.posts) || [];

  const recent = useBMemo(() => posts.slice(0, 5), [posts]);
  const topics = useBMemo(() => {
    const counts = {};
    posts.forEach((p) => (p.categories || []).forEach((c) => {
      if (c && c !== "Uncategorized") counts[c] = (counts[c] || 0) + 1;
    }));
    return Object.entries(counts)
      .map(([name, count]) => ({ name, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 8);
  }, [posts]);

  return (
    <div className="view blog">
      <Reveal as="header" className="blog-head">
        <h1 className="blog-title">{window.t("writing_title", lang)}</h1>
        <p className="blog-sub">{window.t("writing_sub", lang)}</p>
      </Reveal>

      <div className="blog-layout">
        <div className="blog-main">
          <div className="blog-list">
            {posts.map((p, i) => (
              <Reveal key={p.slug} delay={Math.min(i, 5) * 40}>
                <StoryCard post={p} onOpen={onOpen} lang={lang} />
              </Reveal>
            ))}
          </div>
          <Reveal as="footer" className="blog-foot"><span className="foot-mark">了</span></Reveal>
        </div>

        <BlogSidebar posts={posts} recent={recent} topics={topics}
          onOpen={onOpen} onTopic={onTopic} lang={lang} />
      </div>
    </div>
  );
}

/* right-hand sidebar: about blurb, topics, recent posts */
function BlogSidebar({ posts, recent, topics, onOpen, onTopic, activeTopic, lang }) {
  const profile = (window.DIARY && window.DIARY.profile) || {};
  return (
    <Reveal as="aside" className="blog-aside" delay={80}>
      <div className="aside-card aside-about">
        <img className="aside-avatar" src={AUTHOR.avatar} alt={AUTHOR.name} />
        <p className="aside-name">{AUTHOR.name}</p>
        <p className="aside-bio">
          {(profile.line && (profile.line[lang] || profile.line.en)) || ""}
        </p>
        <a className="aside-link" href={`https://${profile.site || "firdausali.com"}`}
           target="_blank" rel="noopener noreferrer">firdausali.com ↗</a>
      </div>

      {topics.length > 0 && (
        <div className="aside-card">
          <h3 className="aside-heading">{window.t("topics", lang)}</h3>
          <ul className="aside-topics">
            {topics.map((tp) => (
              <li key={tp.name}>
                <button
                  className={`aside-topic ${activeTopic === tp.name ? "is-active" : ""}`}
                  onClick={() => onTopic && onTopic(tp.name)}>
                  <span className="aside-topic-name">{window.topicName(tp.name, lang)}</span>
                  <span className="aside-topic-count">{tp.count}</span>
                </button>
              </li>
            ))}
          </ul>
        </div>
      )}

      {recent.length > 0 && (
        <div className="aside-card">
          <h3 className="aside-heading">{window.t("recent", lang)}</h3>
          <ul className="aside-recent">
            {recent.map((p) => (
              <li key={p.slug} className="aside-recent-item" onClick={() => onOpen(p.slug)}>
                <span className="aside-recent-title">{pick(p, lang).title}</span>
                <span className="aside-recent-date">{fmtDate(p.date, lang)}</span>
              </li>
            ))}
          </ul>
        </div>
      )}
    </Reveal>
  );
}

/* ---------- a single category ---------- */
function TopicView({ topic, onOpen, onTopic, onBack }) {
  const lang = useSiteLang();
  const allPosts = (window.BLOG && window.BLOG.posts) || [];
  useBE(() => {
    window.scrollTo({ top: 0, behavior: "auto" });
    window.track && window.track("topic_opened", { topic: topic, language: lang });
  }, [topic]);

  const inTopic = useBMemo(
    () => allPosts.filter((p) => (p.categories || []).includes(topic)),
    [topic, allPosts]
  );
  const recent = useBMemo(() => allPosts.slice(0, 5), [allPosts]);
  const topics = useBMemo(() => {
    const counts = {};
    allPosts.forEach((p) => (p.categories || []).forEach((c) => {
      if (c && c !== "Uncategorized") counts[c] = (counts[c] || 0) + 1;
    }));
    return Object.entries(counts).map(([name, count]) => ({ name, count }))
      .sort((a, b) => b.count - a.count).slice(0, 8);
  }, [allPosts]);

  const n = inTopic.length;
  const sub = lang === "ja"
    ? `「${window.topicName(topic, lang)}」の記事 ${n} 本。`
    : lang === "ms"
      ? `${n} tulisan di bawah topik ${window.topicName(topic, lang)}.`
      : `${n} ${n === 1 ? "piece" : "pieces"} filed under ${window.topicName(topic, lang)}.`;

  return (
    <div className="view blog">
      <Reveal as="header" className="blog-head">
        <button className="back-link topic-back" onClick={onBack}>{window.t("back_writing", lang)}</button>
        <p className="topic-kicker">{window.t("topic_kicker", lang)}</p>
        <h1 className="blog-title">{window.topicName(topic, lang)}</h1>
        <p className="blog-sub">{sub}</p>
      </Reveal>

      <div className="blog-layout">
        <div className="blog-main">
          {n > 0 ? (
            <div className="blog-list">
              {inTopic.map((p, i) => (
                <Reveal key={p.slug} delay={Math.min(i, 4) * 45}>
                  <StoryCard post={p} onOpen={onOpen} lang={lang} />
                </Reveal>
              ))}
            </div>
          ) : (
            <p className="blog-sub" style={{ paddingBlock: "2rem" }}>
              <button className="back-link" onClick={onBack}>{window.t("back_writing", lang)}</button>
            </p>
          )}
          <Reveal as="footer" className="blog-foot"><span className="foot-mark">了</span></Reveal>
        </div>

        <BlogSidebar posts={allPosts} recent={recent} topics={topics}
          onOpen={onOpen} onTopic={onTopic} activeTopic={topic} lang={lang} />
      </div>
    </div>
  );
}

/* reading-progress bar (also emits article_progress at 25/50/75/100%) */
function ReadingProgress({ targetRef, slug }) {
  const [pct, setPct] = useBState(0);
  const seen = useBRef({});
  useBE(() => { seen.current = {}; }, [slug]);
  useBE(() => {
    const onScroll = () => {
      const el = targetRef.current;
      if (!el) return;
      const total = el.scrollHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-el.getBoundingClientRect().top, 0), Math.max(total, 1));
      const p = total > 0 ? (scrolled / total) * 100 : 0;
      setPct(p);
      [25, 50, 75, 100].forEach((d) => {
        if (p >= d && !seen.current[d]) {
          seen.current[d] = 1;
          window.track && window.track("article_progress", { slug: slug, depth: d });
        }
      });
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [targetRef, slug]);
  return (
    <div className="read-progress" aria-hidden="true">
      <div className="read-progress-bar" style={{ transform: `scaleX(${pct / 100})` }} />
    </div>
  );
}

/* native share (mobile share sheet) with a copy-link fallback */
function ShareButton({ title }) {
  const [copied, setCopied] = useBState(false);
  const share = () => {
    const url = location.href;
    if (navigator.share) {
      navigator.share({ title, url }).catch(() => {});
    } else if (navigator.clipboard) {
      navigator.clipboard.writeText(url).then(() => {
        setCopied(true); setTimeout(() => setCopied(false), 1600);
      });
    }
  };
  return (
    <button className="post-lang-btn post-share" onClick={share}>
      {copied ? "Copied ↗" : "Share ↗"}
    </button>
  );
}

/* ---------- a single article, Medium reading view, with per-article language picker ---------- */
function PostView({ post, onBack, neighbors, onOpen }) {
  const siteLang = useSiteLang();
  const artRef = useBRef(null);
  // reader can override the language for THIS article; defaults to the site language
  const [readLang, setReadLang] = useBState(null);
  useBE(() => {
    setReadLang(null); window.scrollTo({ top: 0, behavior: "auto" });
    if (post) window.track && window.track("article_opened", {
      slug: post.slug, title: pick(post, siteLang).title,
      category: (post.categories || [])[0] || null, language: siteLang,
      is_translation: post.origLang !== siteLang,
    });
  }, [post && post.slug]);
  useBE(() => { setReadLang(null); }, [siteLang]);
  if (!post) return null;

  const lang = readLang || siteLang;
  const v = pick(post, lang);
  const cat = post.categories && post.categories[0];
  const available = v.available || Object.keys(post.langs || {});

  return (
    <div className="view post-page" ref={artRef}>
      <ReadingProgress targetRef={artRef} slug={post.slug} />

      <Reveal as="nav" className="entry-back">
        <button className="back-link" onClick={onBack}>{window.t("back_writing", siteLang)}</button>
      </Reveal>

      <Reveal as="header" className="post-page-head" delay={60}>
        {cat && cat !== "Uncategorized" &&
          <p className="post-kicker">{window.topicName(cat, lang)}</p>}
        <h1 className="post-page-title">{v.title}</h1>
        {v.excerpt && <p className="post-standfirst">{v.excerpt}</p>}

        <div className="post-byline">
          <img className="byline-avatar" src={AUTHOR.avatar} alt="" />
          <div className="byline-detail">
            <span className="byline-name">{AUTHOR.name}</span>
            <span className="byline-meta">
              {fmtDate(post.date, lang)} · {v.readingMinutes} {window.t("min_read", lang)}
            </span>
          </div>
        </div>

        {available.length > 1 && (
          <div className="post-langs" role="group" aria-label="Read in another language">
            {["ms", "en", "ja"].filter((c) => available.includes(c)).map((c) => (
              <button key={c}
                className={`post-lang-btn ${v.usedLang === c ? "is-active" : ""}`}
                onClick={() => setReadLang(c)}>
                {LANG_LABEL[c]}
              </button>
            ))}
            <ShareButton title={v.title} />
          </div>
        )}
        {available.length <= 1 && <div className="post-langs"><ShareButton title={v.title} /></div>}
      </Reveal>

      {post.cover && (
        <Reveal as="figure" className="post-cover" delay={90}>
          <img src={post.cover} alt="" loading="lazy" />
        </Reveal>
      )}

      <Reveal as="div" className="post-page-body" delay={110}>
        <div className="post-prose" dangerouslySetInnerHTML={{ __html: v.html }} />
      </Reveal>

      {(neighbors.prev || neighbors.next) && (
        <Reveal as="nav" className="entry-nav">
          {neighbors.next && (
            <button className="entry-nav-link" onClick={() => onOpen(neighbors.next.slug)}>
              <span className="enl-label">{siteLang === "ja" ? "新しい記事" : siteLang === "ms" ? "Lebih baharu" : "Newer"}</span>
              <span className="enl-title">{pick(neighbors.next, lang).title}</span>
            </button>
          )}
          {neighbors.prev && (
            <button className="entry-nav-link align-end" onClick={() => onOpen(neighbors.prev.slug)}>
              <span className="enl-label">{siteLang === "ja" ? "古い記事" : siteLang === "ms" ? "Lebih lama" : "Older"}</span>
              <span className="enl-title">{pick(neighbors.prev, lang).title}</span>
            </button>
          )}
        </Reveal>
      )}

      {typeof Comments !== "undefined" &&
        (!window.COMMENTS_CONFIG || window.COMMENTS_CONFIG.enabled !== false) && (
        <Reveal as="section" className="comments-wrap" delay={60}>
          <Comments slug={post.slug} />
        </Reveal>
      )}
    </div>
  );
}

Object.assign(window, { BlogView, PostView, TopicView, fmtDate });
