// sections.jsx — section components for the homepage

const { useEffect, useRef, useState } = React;

// ─────────────────────────────────────────────────────────────────────────────
// Reveal-on-scroll hook
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    // If the doc is hidden (background tab), show everything immediately — IO won't fire.
    if (document.visibilityState === 'hidden') {
      el.querySelectorAll('.reveal').forEach((n) => n.classList.add('in'));
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '-40px 0px' });
    el.querySelectorAll('.reveal').forEach((n) => io.observe(n));
    // Safety net: if for any reason a reveal element hasn't fired after 2.5s
    // (e.g. observer never fired because parent is offscreen), force it visible.
    const t = setTimeout(() => {
      el.querySelectorAll('.reveal:not(.in)').forEach((n) => n.classList.add('in'));
    }, 2500);
    return () => { io.disconnect(); clearTimeout(t); };
  }, []);
  return ref;
}

// ─────────────────────────────────────────────────────────────────────────────
function Nav({ c, lang, setLang, brandName, brandSub, scrolled }) {
  return (
    <nav className={'site-nav' + (scrolled ? ' scrolled' : '')}>
      <div className="nav-links">
        <a href="#story">{c.nav.story}</a>
        <a href="#suites">{c.nav.suites}</a>
        <a href="#dining">{c.nav.dining}</a>
        <a href="#activities">{c.nav.activities}</a>
        <a href="#access">{c.nav.access}</a>
      </div>
      <div className="logo">
        {brandName}
        <span className="sub">{brandSub}</span>
      </div>
      <div className="nav-right">
        <LocalTime lang={lang} />
        <div className="lang-toggle">
          <button className={lang === 'ja' ? 'active' : ''} onClick={() => setLang('ja')}>JA</button>
          <span className="slash">/</span>
          <button className={lang === 'en' ? 'active' : ''} onClick={() => setLang('en')}>EN</button>
        </div>
        <a className="reserve" href="#reserve"><span>{c.nav.reserve}</span></a>
      </div>
    </nav>
  );
}

// Live local time at Chichijima (JST = UTC+9)
function LocalTime({ lang }) {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000 * 30);
    return () => clearInterval(id);
  }, []);
  // Format to JST regardless of viewer's timezone
  const jst = new Intl.DateTimeFormat('en-GB', {
    hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Asia/Tokyo',
  }).format(now);
  return (
    <div className="local-time" title="Chichijima local time">
      <span className="lt-dot" />
      <span className="lt-label">{lang === 'ja' ? '父島' : 'Chichijima'}</span>
      <span className="lt-clock">{jst}</span>
    </div>
  );
}

