// Hero — 纸本首屏:真实馆藏三维扫描悬在暖纸上,文字退成一张展签。
// 设计取向:印刷品 / 水墨的语言(大留白、极小说明、单一强调色),
// 不用「暗色照片 + 巨型标题」那套通用模板。
const { useState, useEffect, useRef, useCallback } = React;

const NAV_LINKS = ['exhibitions', 'collection', 'gallery', 'about', 'visit', 'festival'];

function Nav({ lang, setLang, paper, page, solidAt = 0.9 }) {
  const t = T.nav[lang];
  const [solid, setSolid] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  useEffect(() => {
    const onScroll = () => setSolid(window.scrollY > window.innerHeight * solidAt);
    window.addEventListener('scroll', onScroll);
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, [solidAt]);
  useEffect(() => {
    document.body.style.overflow = menuOpen ? 'hidden' : '';
  }, [menuOpen]);
  const close = () => setMenuOpen(false);
  // 纸本首屏下导航从一开始就是墨色;暗色回退首屏才走原来的「滚动后变深」
  const dark = paper || solid || menuOpen;

  const LangSwitch = () => (
    <>
      <button type="button" className={'lang' + (lang === 'zh' ? ' active' : '')}
        aria-pressed={lang === 'zh'} onClick={() => setLang('zh')}>CN</button>
      <span>·</span>
      <button type="button" className={'lang' + (lang === 'en' ? ' active' : '')}
        aria-pressed={lang === 'en'} onClick={() => setLang('en')}>EN</button>
    </>);

  return (
    <>
      <nav className={'nav' + (solid ? ' solid' : '') + (paper ? ' on-paper' : '') + (menuOpen ? ' menu-open' : '')} style={{ height: "50px" }}>
        <a href="/" className="brand" aria-label="南通市富美帽饰博物馆 Nantong Foremost Hat Museum">
          <img className="brand-logo" src={dark ? 'assets/logo.png?v=4' : 'assets/logo-white.png?v=4'}
            alt="Nantong Foremost Hat Museum"
            style={{ objectFit: "contain", objectPosition: "left center", width: "250px" }} />
        </a>
        <div className="nav-links">
          {NAV_LINKS.map(k => (
            <a key={k} href={'/' + k} className={(page === k ? 'current' : '') + (k === 'festival' ? ' fest' : '')}
               aria-current={page === k ? 'page' : undefined}>{t[k]}</a>))}
        </div>
        <div className="nav-cta"><LangSwitch /></div>
        <button className="nav-burger" aria-label={menuOpen ? t.close : t.open}
          aria-expanded={menuOpen} onClick={() => setMenuOpen(o => !o)}>
          <span></span><span></span><span></span>
        </button>
      </nav>
      <div className={'nav-drawer' + (menuOpen ? ' open' : '')} aria-hidden={!menuOpen}>
        <a href="/" onClick={close} className={page === 'home' ? 'current' : ''}>{t.home}</a>
        {NAV_LINKS.map(k => (
          <a key={k} href={'/' + k} onClick={close} className={(page === k ? 'current' : '') + (k === 'festival' ? ' fest' : '')}>{t[k]}</a>))}
        <div className="nav-drawer-foot"><LangSwitch /></div>
      </div>
    </>);
}

// 三维不可用时(WebGL 关闭 / CDN 被墙 / 加载超时)退回原来的影像首屏
// ── 首屏:博物馆外观大图轮播 ──────────────────────────────
// 六张实拍,6.5 秒一换、交叉淡入、缓慢推近;悬停 / 页面不可见 / 用户要求减少动画时停;支持左右滑。
// 首图立即加载,其余等首图到位再挂,首屏只多一张图的流量。
// ws = 已导出的宽度档(assets/hero/slide-<id>-<w>.jpg),浏览器按 DPR × 视口宽自己挑;
// 视网膜手机取 1280,2× 桌面取 2560 / 3840,不再是一张 2000 拉到全屏
const SLIDES = [
  { id: 'pond-day', pos: '50% 58%', ws: [1280, 1920, 2560, 3840] },
  { id: 'pond-dusk', pos: '50% 50%', ws: [1280, 1920, 2560, 3840] },
  { id: 'aerial', pos: '50% 45%', ws: [1280, 1920, 2560, 3840] },
  { id: 'tree', pos: '50% 55%', ws: [1280, 1920, 2560] },
  { id: 'sky', pos: '50% 40%', ws: [1280, 1920, 2560, 3840] },
  { id: 'columns', pos: '50% 50%', ws: [1280, 1920, 2560] }
];
const slideSrc = (id, w) => `assets/hero/slide-${id}-${w}.jpg`;
const HOLD = 6500;

function HeroCarousel({ lang }) {
  const t = T.carousel[lang];
  const [i, setI] = useState(0);
  const [rest, setRest] = useState(false);
  const [paused, setPaused] = useState(false);
  const reduce = useRef(window.matchMedia('(prefers-reduced-motion: reduce)').matches);
  const touch = useRef(null);
  useEffect(() => { const id = setTimeout(() => setRest(true), 1200); return () => clearTimeout(id); }, []);
  useEffect(() => {
    if (paused || reduce.current || !rest) return;
    const id = setInterval(() => { if (document.visibilityState === 'visible') setI(v => (v + 1) % SLIDES.length); }, HOLD);
    return () => clearInterval(id);
  }, [paused, rest]);
  const go = d => setI(v => (v + d + SLIDES.length) % SLIDES.length);
  return (
    <section className="hc" data-screen-label="01 Hero"
      onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}
      onTouchStart={e => { touch.current = e.touches[0].clientX; }}
      onTouchEnd={e => {
        if (touch.current == null) return;
        const dx = e.changedTouches[0].clientX - touch.current; touch.current = null;
        if (Math.abs(dx) > 40) go(dx < 0 ? 1 : -1);
      }}>
      {SLIDES.map((sl, k) => (k === 0 || rest) && (
        <div key={sl.id} className={'hc-slide' + (k === i ? ' on' : '')} aria-hidden={k !== i}>
          <img src={slideSrc(sl.id, 1920)} srcSet={sl.ws.map(w => `${slideSrc(sl.id, w)} ${w}w`).join(', ')} sizes="100vw"
            alt={t.slides[sl.id]} style={{ objectPosition: sl.pos }}
            loading={k === 0 ? 'eager' : 'lazy'} fetchpriority={k === 0 ? 'high' : 'low'} decoding="async" />
        </div>))}
      <div className="hc-shade" aria-hidden></div>
      <div className="hc-text">
        <h1 className="hc-h1">{t.h1}</h1>
        <p className="hc-sub">{t.sub}</p>
        <div className="hc-cta">
          <a className="hc-btn red" href="/festival">{t.cta1} →</a>
          <a className="hc-btn" href="/collection">{t.cta2} →</a>
        </div>
      </div>
      <div className="hc-foot mono">
        <span className="hc-cap">{t.slides[SLIDES[i].id]}</span>
        <span className="hc-ctl">
          <button type="button" onClick={() => go(-1)} aria-label={t.prev}>←</button>
          <span className="hc-n">{String(i + 1).padStart(2, '0')} / {String(SLIDES.length).padStart(2, '0')}</span>
          <button type="button" onClick={() => go(1)} aria-label={t.next}>→</button>
        </span>
        <span className="hc-dots" role="tablist">
          {SLIDES.map((sl, k) => <button key={sl.id} type="button" role="tab" aria-selected={k === i} className={k === i ? 'on' : ''} onClick={() => setI(k)} aria-label={t.slides[sl.id]} />)}
        </span>
      </div>
    </section>);
}

