const { Button, Badge } = window.WhiskyNoirTheValleyBarbershop_c4fed7;

// The Last Pour 最後一口 — scroll-scrubbed film hero.
// Canvas + pre-extracted JPEG frames (never <video currentTime>), ImageBitmap
// sliding window so every draw is a GPU blit, lerped playhead for butter.
function ScrubFilm({ onBook, go, lang }) {
  const FRAME_COUNT = 361;
  const T = (zh, en) => lang === 'en' ? en : zh;
  const driverRef = React.useRef(null);
  const canvasRef = React.useRef(null);
  const beatRefs = React.useRef([]);
  const fadeRef = React.useRef(null);
  const [reduced] = React.useState(() => window.matchMedia('(prefers-reduced-motion: reduce)').matches);
  const [loaded, setLoaded] = React.useState(false);
  // The sticky nav occupies flow height, which pushes the 100svh stage partly
  // below the fold at scroll 0 (hero CTAs get clipped). Pull the driver up by
  // the nav's measured height — the film runs under the translucent nav instead.
  const [navH, setNavH] = React.useState(0);
  React.useEffect(() => {
    const nav = document.querySelector('.vb-nav');
    const measure = () => setNavH(nav ? nav.offsetHeight : 0);
    measure();
    window.addEventListener('resize', measure);
    return () => window.removeEventListener('resize', measure);
  }, []);

  React.useEffect(() => {
    if (reduced) return;
    const small = window.matchMedia('(max-width: 768px)').matches;
    const DIR = '../../assets/motion/frames/' + (small ? '640/' : '1280/');
    const pad = n => String(n + 1).padStart(4, '0');
    const canvas = canvasRef.current, ctx = canvas.getContext('2d');
    const images = new Array(FRAME_COUNT);
    const bitmaps = new Map(), decoding = new Set();
    const B_AHEAD = 48, B_KEEP = 64;
    let bmpCenter = -999, current = 0, target = 0, displayed = -1, raf = 0, killed = false;

    function resize() {
      const w = canvas.clientWidth, h = canvas.clientHeight;
      if (canvas.width !== w || canvas.height !== h) { canvas.width = w; canvas.height = h; displayed = -1; }
    }

    // Cover while the crop stays modest; past that, cap the crop instead of
    // letterboxing to a thin strip — a 16:9 film in a portrait phone would
    // otherwise shrink to ~210px of picture in an 812px viewport.
    function fit(bm) {
      const cw = canvas.width, ch = canvas.height;
      const maxCrop = cw < ch ? 0.5 : 0.22;
      const sCover = Math.max(cw / bm.width, ch / bm.height);
      const sContain = Math.min(cw / bm.width, ch / bm.height);
      const crop = 1 - Math.min(cw / (bm.width * sCover), ch / (bm.height * sCover));
      let s = sCover;
      if (crop > maxCrop) {
        s = Math.max(sContain, Math.min(sCover, cw / (bm.width * (1 - maxCrop))));
      }
      const w = bm.width * s, h = bm.height * s;
      ctx.fillStyle = '#0E0D0B'; ctx.fillRect(0, 0, cw, ch);
      ctx.drawImage(bm, (cw - w) / 2, (ch - h) / 2, w, h);
    }

    function nearest(idx) {
      if (bitmaps.has(idx)) return bitmaps.get(idx);
      for (let d = 1; d < FRAME_COUNT; d++) {
        if (bitmaps.has(idx - d)) return bitmaps.get(idx - d);
        if (bitmaps.has(idx + d)) return bitmaps.get(idx + d);
      }
      return null;
    }

    function drawFrame(idx, force) {
      if (idx === displayed && !force) return;
      const bm = nearest(idx);
      if (!bm) return;
      resize(); fit(bm); displayed = idx;
    }

    function ensureBitmaps(center) {
      if (Math.abs(center - bmpCenter) < 3) return;
      bmpCenter = center;
      const lo = Math.max(0, center - 16), hi = Math.min(FRAME_COUNT - 1, center + B_AHEAD);
      for (let i = lo; i <= hi; i++) {
        if (bitmaps.has(i) || decoding.has(i) || !images[i]) continue;
        decoding.add(i);
        createImageBitmap(images[i]).then(b => {
          decoding.delete(i);
          if (killed || Math.abs(i - bmpCenter) > B_KEEP) { b.close(); return; }
          bitmaps.set(i, b);
          if (i === Math.round(current)) drawFrame(i, true);
        }).catch(() => decoding.delete(i));
      }
      for (const k of Array.from(bitmaps.keys()))
        if (k < center - B_KEEP || k > center + B_KEEP) { bitmaps.get(k).close(); bitmaps.delete(k); }
    }

    // concurrency-capped pump: opening run first, then the rest
    let inFlight = 0, next = 0;
    const order = [];
    for (let i = 0; i < 48; i++) order.push(i);
    for (let i = 48; i < FRAME_COUNT; i++) order.push(i);
    function pump() {
      while (inFlight < 10 && next < order.length) {
        const i = order[next++]; inFlight++;
        const img = new Image();
        img.onload = () => { images[i] = img; inFlight--; if (i < 3) setLoaded(true); bmpCenter = -999; pump(); };
        img.onerror = () => { inFlight--; pump(); };
        img.src = DIR + 'f_' + pad(i) + '.jpg';
      }
    }
    pump();

    const beats = [
      { in: -0.1, peak: 0, out: 0.22 },
      { in: 0.4, peak: 0.54, out: 0.7 },
      { in: 0.84, peak: 0.94, out: 2 }
    ];
    function beatAlpha(b, p) {
      if (p < b.in || (b.out <= 1.5 && p > b.out)) return 0;
      if (p < b.peak) return (p - b.in) / Math.max(1e-4, b.peak - b.in);
      if (b.out > 1.5) return 1;
      return 1 - (p - b.peak) / Math.max(1e-4, b.out - b.peak);
    }

    function progress() {
      const r = driverRef.current.getBoundingClientRect();
      return Math.max(0, Math.min(1, -r.top / (r.height - window.innerHeight)));
    }

    function tick() {
      if (killed) return;
      const p = progress();
      target = p * (FRAME_COUNT - 1);
      current += (target - current) * 0.14;
      if (Math.abs(target - current) < 0.4) current = target;
      const idx = Math.round(current);
      ensureBitmaps(idx);
      drawFrame(idx);
      beats.forEach((b, i) => {
        const el = beatRefs.current[i];
        if (!el) return;
        const a = beatAlpha(b, p);
        el.style.opacity = a;
        el.style.transform = 'translateY(' + ((1 - a) * 14) + 'px)';
        el.style.pointerEvents = a > 0.5 ? 'auto' : 'none';
      });
      if (fadeRef.current) fadeRef.current.style.opacity = Math.max(0, (p - 0.92) / 0.08);
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);

    const onResize = () => { resize(); displayed = -1; };
    window.addEventListener('resize', onResize);
    resize();

    const jump = new URLSearchParams(location.search).get('jump');
    if (jump !== null) { history.scrollRestoration = 'manual'; window.scrollTo(0, +jump || 0); }
    const ready = setInterval(() => { if (displayed >= 0) { window.__ready = true; clearInterval(ready); } }, 120);

    return () => {
      killed = true; cancelAnimationFrame(raf); clearInterval(ready);
      window.removeEventListener('resize', onResize);
      bitmaps.forEach(b => b.close()); bitmaps.clear();
    };
  }, [reduced]);

  const heroCopy = (
    <>
      <Badge tone="amber">{T('中山 · 台北 Zhongshan, Taipei', 'Zhongshan, Taipei')}</Badge>
      <h1 style={{ fontFamily: 'var(--font-han)', fontWeight: 'var(--weight-light)', fontSize: 'clamp(30px,5.2vw,72px)', letterSpacing: '.28em', lineHeight: 1.25, color: 'var(--bone-500)', margin: 0, textShadow: '0 1px 22px rgba(0,0,0,.55)' }}>你 來 理 髮<br />我 們 提 供 酒 精</h1>
      <p style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic', fontWeight: 300, fontSize: 'clamp(19px,2.2vw,30px)', color: 'var(--text-accent)', margin: 0, textShadow: '0 1px 16px rgba(0,0,0,.6)' }}>You get a haircut — we provide the drinks.</p>
      <div style={{ display: 'flex', gap: 'var(--space-4)', flexWrap: 'wrap' }}>
        <Button variant="primary" size="lg" onClick={onBook}>{T('立即預約', 'Book now')}</Button>
        <Button variant="secondary" size="lg" onClick={() => go('services')}>{T('服務項目', 'Services')}</Button>
      </div>
    </>
  );

  if (reduced) {
    return (
      <section style={{ position: 'relative', minHeight: '92svh', display: 'grid', alignItems: 'end', overflow: 'hidden', background: 'var(--ink-900)', marginTop: -navH }}>
        <img src="../../assets/motion/last-pour-poster.jpg" alt="" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
        <div style={{ position: 'absolute', inset: 0, background: 'var(--scrim-left)' }} />
        <div style={{ position: 'relative', maxWidth: 'var(--content-max)', width: '100%', margin: '0 auto', padding: '0 var(--gutter-inline) var(--space-10)', display: 'grid', gap: 'var(--space-6)', justifyItems: 'start' }}>{heroCopy}</div>
      </section>
    );
  }

  const beatBase = {
    position: 'absolute', left: 0, right: 0, bottom: 0, opacity: 0,
    padding: '0 var(--gutter-inline) clamp(28px, 6svh, 64px)', pointerEvents: 'none',
    transition: 'none'
  };
  const beatInner = { maxWidth: 'var(--content-max)', margin: '0 auto', display: 'grid', gap: 'var(--space-5)', justifyItems: 'start', position: 'relative' };
  const beatScrim = { content: '""', position: 'absolute', zIndex: -1, inset: '-60% -20% -40% -12%', background: 'radial-gradient(60% 62% at 24% 55%, rgba(6,3,1,.66), rgba(6,3,1,.36) 48%, transparent 78%)' };

  return (
    <div ref={driverRef} style={{ position: 'relative', height: window.matchMedia('(max-width: 768px)').matches ? '340svh' : '420vh', background: 'var(--ink-900)', marginTop: -navH }}>
      <div style={{ position: 'sticky', top: 0, height: '100svh', overflow: 'hidden' }}>
        <canvas ref={canvasRef} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: loaded ? 1 : 0, transition: 'opacity .6s var(--ease-reveal, ease-out)' }} />
        <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', background: 'radial-gradient(120% 90% at 50% 45%, transparent 60%, rgba(8,5,2,.42))' }} />

        <div ref={el => beatRefs.current[0] = el} style={beatBase}>
          <div style={beatInner}><span style={beatScrim} />{heroCopy}</div>
        </div>

        <div ref={el => beatRefs.current[1] = el} style={{ ...beatBase, bottom: 'auto', top: '50%', transform: 'translateY(-50%)' }}>
          <div style={beatInner}>
            <span style={beatScrim} />
            <p style={{ font: 'var(--type-eyebrow)', letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase', color: 'var(--text-metal)', margin: 0 }}>The Last Pour</p>
            <h2 style={{ fontFamily: 'var(--font-han)', fontWeight: 'var(--weight-light)', fontSize: 'clamp(24px,3.4vw,50px)', letterSpacing: '.22em', lineHeight: 1.45, color: 'var(--bone-500)', margin: 0, textShadow: '0 1px 22px rgba(0,0,0,.55)' }}>醉漢理髮店，是屬於每一個<br />「努力生活的男人」的避風港</h2>
            <p style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic', fontWeight: 300, fontSize: 'clamp(17px,2vw,25px)', color: 'var(--text-accent)', margin: 0 }}>A safe harbor for every man who works hard at life.</p>
          </div>
        </div>

        <div ref={el => beatRefs.current[2] = el} style={beatBase}>
          <div style={beatInner}>
            <span style={beatScrim} />
            <p style={{ fontFamily: 'var(--font-han)', fontWeight: 300, fontSize: 'clamp(20px,2.6vw,34px)', letterSpacing: '.2em', color: 'var(--bone-500)', margin: 0, textShadow: '0 1px 22px rgba(0,0,0,.55)' }}>{T('先 坐 下 ， 酒 已 經 倒 好 了', 'Sit down — the pour is ready.')}</p>
            <div style={{ display: 'flex', gap: 'var(--space-4)', flexWrap: 'wrap' }}>
              <Button variant="primary" size="lg" onClick={onBook}>{T('立即預約', 'Book now')}</Button>
              <Button variant="ghost" onClick={() => go('services')}>{T('查看服務', 'See services')}</Button>
            </div>
          </div>
        </div>

        <div ref={fadeRef} style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0, background: 'linear-gradient(to bottom, transparent 30%, var(--ink-900))' }} />
      </div>
    </div>
  );
}
Object.assign(window, { ScrubFilm });
