// Main app for jingxuan.uk
const { useState, useEffect, useRef } = React;

// `data` defaults to the static bundle in data.js so the homepage renders
// instantly; App's first effect re-fetches /api/site-data (KV-backed, what
// /edit writes to) and reassigns this binding before forcing a re-render.
let data = window.SITE_DATA;
const PubThumb = window.PubThumb;

// ============================================================
// Small UI atoms
// ============================================================

const MonoLabel = ({ children, className = "" }) => (
  <span className={`mono-label ${className}`}>{children}</span>
);

const WechatChip = ({ id }) => {
  const [copied, setCopied] = useState(false);
  const onClick = (e) => {
    e.preventDefault();
    if (navigator.clipboard?.writeText) {
      navigator.clipboard.writeText(id).then(() => {
        setCopied(true);
        setTimeout(() => setCopied(false), 1500);
      });
    }
  };
  return (
    <a href="#" onClick={onClick} className="link-chip" title={`微信 — click to copy ${id}`} style={{ cursor: "pointer" }}>
      <Icon name="wechat" /> {copied ? "Copied!" : `微信 ${id}`}
    </a>
  );
};

const SectionTitle = ({ index, title, hint }) => (
  <header className="section-head">
    <div className="section-head-meta">
      {index && <MonoLabel>§ {index}</MonoLabel>}
      {hint && <MonoLabel className="dim">{hint}</MonoLabel>}
    </div>
    <h2 className="section-title">{title}</h2>
  </header>
);

// Inline icon set — kept minimal, stroke-based
const Icon = ({ name, size = 16 }) => {
  const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" };
  const paths = {
    scholar: <><path d="M3 9l9-5 9 5-9 5-9-5z" /><path d="M7 11v5c2 1.5 8 1.5 10 0v-5" /><path d="M21 9v6" /></>,
    github: <><path d="M9 19c-4 1.5-4-2-6-2.5M15 22v-4a3.4 3.4 0 0 0-1-2.5c3 0 6-2 6-5.5a4 4 0 0 0-1-2.8 3.5 3.5 0 0 0-.1-2.7s-1.1-.4-3.4 1.3a11.7 11.7 0 0 0-6 0C7.1 2.1 6 2.5 6 2.5a3.5 3.5 0 0 0-.1 2.7A4 4 0 0 0 5 8c0 3.5 3 5.5 6 5.5a3.4 3.4 0 0 0-1 2.5v4" /></>,
    x: <><path d="M4 4l7.5 9.5L4.5 20H7l6-6 4.5 6H20l-7.5-9.5L19.5 4H17l-5.5 5.5L7.5 4H4z" fill="currentColor" stroke="none" /></>,
    mail: <><rect x="3" y="5" width="18" height="14" rx="1" /><path d="M3 7l9 6 9-6" /></>,
    wechat: <><text x="2" y="17" fontFamily="serif" fontSize="14" fill="currentColor" stroke="none" fontWeight="700">微</text></>,
    linuxdo: <><circle cx="12" cy="12" r="9" /><path d="M7 12h10M12 7v10" /></>,
    cv: <><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" /><path d="M14 3v5h5" /><path d="M9 13h6M9 17h4" /></>,
    arrow: <><path d="M5 12h14M13 5l7 7-7 7" /></>,
    arrowUp: <><path d="M7 17L17 7M7 7h10v10" /></>,
    back: <><path d="M19 12H5M12 19l-7-7 7-7" /></>,
    clock: <><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></>,
  };
  return <svg {...props}>{paths[name]}</svg>;
};

// ============================================================
// Top navigation
// ============================================================

const TABS = [
  { id: "about", label: "About" },
  { id: "research", label: "Research" },
  { id: "news", label: "News" },
  { id: "publications", label: "Publications" },
  { id: "blog", label: "Article" },
  { id: "service", label: "Service" },
];

