// Membership benefits — Soho-House-style full-bleed image grid.
// Three rows of photographic tiles. Each row scrolls INFINITELY in both
// directions (the list is rendered three times and the viewport is kept pinned
// to the middle copy, jumping by one copy-width at the seams) and the tiles
// snap into place when you stop. The middle row is shifted half a tile via a
// transform so it bricks against its neighbours without an empty snap stop.
//
// Image slots: the eight real photographs, scattered at random across the
// tiles on load. Each row shuffles the pool independently and avoids placing
// the same photo in adjacent tiles, so the placements feel organic.
const TABLE_IMAGES = [
  "assets/table-01.jpg",
  "assets/table-02.jpg",
  "assets/table-03.jpg",
  "assets/table-04.jpg",
  "assets/table-05.jpg",
  "assets/table-06.jpg",
  "assets/table-07.jpg",
  "assets/table-08.jpg",
  "assets/table-09.jpg",
  "assets/table-10.jpg",
  "assets/table-11.jpg",
  "assets/table-12.jpg",
  "assets/table-13.jpg",
  "assets/table-14.jpg",
  "assets/table-15.jpg",
  "assets/table-16.jpg",
  "assets/table-17.jpg",
];

const shuffle = (arr) => {
  const a = [...arr];
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
};

// Fill `length` tiles by repeatedly drawing reshuffled copies of the pool,
// never repeating the previous tile's image at a seam.
const pickImages = (length) => {
  const out = [];
  let bag = [];
  for (let i = 0; i < length; i++) {
    if (bag.length === 0) {
      bag = shuffle(TABLE_IMAGES);
      if (out.length && bag[0] === out[out.length - 1] && bag.length > 1) {
        [bag[0], bag[1]] = [bag[1], bag[0]];
      }
    }
    out.push(bag.shift());
  }
  return out;
};

const makeRow = (prefix) => {
  const imgs = pickImages(10);
  return Array.from({ length: 10 }, (_, i) => ({
    id: prefix + (i + 1),
    n: String(i + 1).padStart(2, "0"),
    img: imgs[i],
  }));
};

const BENEFITS_ROWS = [makeRow("a"), makeRow("b"), makeRow("c")];

