/* pages-gallery.jsx — 活动相册 · 时间线版
   结构：深色页头（总数）→ 吸顶年份索引（换行排布，手机上全部可见）→ 按年份分组的竖向时间线
   每场：日期 / 标题 / 地点 / 人数 / 说明 + 照片拼贴，点开进入全屏灯箱
*/

function galParseDate(s) {
  const m = /(\d{4})年(\d{1,2})月(\d{1,2})/.exec(s || '');
  return m ? Number(m[1]) * 10000 + Number(m[2]) * 100 + Number(m[3]) : 0;
}
function galShortDate(s) {
  /* "2019年5月3~5日" → { md: "5.03—05", y: 2019 } */
  const m = /(\d{4})年(\d{1,2})月(\d{1,2})(?:[~～-](\d{1,2}))?日?/.exec(s || '');
  if (!m) return { md: s || '', y: '' };
  const p = (n) => String(n).padStart(2, '0');
  return { y: m[1], md: m[2] + '.' + p(m[3]) + (m[4] ? '—' + p(m[4]) : '') };
}

function GalleryStyles() {
  return <style>{`
.gt-hero{background:var(--ink);color:var(--on-dark);padding:clamp(40px,5vw,76px) 0 clamp(26px,3vw,40px)}
.gt-hero h1{color:#fff;margin-top:16px;font-size:clamp(32px,4vw,56px)}
.gt-hero p{margin-top:16px;max-width:52ch;color:rgba(238,244,243,.68);font-size:15.5px;line-height:1.85;font-weight:300}
.gt-stats{display:flex;flex-wrap:wrap;gap:clamp(20px,4vw,56px);margin-top:28px;padding-top:22px;border-top:1px solid rgba(238,244,243,.12)}
.gt-stats b{display:block;font-family:var(--f-serif);font-weight:400;font-size:clamp(28px,3vw,40px);color:#fff;line-height:1}
.gt-stats span{display:block;margin-top:8px;font-size:12px;letter-spacing:.06em;color:rgba(238,244,243,.55)}

/* 年份索引：flex-wrap，不横向滚动 —— 任何屏宽都能一眼看到全部年份 */
.gt-rail{position:sticky;top:72px;z-index:30;background:color-mix(in oklab,var(--cloud) 92%,transparent);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.gt-rail-in{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:12px var(--pad-x);max-width:var(--max,1240px);margin:0 auto}
.gt-rail .k{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--txt-3);margin-right:8px}
.gt-rail button{border:1px solid var(--line-strong);background:var(--paper,#fff);color:var(--txt-2);border-radius:999px;padding:7px 13px;font-size:13px;line-height:1;display:inline-flex;align-items:baseline;gap:6px;cursor:pointer;transition:all .25s}
.gt-rail button small{font-family:var(--f-mono);font-size:10.5px;color:var(--txt-3)}
.gt-rail button:hover{border-color:var(--teal-500);color:var(--teal-900)}
.gt-rail button.on{background:var(--teal-900);border-color:var(--teal-900);color:#fff}
.gt-rail button.on small{color:rgba(255,255,255,.65)}

/* 时间线 */
.gt-body{padding:clamp(28px,4vw,64px) 0 clamp(60px,7vw,110px)}
.gt-year{display:grid;grid-template-columns:140px minmax(0,1fr);gap:0 clamp(24px,3vw,48px);scroll-margin-top:140px}
.gt-year + .gt-year{margin-top:clamp(36px,4vw,64px)}
.gt-ylab{position:relative}
.gt-ylab .y{position:sticky;top:150px;font-family:var(--f-serif);font-size:clamp(44px,4.4vw,64px);line-height:.9;color:var(--teal-900);letter-spacing:-.02em}
.gt-ylab .y small{display:block;margin-top:10px;font-family:var(--f-mono);font-size:10.5px;letter-spacing:.14em;color:var(--txt-3);text-transform:uppercase}
.gt-list{position:relative;padding-left:32px;border-left:1px solid var(--line-strong)}
.gt-ev{position:relative;scroll-margin-top:150px}
.gt-ev + .gt-ev{margin-top:clamp(30px,3.4vw,48px)}
.gt-ev::before{content:"";position:absolute;left:-38px;top:6px;width:11px;height:11px;border-radius:50%;background:var(--cloud);border:2px solid var(--teal-500)}
.gt-ev.big::before{background:var(--gold);border-color:var(--gold)}
.gt-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 14px}
.gt-date{font-family:var(--f-mono);font-size:13px;letter-spacing:.06em;color:var(--teal-700)}
.gt-kind{font-size:11px;letter-spacing:.06em;padding:3px 9px;border-radius:999px;background:color-mix(in oklab,var(--teal-500) 12%,transparent);color:var(--teal-800)}
.gt-kind.l2{background:color-mix(in oklab,var(--gold) 20%,transparent);color:var(--gold-deep)}
.gt-kind.sp{background:var(--ink);color:#fff}
.gt-ev h3{margin-top:8px;font-size:clamp(19px,1.7vw,24px);line-height:1.35}
.gt-meta{margin-top:8px;display:flex;flex-wrap:wrap;gap:4px 16px;font-size:13.5px;color:var(--txt-2)}
.gt-meta b{font-weight:500;color:var(--txt)}
.gt-note{margin-top:10px;font-size:14px;line-height:1.8;color:var(--txt-2);font-weight:300;max-width:62ch}

/* 照片拼贴：首张大图 + 其余小图 */
.gt-mosaic{margin-top:16px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-auto-rows:clamp(78px,8.4vw,128px);gap:6px}
.gt-mosaic button{position:relative;padding:0;border:0;overflow:hidden;background:var(--cloud-2);cursor:zoom-in;border-radius:4px}
.gt-mosaic button:first-child{grid-column:span 2;grid-row:span 2}
.gt-mosaic img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s var(--ease),filter .4s}
.gt-mosaic button:hover img{transform:scale(1.04)}
.gt-mosaic .more{position:absolute;inset:0;display:grid;place-items:center;background:rgba(7,26,28,.55);color:#fff;font-size:14px;letter-spacing:.04em}
.gt-foot{margin-top:10px;display:flex;flex-wrap:wrap;gap:6px 18px;align-items:center;font-size:12px;color:var(--txt-3)}
.gt-foot button{border:0;background:none;padding:0;color:var(--teal-700);font-size:12.5px;cursor:pointer;text-decoration:underline;text-underline-offset:3px}

@media(max-width:820px){
  .gt-year{grid-template-columns:1fr}
  .gt-ylab .y{position:static;font-size:40px;margin-bottom:16px;display:flex;align-items:baseline;gap:12px}
  .gt-ylab .y small{margin-top:0}
  .gt-mosaic{grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:clamp(74px,22vw,110px)}
}
@media(max-width:560px){
  .gt-rail{top:64px}
  .gt-rail-in{gap:6px;padding:10px var(--pad-x)}
  .gt-rail .k{margin-right:4px}
  .gt-rail button{padding:8px 11px;font-size:13px;min-height:36px}
  .gt-list{padding-left:20px}
  .gt-ev::before{left:-26px}
  .gt-year,.gt-ev{scroll-margin-top:200px}
  .gt-mosaic{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:28vw}
  .gt-mosaic button:nth-child(n+6){display:none}
}
`}</style>;
}