const Nav = ({ view, setView }) => {
  const onClick = (id) => (e) => {
    e.preventDefault();
    setView(id);
    window.scrollTo({ top: 0, behavior: "instant" });
  };

  // Hidden admin trigger: triple-click on the brand mark within 600ms → /edit.html
  const brandClicks = useRef(0);
  const brandClickTimer = useRef(null);
  const onBrandMarkClick = (e) => {
    brandClicks.current += 1;
    clearTimeout(brandClickTimer.current);
    if (brandClicks.current >= 3) {
      e.preventDefault();
      e.stopPropagation();
      brandClicks.current = 0;
      window.location.href = "/edit.html";
      return;
    }
    brandClickTimer.current = setTimeout(() => { brandClicks.current = 0; }, 600);
  };

  return (
    <nav className="nav">
      <div className="nav-inner">
        <a href="#" className="nav-brand" onClick={onClick("about")}>
          <span className="brand-mark" onClick={onBrandMarkClick}>jk</span>
          <span className="brand-name">Jingxuan&nbsp;Kang</span>
        </a>
        <ul className="nav-tabs">
          {TABS.map((t) => (
            <li key={t.id}>
              {t.external ? (
                <a href={t.external} target="_blank" rel="noopener">{t.label}</a>
              ) : (
                <a href={`#${t.id}`} onClick={onClick(t.id)} className={view === t.id ? "active" : ""}>
                  {t.label}
                </a>
              )}
            </li>
          ))}
        </ul>
        <a className="nav-cv" href={data.links.cv} target="_blank" rel="noopener">
          <Icon name="cv" size={14} />
          <span>CV</span>
        </a>
      </div>
    </nav>
  );
};

// ============================================================
// Hero / About
// ============================================================

const Hero = () => (
  <section id="about" className="section section--hero" data-screen-label="01 About">
    <div className="hero-grid">
      <div className="hero-text">
        <MonoLabel className="hero-kicker">jingxuan.uk · est. 2026</MonoLabel>
        <h1 className="hero-name">Jingxuan Kang</h1>
        <p className="hero-role">
          PhD Student · <span className="hero-aff">Imperial College London</span>
        </p>
        <p className="hero-dept">{data.profile.department}</p>

        <div className="hero-bio">
          {data.profile.bio.map((p, i) => <p key={i}>{p}</p>)}
        </div>

        <div className="hero-meta">
          <div className="hero-meta-row">
            <MonoLabel className="dim">email</MonoLabel>
            <a href={data.links.email}>{data.profile.email}</a>
          </div>
          <div className="hero-meta-row">
            <MonoLabel className="dim">based in</MonoLabel>
            <span>{data.profile.location}</span>
          </div>
        </div>

        <div className="hero-links">
          <a href={data.links.scholar} className="link-chip" target="_blank" rel="noopener"><Icon name="scholar" /> Scholar</a>
          <a href={data.links.github} className="link-chip" target="_blank" rel="noopener"><Icon name="github" /> GitHub</a>
          <a href={data.links.x} className="link-chip" target="_blank" rel="noopener"><Icon name="x" /> X</a>
          <a href={data.links.email} className="link-chip"><Icon name="mail" /> Email</a>
          {data.links.wechat && <WechatChip id={data.links.wechat} />}
          <a href={data.links.linuxdo} className="link-chip" target="_blank" rel="noopener"><Icon name="linuxdo" /> Linux.do</a>
        </div>
      </div>

      <aside className="hero-card">
        <img className="profile-photo" src="photo.jpg" alt="Jingxuan Kang" />

        <div className="hero-card-divider" />

        <div className="hero-card-block">
          <MonoLabel className="dim">currently</MonoLabel>
          <p>Working on representation, generation, and understanding.</p>
        </div>
      </aside>
    </div>
  </section>
);

// ============================================================
// Research Interests
// ============================================================

const Research = () => (
  <section id="research" className="section" data-screen-label="02 Research">
    <SectionTitle index="01" title="Research interests" hint="what i think about" />
    <div className="interests">
      {data.interests.map((it, i) => (
        <article key={i} className="interest">
          <div className="interest-number">{String(i + 1).padStart(2, "0")}</div>
          <h3 className="interest-title">{it.title}</h3>
          <p className="interest-blurb">{it.blurb}</p>
        </article>
      ))}
    </div>
  </section>
);

// ============================================================
// News
// ============================================================