// staticMode:没有 WebGL 时不挂查看器,只显示同一件器物的缩略图,轨道照样能切
function HeroStage({ lang, staticMode }) {
  const t = T.hero[lang];
  const objs = T.objects.filter(o => o.hero);   // 首屏只放精选 8 件,全部 21 件在典藏库
  const [idx, setIdx] = useState(0);
  const [busy, setBusy] = useState(true);
  const [pct, setPct] = useState(0);
  const [card, setCard] = useState(null);   // 明信片:存点按当下截取的那一帧
  const [openingCard, setOpeningCard] = useState(false);
  const [touchMode] = useState(() => matchMedia('(any-pointer: coarse)').matches || navigator.maxTouchPoints > 0);
  const [interacting, setInteracting] = useState(false);
  const cardTrigger = useRef(null);
  const stage = useRef(null);
  const api = useRef(null);

  useEffect(() => {
    if (staticMode) { setBusy(false); return; }
    let alive = true;
    (async () => {
      const a = await window.__viewerReady;
      if (!alive || !stage.current) return;
      api.current = a;
      a.mount(stage.current, {scrollFirst: touchMode});
      try { await a.load(objs[0].slug, p => alive && setPct(p)); } catch (e) {}
      if (alive) setBusy(false);
    })();
    return () => { alive = false; if (api.current) api.current.dispose(); };
  }, []);

  useEffect(() => {
    if (!touchMode || !api.current) return;
    api.current.setInteraction(interacting);
  }, [interacting, busy, touchMode]);

  const pick = useCallback(async (i) => {
    if (openingCard) return;
    if (staticMode) { setIdx(i); return; }
    if (!api.current || i === idx || busy) return;
    setIdx(i); setPct(0); setBusy(true);
    try { await api.current.load(objs[i].slug, p => setPct(p)); } catch (e) {}
    setBusy(false);
  }, [idx, busy, objs, staticMode, openingCard]);

  const o = objs[idx];
  return (
    <section className="hero-paper as-section" id="stage" data-screen-label="03 Stage">
      {/* 建筑在地:自家绘制的博艺楼水墨稿,底部通栏,顶边渐隐融进纸色 */}
      <img className="bldg" src="assets/bldg-wide.jpg" alt="南通市富美帽饰博物馆 · 博艺楼" aria-hidden="true" />

      <div className={'stage-wrap' + (touchMode && !interacting ? ' stage-scroll-first' : '')}>
        <div className="stage" ref={stage} />
        {/* 三维就绪前先摆一张同一件器物的静态图,几秒后无缝让位;不让访客对着「0%」发呆 */}
        <img className={'stage-poster' + (busy || staticMode ? '' : ' gone')} src={`models/thumbs/${o.slug}.jpg`} alt="" aria-hidden="true" />
        <div className="stage-shadow" aria-hidden></div>
        <div className={'stage-load mono' + (busy && !staticMode ? '' : ' done')} aria-live="polite">
          {t.loading}<span className="pct">{Math.round(pct * 100)}%</span>
        </div>
      </div>

      {touchMode && !staticMode && <div className="stage-touch-tools">
        <button type="button" disabled={busy} aria-pressed={interacting} onClick={() => setInteracting(v => !v)}>
          {lang === 'zh' ? (interacting ? '完成旋转 · 继续浏览 ↓' : '点击旋转藏品 ↻') : (interacting ? 'Done · continue scrolling ↓' : 'Tap to rotate ↻')}
        </button>
        <small>{lang === 'zh' ? (interacting ? '拖动藏品，完成后点上方按钮' : '上下滑动浏览页面；想看细节时再开启旋转') : (interacting ? 'Drag the object; tap Done to scroll again' : 'Swipe to scroll; enable rotation to explore the object')}</small>
      </div>}

      {/* 顺着画稿构图:左侧留白放展签,右侧柱廊下放轨道,中间那匹红马是比例尺,让开 */}
      <div className="hero-foot">
      <figcaption className="plate">
        <div className="plate-rule" aria-hidden></div>
        <h2 className="plate-name">{lang === 'zh' ? o.zh : o.en}</h2>
        <div className="plate-sub mono">{lang === 'zh' ? o.zhSub : o.enSub}</div>
        <div className="plate-place mono">Nantong, China</div>
      </figcaption>

      <div className="rail" role="tablist" aria-label={t.objLabel}>
        {objs.map((x, i) => (
          <button key={x.slug} role="tab" aria-selected={i === idx}
            className={'rail-item' + (i === idx ? ' on' : '')}
            title={lang === 'zh' ? x.zh : x.en}
            onClick={() => pick(i)}>
            <img src={`models/thumbs/${x.slug}.jpg`} alt={lang === 'zh' ? x.zh : x.en} decoding="async" />
          </button>
        ))}
      </div>
      </div>

      <div className="hero-meta mono">
        <span>{t.mast}</span>
        <button className="mast-card" ref={cardTrigger} disabled={openingCard} aria-haspopup="dialog" aria-busy={openingCard}
          onClick={async () => {
            // 必须在弹窗盖住画布之前截 —— 画布被遮挡后浏览器会停止合成,截出来是空白
            setOpeningCard(true);
            try {
              const shot = !busy && !staticMode ? await window.grabShot(api.current) : null;
              setCard({ shot });
            } finally { setOpeningCard(false); }
          }}>
          <span className="pc-envelope" aria-hidden="true"><i className="pc-envelope-letter" /><i className="pc-envelope-pocket" /><i className="pc-envelope-flap" /></span>
          <span className="mast-card-copy"><strong>{openingCard ? T.card[lang].sharing : T.card[lang].open}</strong><small>{T.card[lang].openHint}</small></span>
          <span className="mast-card-arrow" aria-hidden="true">↗</span>
        </button>
        <span className="hint">{touchMode ? (lang === 'zh' ? (interacting ? '正在旋转 · 完成后继续浏览' : '上下滑动浏览 · 点选切换藏品') : (interacting ? 'Rotation enabled · tap Done to scroll' : 'Swipe to scroll · tap to switch objects')) : t.hint}</span>
      </div>

      {card && <Postcard lang={lang} shot={card.shot} object={o} returnFocusRef={cardTrigger} onClose={() => setCard(null)} />}
    </section>);
}

// mode 由 App 统一判定(导航配色要跟首屏一致,不能各判各的)
function Hero({ lang, mode }) {
  if (mode === 'wait') return <section className="hero-paper as-section hero-boot" id="stage" data-screen-label="03 Stage" />;
  return <HeroStage lang={lang} staticMode={mode === 'photo'} />;
}

Object.assign(window, { Nav, Hero, HeroStage, HeroCarousel });