function GalleryTimeline({ id }) {
  const events = React.useMemo(
    () => (QD.events || []).slice().sort((a, b) => galParseDate(b.date || b.dateText) - galParseDate(a.date || a.dateText)),
    []
  );
  const years = React.useMemo(() => {
    const m = new Map();
    events.forEach((e) => {
      const y = String(e.year || galShortDate(e.date).y);
      if (!m.has(y)) m.set(y, []);
      m.get(y).push(e);
    });
    return [...m.entries()];
  }, [events]);

  const totals = React.useMemo(() => ({
    n: events.length,
    people: events.reduce((a, e) => a + (e.people || 0), 0),
    shown: events.reduce((a, e) => a + photoList(e).length, 0),
    orig: events.reduce((a, e) => a + (e.total || e.photos || 0), 0),
  }), [events]);

  const [curYear, setCurYear] = React.useState(years[0] ? years[0][0] : '');
  const [lb, setLb] = React.useState(null); // { ev, i }

  /* 滚动时高亮当前年份 */
  React.useEffect(() => {
    const els = [...document.querySelectorAll('.gt-year')];
    if (!els.length || !('IntersectionObserver' in window)) return;
    const io = new IntersectionObserver((es) => {
      const vis = es.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
      if (vis[0]) setCurYear(vis[0].target.getAttribute('data-year'));
    }, { rootMargin: '-160px 0px -55% 0px', threshold: 0 });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, [years]);

  const scrollToEl = (el) => {
    if (!el) return;
    const rail = document.querySelector('.gt-rail');
    const off = rail ? Math.max(rail.getBoundingClientRect().bottom, 0) + 14 : 140;
    const top = el.getBoundingClientRect().top + window.scrollY - off;
    window.scrollTo({ top, behavior: 'smooth' });
  };
  const jumpYear = (y) => { setCurYear(y); scrollToEl(document.querySelector('.gt-year[data-year="' + y + '"]')); };

  /* 从活动详情「看相册」跳过来时，定位到那一场 */
  React.useEffect(() => {
    if (!id) return;
    const t = setTimeout(() => scrollToEl(document.getElementById('gt-' + id)), 120);
    return () => clearTimeout(t);
  }, [id]);

  const step = React.useCallback((d) => {
    setLb((s) => {
      if (!s) return s;
      const n = photoList(s.ev).length;
      return { ev: s.ev, i: (s.i + d + n) % n };
    });
  }, []);
  const close = React.useCallback(() => setLb(null), []);

  const kindCls = (k) => (k === '二阶段' ? 'l2' : (k === '特别场' ? 'sp' : ''));
  const go = window.__go;

  return (
    <>
      <RealStyles />
      <GalleryStyles />

      <section className="gt-hero">
        <div className="wrap">
          <div className="eyebrow on-dark" style={{ display: 'inline-flex' }}>活动相册 · {years.length ? years[years.length - 1][0] : ''}—{years.length ? years[0][0] : ''}</div>
          <h1 className="d1">每一场课，都有人在现场。</h1>
          <p>按时间从近到远排列。每场保留了代表性的现场照片，点开任意一张可以全屏翻看。</p>
          <div className="gt-stats">
            <div><b>{totals.n}</b><span>场课程与活动</span></div>
            <div><b>{totals.people}</b><span>人次参与</span></div>
            <div><b>{years.length}</b><span>个年份</span></div>
            <div><b>{totals.orig}<sup style={{ fontSize: '.5em' }}>+</sup></b><span>张原站现场照片</span></div>
          </div>
        </div>
      </section>

      <nav className="gt-rail" aria-label="按年份跳转">
        <div className="gt-rail-in">
          <span className="k">年份</span>
          {years.map(([y, list]) => (
            <button key={y} className={curYear === y ? 'on' : ''} onClick={() => jumpYear(y)} aria-current={curYear === y ? 'true' : undefined}>
              {y}<small>{list.length}</small>
            </button>
          ))}
        </div>
      </nav>

      <section className="gt-body">
        <div className="wrap">
          {years.map(([y, list]) => (
            <div className="gt-year" key={y} data-year={y}>
              <div className="gt-ylab">
                <div className="y">{y}<small>{list.length} 场 · {list.reduce((a, e) => a + (e.people || 0), 0)} 人次</small></div>
              </div>
              <div className="gt-list">
                {list.map((ev) => {
                  const photos = photoList(ev);
                  const d = galShortDate(ev.date || ev.dateText);
                  const maxShow = 7;
                  const shown = photos.slice(0, maxShow);
                  const rest = photos.length - shown.length;
                  return (
                    <article key={ev.id} id={'gt-' + ev.id} className={'gt-ev' + ((ev.people || 0) >= 70 ? ' big' : '')}>
                      <div className="gt-head">
                        <span className="gt-date">{d.md}</span>
                        <span className={'gt-kind ' + kindCls(ev.kind)}>{ev.kind}</span>
                      </div>
                      <h3>{ev.title}</h3>
                      <div className="gt-meta">
                        <span>{ev.city}{ev.place ? ' · ' + ev.place : ''}</span>
                        {ev.people ? <span><b>{ev.people}</b> 人</span> : null}
                      </div>
                      {ev.note ? <p className="gt-note">{ev.note}</p> : null}
                      {shown.length > 0 && (
                        <div className="gt-mosaic">
                          {shown.map((src, k) => (
                            <button key={src} onClick={() => setLb({ ev, i: k })} aria-label={`查看第 ${k + 1} 张`}>
                              <img src={src} alt={`${ev.title} 现场 ${k + 1}`} loading="lazy" />
                              {k === shown.length - 1 && rest > 0 && <span className="more">+{rest}</span>}
                            </button>
                          ))}
                        </div>
                      )}
                      <div className="gt-foot">
                        <span>本站选取 {photos.length} 张{(ev.total || 0) > photos.length ? ` · 原站此场共 ${ev.total} 张` : ''}</span>
                        {photos.length > 0 && <button onClick={() => setLb({ ev, i: 0 })}>全屏浏览</button>}
                        {go && <button onClick={() => go('events')}>活动介绍</button>}
                      </div>
                    </article>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
      </section>

      {lb && <Lightbox ev={lb.ev} index={lb.i} onClose={close} onStep={step} />}
    </>
  );
}

Object.assign(window, { GalleryTimeline, GalleryStyles });