const Highlights = ({ setView }) => (
  <div className="highlights">
    {data.highlights.map((h, i) => {
      const isExternal = h.href.startsWith("mailto:") || h.href.startsWith("http");
      const onClick = (e) => {
        if (h.href.startsWith("#")) {
          e.preventDefault();
          setView(h.href.slice(1));
          window.scrollTo({ top: 0, behavior: "instant" });
        }
      };
      return (
        <a key={i} className="highlight-card" href={h.href} onClick={onClick}
           target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener" : undefined}>
          <MonoLabel className="highlight-label">{h.label}</MonoLabel>
          <h3 className="highlight-title">{h.title}</h3>
          <p className="highlight-body">{h.body}</p>
          <span className="highlight-cta">{h.cta} <Icon name="arrow" size={12} /></span>
        </a>
      );
    })}
  </div>
);

const NewsAndHighlights = ({ setView }) => (
  <section className="section section--news-home">
    <SectionTitle index="01" title={<>News & <em>Highlights</em></>} hint="recent activity" />

    <Highlights setView={setView} />

    <ol className="news-list">
      {data.news.slice(0, 5).map((n, i) => (
        <li key={i} className="news-item">
          <MonoLabel className="news-date">{n.date}</MonoLabel>
          <p className="news-body">{n.body}</p>
        </li>
      ))}
    </ol>

    <a href="#news" className="view-all" onClick={(e) => { e.preventDefault(); setView("news"); window.scrollTo({ top: 0, behavior: "instant" }); }}>
      View all news <Icon name="arrow" size={12} />
    </a>
  </section>
);

const News = () => (
  <section id="news" className="section" data-screen-label="03 News">
    <SectionTitle index="—" title="News & highlights" hint={`${data.news.length} entries · all-time`} />
    <ol className="news-list">
      {data.news.map((n, i) => (
        <li key={i} className="news-item">
          <MonoLabel className="news-date">{n.date}</MonoLabel>
          <p className="news-body">{n.body}</p>
        </li>
      ))}
    </ol>
  </section>
);

// ============================================================
// Publications
// ============================================================

const formatAuthors = (authors) => {
  return authors.map((a, i) => {
    const isMe = a.replace(/[†*]/g, "").trim() === "J. Kang";
    return (
      <span key={i} className={isMe ? "author author--me" : "author"}>
        {a}{i < authors.length - 1 ? ", " : ""}
      </span>
    );
  });
};

const PubThumbImg = ({ pub }) => {
  const [errored, setErrored] = useState(false);
  const src = `papers/thumbs/${pub.id}.webp`;
  if (errored) {
    return <PubThumb kind={pub.thumb.kind} palette={pub.thumb.palette} />;
  }
  return <img className="thumb-img" src={src} alt="" loading="lazy" onError={() => setErrored(true)} />;
};

const PubLink = ({ href, children }) => (
  <a className="text-button text-button--sm" href={href} target="_blank" rel="noopener">
    {children} <Icon name="arrowUp" size={11} />
  </a>
);

const Publications = () => {
  return (
    <section id="publications" className="section" data-screen-label="04 Publications">
      <SectionTitle index="03" title="Publications" hint={`${data.publications.length} papers`} />

      <div className="filter-bar">
        <div className="filter-spacer" />
        <a className="text-button text-button--sm" href={data.links.scholar} target="_blank" rel="noopener">Google Scholar <Icon name="arrowUp" size={11} /></a>
      </div>

      <ul className="pubs">
        {data.publications.map((p) => (
          <li key={p.id} className="pub">
            <div className="pub-thumb">
              <PubThumbImg pub={p} />
            </div>
            <div className="pub-body">
              <div className="pub-venue-row">
                <MonoLabel className="pub-venue">{p.venue} '{String(p.year).slice(2)}</MonoLabel>
                {p.tag && <MonoLabel className="pub-tag">{p.tag}</MonoLabel>}
                {typeof p.citations === "number" && p.citations > 0 && (
                  <MonoLabel className="pub-tag pub-cites">cited by {p.citations}</MonoLabel>
                )}
              </div>
              <h3 className="pub-title">{p.title}</h3>
              <p className="pub-authors">{formatAuthors(p.authors)}</p>
              <div className="pub-actions">
                {p.paper && <PubLink href={p.paper}>Paper</PubLink>}
                {p.pdf && <PubLink href={p.pdf}>PDF</PubLink>}
                {p.code && <PubLink href={p.code}>Code</PubLink>}
              </div>
            </div>
          </li>
        ))}
      </ul>
    </section>
  );
};