// Helper: line-mask wrapper for staggered "rise from a clipping rectangle" reveals
function L({ children, d = 0, className = '' }) {
  return (
    <span className={'line ' + className}>
      <span className="line-i" style={{ '--d': d + 's' }}>{children}</span>
    </span>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Hero({ c, layout, heroImg, lang, slideshowOn, slideDur, kenburns, slides }) {
  const h = c.hero;
  const reduceMotion = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  // Build slide list. When slideshow is off, the user's chosen heroImg shows alone.
  const list = (slideshowOn && slides && slides.length > 0)
    ? slides
    : [{ src: heroImg, kickerJa: h.kicker, kickerEn: h.kicker, tag: h.meta3 }];

  const [idx, setIdx] = useState(0);
  const [parallax, setParallax] = useState(0);
  const [ready, setReady] = useState(false);

  // Gate entrance animations on fonts+first-image being ready — kills the FOUT jolt
  useEffect(() => {
    let cancelled = false;
    const firstImg = (list[0] && list[0].src) || null;
    const fontsP = (document.fonts && document.fonts.ready) || Promise.resolve();
    const imgP = firstImg
      ? new Promise((res) => { const im = new Image(); im.onload = res; im.onerror = res; im.src = firstImg; })
      : Promise.resolve();
    Promise.all([fontsP, imgP]).then(() => { if (!cancelled) setReady(true); });
    // safety net so we never hang
    const safety = setTimeout(() => { if (!cancelled) setReady(true); }, 2200);
    return () => { cancelled = true; clearTimeout(safety); };
  }, []);

  // Preload ALL non-first slides at mount so crossfades don't show half-loaded frames
  useEffect(() => {
    list.slice(1).forEach((s) => { if (s.src) { const im = new Image(); im.src = s.src; } });
  }, []);

  // Auto-advance (independent of mouse — just keeps cycling)
  useEffect(() => {
    if (list.length <= 1 || reduceMotion || !ready) return;
    const ms = (slideDur || 6) * 1000;
    const id = setTimeout(() => setIdx((i) => (i + 1) % list.length), ms);
    return () => clearTimeout(id);
  }, [idx, list.length, slideDur, reduceMotion, ready]);

  // If list shrinks/grows, clamp idx
  useEffect(() => { if (idx >= list.length) setIdx(0); }, [list.length]);

  // Parallax on scroll (only first viewport — capped)
  useEffect(() => {
    let raf = 0;
    const on = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        const y = Math.min(window.scrollY, window.innerHeight);
        setParallax(y * 0.32);
      });
    };
    window.addEventListener('scroll', on, { passive: true });
    return () => { window.removeEventListener('scroll', on); cancelAnimationFrame(raf); };
  }, []);

  const prev = () => setIdx((i) => (i - 1 + list.length) % list.length);
  const next = () => setIdx((i) => (i + 1) % list.length);

  const kickerKey = lang === 'en' ? 'kickerEn' : 'kickerJa';
  const dur = (slideDur || 6) + 's';

  return (
    <header className={'hero' + (kenburns === false ? ' no-kb' : '') + (ready ? ' ready' : '')}
            data-layout={layout} data-tone={list[idx]?.tone || 'medium'}>
      <div className="bg" style={{ transform: `translate3d(0, ${parallax}px, 0)` }}>
        {list.map((s, i) => (
          <div key={i} className={'h-slide' + (i === idx ? ' active' : '')}>
            <img src={s.src} alt=""
                 loading={i === 0 ? 'eager' : 'eager'}
                 fetchpriority={i === 0 ? 'high' : 'low'}
                 decoding="async" />
          </div>
        ))}
      </div>
      <div className="scrim" />

      {/* Subtle drifting embers — atmospheric only */}
      <div className="embers" aria-hidden="true">
        {Array.from({ length: 12 }).map((_, i) => <span key={i} />)}
      </div>

      {/* Vertical rotated edge label */}
      <div className="hero-edge" aria-hidden="true">
        <span>STELLARE BONIN · 27.07°N 142.12°E · BONINSPRING</span>
        <span className="tick" />
        <span>EST. MMXXIV</span>
      </div>

      {/* Top-right rotating coordinate badge */}
      <CoordBadge slideTag={list[idx]?.tag} />

      <div className="hero-content">
        <div>
          <div className="kicker">
            {list.map((s, i) => (
              <span key={i} className={'k-line' + (i === idx ? ' on' : '')}>
                {s[kickerKey] || h.kicker}
              </span>
            ))}
            {/* invisible spacer to give the absolute-positioned kicker its width */}
            <span style={{ visibility: 'hidden' }}>{list[idx]?.[kickerKey] || h.kicker}</span>
          </div>
          <h1>
            <L d={0.20}>{h.titleA}</L>
            <L d={0.36} className="em-line"><span className="em">{h.titleB}</span></L>
          </h1>
          <p className="lede h-lede" style={{ '--d': '.7s' }}>{h.lede}</p>
        </div>
        <div className="hero-meta h-meta" style={{ '--d': '.95s' }}>
          <div className="row"><span>{h.meta1}</span><span className="rule"></span><span>{h.meta2}</span></div>
          <div className="row"><span>{list[idx]?.tag || h.meta3}</span></div>
        </div>
      </div>

      {list.length > 1 && (
        <React.Fragment>
          <div className="hero-arrows">
            <button onClick={prev} aria-label="Previous slide">
              <svg viewBox="0 0 24 24"><path d="M15 5L8 12l7 7" /></svg>
            </button>
            <button onClick={next} aria-label="Next slide">
              <svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7" /></svg>
            </button>
          </div>
          <div className="hero-pager">
            <div className="pager-counter">
              <span className="cur">{String(idx + 1).padStart(2, '0')}</span>
              <span className="sep">/</span>
              <span className="tot">{String(list.length).padStart(2, '0')}</span>
            </div>
            <div className="pager-dots">
              {list.map((_, i) => (
                <button key={i}
                        className={'dot' + (i === idx ? ' active' : '') + (i < idx ? ' done' : '')}
                        style={{ '--dur': dur }}
                        onClick={() => setIdx(i)}
                        aria-label={`Slide ${i + 1}`}>
                  <span className="bar"></span>
                </button>
              ))}
            </div>
          </div>
        </React.Fragment>
      )}

      <div className="scroll-cue">{h.explore}</div>
    </header>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Marquee({ items }) {
  if (!items || items.length === 0) return null;
  const doubled = [...items, ...items];
  return (
    <div className="marquee" aria-hidden="true">
      <div className="marquee-track">
        {doubled.map((s, i) => (
          <React.Fragment key={i}>
            <span>{s}</span>
            <span className="star">✦</span>
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Rotating coordinate badge — sits top-right on hero
function CoordBadge({ slideTag }) {
  // Curved text along a circle (radius 50, center 58,58)
  const r = 50;
  const outerText = 'BONINSPRING ·  STELLARE BONIN ·  27.07°N 142.12°E ·  ';
  return (
    <div className="hero-badge" aria-hidden="true">
      <svg viewBox="0 0 116 116">
        <defs>
          <path id="ring-path-outer" d={`M 58 58 m -${r} 0 a ${r} ${r} 0 1 1 ${r*2} 0 a ${r} ${r} 0 1 1 -${r*2} 0`} />
          <path id="ring-path-inner" d={`M 58 58 m -38 0 a 38 38 0 1 1 76 0 a 38 38 0 1 1 -76 0`} />
        </defs>
        {/* outer circle line + N/E/S/W ticks (static) */}
        <circle cx="58" cy="58" r={r + 3} fill="none" stroke="currentColor" strokeWidth="0.5" opacity="0.35" />
        <circle cx="58" cy="58" r={r} fill="none" stroke="currentColor" strokeWidth="0.7" opacity="0.75" />
        {[0, 90, 180, 270].map(deg => (
          <line key={deg} x1="58" y1={58 - r - 2} x2="58" y2={58 - r + 4}
                className="tick-mark"
                transform={`rotate(${deg} 58 58)`} />
        ))}
        {/* rotating outer text */}
        <g className="ring-outer">
          <text className="ring-text">
            <textPath href="#ring-path-outer" startOffset="0">{outerText + outerText}</textPath>
          </text>
        </g>
        {/* rotating inner text (smaller) */}
        <g className="ring-inner">
          <text className="ring-text" style={{ fontSize: 7, opacity: 0.55 }}>
            <textPath href="#ring-path-inner" startOffset="0">{'PACIFIC · OGASAWARA · MMXXIV · '.repeat(3)}</textPath>
          </text>
        </g>
      </svg>
      <div className="compass">
        <div className="n">N 27.07°</div>
        <div className="e">E 142.12°</div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Stats banner with count-up animation (Concept → Suites transition)
function Stats({ lang }) {
  const stats = (window.STATS && window.STATS[lang]) || (window.STATS && window.STATS.ja) || [];
  const wrapRef = useRef(null);
  const [trigger, setTrigger] = useState(false);
  useEffect(() => {
    const el = wrapRef.current; if (!el) return;
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting) { setTrigger(true); io.disconnect(); } });
    }, { threshold: 0.3 });
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return (
    <section className="stats" ref={wrapRef}>
      <div className="stats-head">
        <div className="eyebrow"><span className="ebar" />{lang === 'ja' ? 'BY THE NUMBERS — 静かな数字' : 'BY THE NUMBERS'}</div>
        <h3>{lang === 'ja'
          ? '到達は易くない。しかし、辿り着いた者だけに開かれる、四つの数字。'
          : 'Not easy to reach — and that is precisely the point. Four numbers that frame the stay.'}</h3>
      </div>
      <div className="stats-grid">
        {stats.map((s, i) => (
          <div className="stat" key={i}>
            <div className="big">
              <CountUp to={s.num} active={trigger} delay={i * 0.18} decimals={s.num % 1 !== 0 ? 2 : 0} />
              <span className="suffix">{s.suffix}</span>
            </div>
            <div className="label">{s.label}</div>
            <div className="sub">{s.sub}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

// Count-up tween — eases out over ~1.6s
function CountUp({ to, active, delay = 0, decimals = 0 }) {
  const [v, setV] = useState(0);
  useEffect(() => {
    if (!active) return;
    let raf = 0; const dur = 1700; let start = 0;
    const tick = (t) => {
      if (!start) start = t + delay * 1000;
      const k = Math.max(0, Math.min(1, (t - start) / dur));
      // ease-out cubic
      const e = 1 - Math.pow(1 - k, 3);
      setV(to * e);
      if (k < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [active, to, delay]);
  return <span>{v.toFixed(decimals)}</span>;
}

// ─────────────────────────────────────────────────────────────────────────────
// Sticky vertical section index — left edge, appears after hero, mix-blend-mode
function SectionIndex({ lang }) {
  const items = window.SECTIONS || [];
  const [activeId, setActiveId] = useState(null);
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    // Show when out of hero
    const onScroll = () => setVisible(window.scrollY > window.innerHeight * 0.6);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });

    // IntersectionObserver per section
    const io = new IntersectionObserver((es) => {
      // pick the most-intersecting one
      let best = null; let bestRatio = 0;
      es.forEach((e) => { if (e.intersectionRatio > bestRatio) { bestRatio = e.intersectionRatio; best = e.target.id; } });
      if (best) setActiveId(best);
    }, { threshold: [0.1, 0.3, 0.5], rootMargin: '-30% 0px -30% 0px' });
    items.forEach((it) => { const el = document.getElementById(it.id); if (el) io.observe(el); });

    return () => { window.removeEventListener('scroll', onScroll); io.disconnect(); };
  }, []);

  return (
    <nav className={'section-index' + (visible ? ' show' : '')} aria-hidden={!visible}>
      {items.map((it) => (
        <a key={it.id} href={'#' + it.id}
           className={'si-item' + (activeId === it.id ? ' active' : '')}>
          <span className="si-bar" />
          <span className="si-num">{it.num}</span>
          <span className="si-label">{lang === 'ja' ? it.ja : it.en}</span>
        </a>
      ))}
    </nav>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Concept({ c }) {
  const v = c.concept;
  const ref = useReveal();
  return (
    <section className="section concept" id="story" ref={ref}>
      <div className="container concept-grid">
        <div className="left">
          <div className="eyebrow reveal">{v.eyebrow}</div>
          <h2 className="reveal d1">
            <L d={0.05}>{v.title1}</L>
            <L d={0.18}><span className="em">{v.titleAccent}</span></L>
          </h2>
          <div className="reveal d1" style={{ fontSize: 10.5, letterSpacing: '.32em', textTransform: 'uppercase', color: 'var(--ink-mute)', marginTop: 26, marginBottom: 36, fontWeight: 400, display: 'flex', gap: 12, alignItems: 'center' }}><span className="rule"></span>{v.title2}</div>
          <p className="reveal d2">{v.bodyA}</p>
          <p className="reveal d2">{v.bodyB}</p>
          <div className="signature reveal d3">{v.signature}</div>
        </div>
        <div className="reveal d2">
          <div className="img img-reveal">
            <img src={IMG.concept} alt="" loading="lazy" />
          </div>
          <div className="img-cap"><span className="rule"></span>{v.caption}</div>
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Suites({ c }) {
  const v = c.suites;
  const ref = useReveal();
  const imgs = [IMG.suite1, IMG.suite2, IMG.suite3];
  return (
    <section className="section suites" id="suites" ref={ref}>
      <div className="container">
        <div className="suites-head">
          <div>
            <div className="eyebrow reveal">{v.eyebrow}</div>
            <h2 className="reveal d1">{v.title}</h2>
          </div>
          <div className="meta">
            <p className="reveal d2">{v.meta}</p>
          </div>
        </div>
        <div className="suite-list">
          {v.items.map((it, i) => (
            <article className={'suite-card reveal d' + (i+1)} key={i}>
              <div className="img img-reveal"><img src={imgs[i]} alt="" loading="lazy" /></div>
              <div className="num">{it.num}</div>
              <h3>{it.name}</h3>
              <div className="meta-row">
                <span>{it.size}</span>
                <span>·</span>
                <span>{it.view}</span>
                <span>·</span>
                <span>{it.beds}</span>
              </div>
              <p>{it.desc}</p>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Dining({ c }) {
  const v = c.dining;
  const ref = useReveal();
  return (
    <section className="dining" id="dining" ref={ref}>
      <div className="dining-grid">
        <div className="img reveal"><div className="img-reveal" style={{ width: '100%', height: '100%' }}><img src={IMG.dining} alt="" loading="lazy" style={{ width: '100%', height: '100%', objectFit: 'cover' }} /></div></div>
        <div className="copy">
          <div className="eyebrow reveal">{v.eyebrow}</div>
          <h2 className="reveal d1"><L d={0.05}>{v.title1}</L><L d={0.18}><span className="em">{v.title2}</span></L></h2>
          <p className="reveal d2">{v.body}</p>
          <dl className="menu-card reveal d3">
            {v.menu.map((m, i) => (
              <React.Fragment key={i}>
                <dt>{String(i+1).padStart(2, '0')}</dt>
                <dd>
                  <strong style={{ fontWeight: 500 }}>{m.name}</strong>
                  <small>{m.desc} &nbsp;·&nbsp; {m.meta}</small>
                </dd>
              </React.Fragment>
            ))}
          </dl>
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Activities({ c }) {
  const v = c.activities;
  const ref = useReveal();
  return (
    <section className="section activities" id="activities" ref={ref}>
      <div className="container">
        <div className="activities-head">
          <div>
            <div className="eyebrow reveal">{v.eyebrow}</div>
            <h2 className="reveal d1"><L d={0.05}>{v.title}</L></h2>
          </div>
          <div className="meta">
            <p className="reveal d2">{v.meta}</p>
          </div>
        </div>
        <div className="activity-grid">
          {v.items.map((a, i) => (
            <article className={'activity ' + a.span + ' reveal d' + ((i%4)+1)} key={i}>
              <img src={a.img} alt="" loading="lazy" style={a.objectPos ? { objectPosition: a.objectPos } : null} />
              <div className="body">
                <div className="num">{a.num}</div>
                <h3>{a.name}</h3>
                <div className="season">{a.subtitle} &nbsp;·&nbsp; {a.season}</div>
              </div>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Access({ c }) {
  const v = c.access;
  const ref = useReveal();
  return (
    <section className="section access" id="access" ref={ref}>
      <div className="container access-grid">
        <div>
          <div className="eyebrow reveal"><span className="ebar" />{v.eyebrow}</div>
          <h2 className="reveal d1"><L d={0.05}>{v.title1}</L><L d={0.18}><span className="em">{v.title2}</span></L></h2>
          <p className="reveal d2">{v.body}</p>
          <div className="eyebrow reveal d3" style={{ marginTop: 24, color: 'var(--accent)' }}>
            <span className="rule" style={{ marginRight: 12 }}></span>{v.tagline}
          </div>
        </div>
        <div className="journey">
          {v.steps.map((s, i) => (
            <div className={'journey-step reveal d' + (i+1)} key={i}>
              <div style={{ display: 'flex', gap: 16, alignItems: 'baseline' }}>
                <div className="time">{s.time}</div>
                <div className="eyebrow" style={{ fontSize: 9.5 }}>{s.day}</div>
              </div>
              <h4>{s.title}</h4>
              <p>{s.desc}</p>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function Reservation({ c, brandName }) {
  const v = c.reservation;
  const ref = useReveal();
  return (
    <section className="reservation" id="reserve" ref={ref}>
      <div className="container">
        <div className="inner">
          <div>
            <div className="eyebrow reveal" style={{ color: 'rgba(255,255,255,.5)' }}>{v.eyebrow}</div>
            <h2 className="reveal d1"><L d={0.05}>{v.title1}</L><L d={0.18}><span className="em">{v.title2}</span></L></h2>
          </div>
          <div className="booking reveal d2">
            <div className="field"><label>{v.checkin}</label><div className="value">{v.d1}</div></div>
            <div className="field"><label>{v.checkout}</label><div className="value">{v.d2}</div></div>
            <div className="field"><label>{v.guests}</label><div className="value">{v.guestv}</div></div>
            <div className="field"><label>{v.pavilion}</label><div className="value">{v.pavv}</div></div>
            <a className="booking-cta" href="#"><span>{v.cta}</span><span>{v.ctaArrow}</span></a>
          </div>
        </div>

        <div className="colophon">
          <div className="brand">
            <div className="mark">{brandName}</div>
            <p style={{ whiteSpace: 'pre-line' }}>{v.cols[0].body}</p>
          </div>
          {v.cols.slice(1).map((col, i) => (
            <div key={i}>
              <h5>{col.h}</h5>
              <ul>{col.items.map((it, j) => <li key={j}><a href="#">{it}</a></li>)}</ul>
            </div>
          ))}
        </div>
        <div className="smallprint">
          <span>{v.smallA}</span>
          <span>{v.smallB}</span>
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { Nav, LocalTime, Hero, CoordBadge, Marquee, Stats, CountUp, SectionIndex, Concept, Suites, Dining, Activities, Access, Reservation, L });