function BenefitRow({ items, stagger, label }) {
  const trackRef  = React.useRef(null);
  const oneRef    = React.useRef(0);     // width of ONE copy of the list
  const loop = [...items, ...items, ...items];

  // Wrap any scrollLeft back into the middle copy range [one, 2*one).
  const wrap = (x) => {
    const one = oneRef.current;
    if (one <= 0) return x;
    while (x >= 2 * one) x -= one;
    while (x <  one)     x += one;
    return x;
  };

  React.useEffect(() => {
    const el = trackRef.current;
    if (!el) return;

    const measure = () => {
      const first = el.querySelector(".benefit");
      if (!first) return;
      oneRef.current = (first.offsetWidth + 8) * items.length;   // tile + gap
      if (el.scrollLeft < oneRef.current * 0.5 || el.scrollLeft > oneRef.current * 2.5) {
        el.scrollLeft = oneRef.current;                          // pin to middle copy
      }
    };
    requestAnimationFrame(() => { measure(); requestAnimationFrame(measure); });
    const ro = new ResizeObserver(measure);
    ro.observe(el);

    // Seamlessly wrap at the seams. Within the middle copy wrap() is a no-op,
    // so it never fights the snap; it only jumps a whole copy-width (an integer
    // number of tiles, so snap stays aligned) when you cross a boundary.
    let ticking = false;
    const onScroll = () => {
      if (ticking) return;
      ticking = true;
      requestAnimationFrame(() => {
        if (oneRef.current > 0) {
          const wrapped = wrap(el.scrollLeft);
          if (Math.abs(wrapped - el.scrollLeft) > 0.5) el.scrollLeft = wrapped;
        }
        ticking = false;
      });
      if (!isSnapping) scheduleSnap();
    };
    el.addEventListener("scroll", onScroll, { passive: true });

    // Soft snap: after scrolling settles, ease to the nearest tile boundary on
    // this row's OWN grid (a multiple of tile+gap). The middle row's visual
    // half-tile shift comes from a CSS transform, so it snaps to real tiles and
    // never rests on an empty offset.
    let snapTimer = 0, snapRaf = 0, isSnapping = false;
    const stepPx = () => {
      const first = el.querySelector(".benefit");
      return first ? first.offsetWidth + 8 : 380;
    };
    const animateTo = (target) => {
      cancelAnimationFrame(snapRaf);
      const start = el.scrollLeft;
      const dist = target - start;
      if (Math.abs(dist) < 1) return;
      const dur = 280, t0 = performance.now();
      const ease = (p) => 1 - Math.pow(1 - p, 3);
      isSnapping = true;
      const tick = (now) => {
        const p = Math.min(1, (now - t0) / dur);
        el.scrollLeft = start + dist * ease(p);
        if (p < 1) { snapRaf = requestAnimationFrame(tick); }
        else { isSnapping = false; }
      };
      snapRaf = requestAnimationFrame(tick);
    };
    const doSnap = () => {
      if (el.classList.contains("benefits-track--dragging")) return;
      const s = stepPx();
      animateTo(Math.round(el.scrollLeft / s) * s);
    };
    const scheduleSnap = () => {
      if (el.classList.contains("benefits-track--dragging")) return;
      clearTimeout(snapTimer); snapTimer = setTimeout(doSnap, 140);
    };

    // Click-and-drag — mouse + touch — with mid-drag wrap.
    let startX = 0, startScroll = 0, moved = false;
    const down = (e) => {
      moved = false;
      cancelAnimationFrame(snapRaf); isSnapping = false; clearTimeout(snapTimer);
      el.classList.add("benefits-track--dragging");
      startX = (e.touches ? e.touches[0].pageX : e.pageX);
      startScroll = el.scrollLeft;
      window.addEventListener("mousemove", move);
      window.addEventListener("mouseup", up);
      window.addEventListener("touchmove", move, { passive: false });
      window.addEventListener("touchend", up);
    };
    const move = (e) => {
      const x = (e.touches ? e.touches[0].pageX : e.pageX);
      const dx = x - startX;
      if (Math.abs(dx) > 3) moved = true;
      const one = oneRef.current;
      let target = startScroll - dx;
      if (one > 0) {
        while (target >= 2 * one) { target -= one; startScroll -= one; }
        while (target <  one)     { target += one; startScroll += one; }
      }
      el.scrollLeft = target;
      if (e.cancelable && e.touches) e.preventDefault();
    };
    const up = () => {
      el.classList.remove("benefits-track--dragging");
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseup", up);
      window.removeEventListener("touchmove", move);
      window.removeEventListener("touchend", up);
      scheduleSnap();
    };
    const click = (e) => { if (moved) { e.preventDefault(); e.stopPropagation(); } };
    el.addEventListener("mousedown", down);
    el.addEventListener("touchstart", down, { passive: true });
    el.addEventListener("click", click, true);

    return () => {
      ro.disconnect();
      clearTimeout(snapTimer);
      cancelAnimationFrame(snapRaf);
      el.removeEventListener("scroll", onScroll);
      el.removeEventListener("mousedown", down);
      el.removeEventListener("touchstart", down);
      el.removeEventListener("click", click, true);
    };
  }, []);

  const nudge = (dir) => () => {
    const el = trackRef.current;
    if (!el) return;
    const first = el.querySelector(".benefit");
    const step = (first ? first.offsetWidth + 8 : 380);
    el.scrollBy({ left: dir * step, behavior: "smooth" });
  };

  return (
    <div className="benefits-row">
      <button className="benefits-arrow benefits-arrow--prev" onClick={nudge(-1)} aria-label={`previous ${label}`}>
        <Icon name="arrow-left" size={16}/>
      </button>
      <div className={`benefits-track ${stagger ? "benefits-track--stagger" : ""}`} ref={trackRef}>
        <div className="benefits-inner">
          {loop.map((b, i) => (
            <article key={b.id + "-" + i} className={`benefit ${b.img ? "" : "benefit--placeholder"}`}>
              {b.img
                ? <div className="benefit__img" style={{backgroundImage: `url(${b.img})`}}/>
                : <div className="benefit__ph">
                    <span className="benefit__ph-eyebrow">Image</span>
                    <span className="benefit__ph-num">{b.n}</span>
                  </div>}
              <div className="benefit__grad"/>
              {b.text && <div className="benefit__label"><span className="benefit__text">{b.text}</span></div>}
            </article>
          ))}
        </div>
      </div>
      <button className="benefits-arrow benefits-arrow--next" onClick={nudge(1)} aria-label={`next ${label}`}>
        <Icon name="arrow-right" size={16}/>
      </button>
    </div>
  );
}

function Benefits() {
  return (
    <section className="section section--cream" style={{paddingLeft: 0, paddingRight: 0}}>
      <div style={{textAlign: "center", padding: "0 32px 48px"}}>
        <h2 className="h-display">What comes with The Table.</h2>
      </div>
      <div className="benefits">
        <BenefitRow items={BENEFITS_ROWS[0]} label="row one"/>
        <BenefitRow items={BENEFITS_ROWS[1]} label="row two" stagger/>
      </div>
    </section>
  );
}
window.Benefits = Benefits;