// ============================================================
// Service & Office Hours
// ============================================================

const Service = ({ variant }) => (
  <section id="service" className="section" data-screen-label="05 Service">
    <SectionTitle index="04" title="Academic service" hint="reviewing" />

    <div className="service-block" style={{ marginBottom: 56 }}>
      <h4 className="service-heading">Reviewer</h4>
      <ul className="service-list">
        {data.service.reviewer.map((r) => (
          <li key={r.name} className={r.badge ? "reviewer-chip reviewer-chip--featured" : "reviewer-chip"}>
            <span className="reviewer-name">{r.name}</span>
            {r.years && <span className="reviewer-years">{r.years}</span>}
            {r.badge && <span className="reviewer-badge">{r.badge}</span>}
          </li>
        ))}
      </ul>
    </div>

    <OfficeHours variant={variant} />
  </section>
);

const OfficeHours = ({ variant }) => (
  <div className={`office-hours office-hours--${variant}`}>
    <div className="office-hours-mark">
      <MonoLabel className="dim">office hours</MonoLabel>
    </div>
    <div className="office-hours-body">
      <h3 className="office-hours-title">Want to chat?</h3>
      <p className="office-hours-blurb">{data.officeHours.blurb}</p>
      <div className="office-hours-meta">
        <MonoLabel className="dim">when</MonoLabel>
        <span>{data.officeHours.slots}</span>
      </div>
      <a className="cta-button" href="mailto:jk26@ic.ac.uk?subject=Office%20hours%20—%20chat%20request&body=Hi%20Jingxuan%2C%0A%0AI%27d%20like%20to%20book%20a%2020-minute%20slot.%20Here%27s%20what%20I%27d%20like%20to%20discuss%3A%0A%0A%5Byour%20note%5D%0A%0AThanks%21"><span>{data.officeHours.cta}</span> <Icon name="arrow" size={14} /></a>
    </div>
  </div>
);

// ============================================================
// Blog view
// ============================================================

// Posts live as standalone HTML under /blog/<slug>.html. blog/index.json is
// rebuilt by scripts/index_blog.py from each file's <meta name="post:*">.
// Two sub-tabs: Paper (论文笔记) / Blog (杂记), split by <meta name="post:category">.
const BlogView = ({ setView }) => {
  const [posts, setPosts] = useState(null); // null = loading, [] = empty
  const [sub, setSub] = useState("blog");   // "paper" | "blog"
  useEffect(() => {
    fetch("/blog/index.json", { headers: { Accept: "application/json" } })
      .then((r) => (r.ok ? r.json() : []))
      .then((list) => setPosts(Array.isArray(list) ? list : []))
      .catch(() => setPosts([]));
  }, []);

  const counts = { paper: 0, blog: 0 };
  (posts || []).forEach((p) => {
    const c = p.category === "paper" ? "paper" : "blog";
    counts[c] += 1;
  });
  const visible = (posts || []).filter((p) => (p.category === "paper" ? "paper" : "blog") === sub);
  const hint = posts === null ? "loading" : posts.length ? `${posts.length} posts` : "coming soon";

  return (
    <section className="section section--blog" data-screen-label="06 Blog">
      <SectionTitle title="Article" hint={hint} />

      <div className="blog-subtabs" role="tablist">
        {[
          { id: "paper", label: "Paper" },
          { id: "blog",  label: "Blog"  },
        ].map((t) => (
          <button
            key={t.id}
            role="tab"
            aria-selected={sub === t.id}
            className={`blog-subtab ${sub === t.id ? "on" : ""}`}
            onClick={() => setSub(t.id)}
          >
            {t.label} <span className="blog-subtab-count">{counts[t.id]}</span>
          </button>
        ))}
      </div>

      {visible.length > 0 && (
        <ul className="blog-list">
          {visible.map((b) => (
            <li key={b.slug} className="blog-item">
              <a className="blog-item-button" href={b.href}>
                <div className="blog-item-meta">
                  <MonoLabel className="dim">{b.date}</MonoLabel>
                  {b.readingTime != null && (
                    <MonoLabel className="dim"><Icon name="clock" size={11} /> {b.readingTime} min</MonoLabel>
                  )}
                  <div className="blog-tags">
                    {(b.tags || []).map((t) => <MonoLabel key={t} className="blog-tag">#{t}</MonoLabel>)}
                  </div>
                </div>
                <h3 className="blog-item-title">{b.title}</h3>
                {b.excerpt && <p className="blog-item-excerpt">{b.excerpt}</p>}
                <span className="blog-item-read">Read <Icon name="arrow" size={12} /></span>
              </a>
            </li>
          ))}
        </ul>
      )}
      {posts && visible.length === 0 && (
        <p className="blog-empty">
          {sub === "paper" ? "还没有论文笔记 — first one coming soon." : "No posts yet — first one is in the oven."}
        </p>
      )}
    </section>
  );
};

// ============================================================
// Footer
// ============================================================

const Footer = () => (
  <footer className="site-footer">
    <div className="footer-inner">
      <div>
        <MonoLabel className="dim">$ whoami</MonoLabel>
        <p className="footer-line">Jingxuan Kang · PhD Student · Imperial College London</p>
      </div>
      <div className="footer-meta">
        <MonoLabel className="dim">© 2026 · last updated 2026.05.15</MonoLabel>
        <MonoLabel className="dim">built with html, ☕ and too many late nights</MonoLabel>
      </div>
    </div>
  </footer>
);

// ============================================================
// Main App
// ============================================================

const App = () => {
  const [view, setView] = useState("about");
  const [tweaks, setTweak] = useTweaks(window.TWEAK_DEFAULTS);
  const [, forceRerender] = useState(0);

  useEffect(() => {
    let cancelled = false;
    // /site.json is the public, Cloudflare-Access-bypass-safe mirror of
    // site:data KV. /api/site-data is the same data but gated to edit users.
    fetch("/site.json", { headers: { Accept: "application/json" } })
      .then((r) => (r.ok ? r.json() : null))
      .then((next) => {
        if (cancelled || !next || typeof next !== "object" || !next.profile) return;
        data = next;
        window.SITE_DATA = next;
        forceRerender((n) => n + 1);
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, []);

  useEffect(() => {
    const fromHash = () => {
      const h = window.location.hash.replace("#", "");
      const valid = TABS.some((t) => t.id === h);
      if (valid && h !== view) setView(h);
    };
    fromHash();
    window.addEventListener("hashchange", fromHash);
    return () => window.removeEventListener("hashchange", fromHash);
  }, []);

  // Hidden admin trigger: Cmd+E / Ctrl+E from anywhere → /edit.html
  useEffect(() => {
    const handler = (e) => {
      if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && e.key.toLowerCase() === "e") {
        e.preventDefault();
        window.location.href = "/edit.html";
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, []);

  useEffect(() => {
    if (view && window.location.hash.replace("#", "") !== view) {
      history.replaceState(null, "", `#${view}`);
    }
  }, [view]);

  return (
    <>
      <Nav view={view} setView={setView} />
      <main className="main">
        {view === "about" && <><Hero /><NewsAndHighlights setView={setView} /></>}
        {view === "research" && <Research />}
        {view === "news" && <News />}
        {view === "publications" && <Publications />}
        {view === "service" && <Service variant={tweaks.officeHoursVariant} />}
        {view === "blog" && <BlogView setView={setView} />}
      </main>
      <Footer />

      <TweaksPanel>
        <TweakSection title="Office Hours card">
          <TweakRadio
            label="Background style"
            value={tweaks.officeHoursVariant}
            onChange={(v) => setTweak("officeHoursVariant", v)}
            options={[
              { value: "a", label: "Rule" },
              { value: "b", label: "Editorial" },
              { value: "c", label: "Orange" },
            ]}
          />
        </TweakSection>
      </TweaksPanel>
    </>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
