// folio/Grid.jsx — asymmetric editorial grid with scroll reveal,
// hover effects, and per-breakpoint span patterns.

const FOLIO_PATTERNS = {
  // Each row sums to the cols count. CSS picks per breakpoint via custom props.
  asymmetric: {
    lg: [7, 5, 4, 4, 4, 5, 7, 4, 8, 4, 4, 4],   // 12 cols
    md: [6, 3, 3, 4, 2, 3, 3, 2, 4, 6, 3, 3],   // 6 cols
    sm: [2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1],   // 2 cols
  },
  balanced: {
    lg: [6, 6, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6],
    md: [3, 3, 2, 2, 2, 3, 3, 2, 2, 2, 3, 3],
    sm: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  },
  mosaic: {
    lg: [8, 4, 4, 4, 4, 4, 4, 8, 6, 6, 4, 4],
    md: [4, 2, 2, 2, 2, 2, 2, 4, 3, 3, 2, 2],
    sm: [2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1],
  },
};

function classes(...xs) { return xs.filter(Boolean).join(" "); }

function FolioGrid({ images, layout, captionMode, density, dimOthers, showSerials, onOpen, expandedId, showAll, onReveal, revealLimit = 5 }) {
  const rootRef = React.useRef(null);

  // Scroll-reveal: tiles in viewport are shown immediately on mount;
  // tiles below the fold fade in as they scroll into view.
  React.useEffect(() => {
    const root = rootRef.current;
    if (!root) return;
    const tiles = Array.from(root.querySelectorAll(".folio-tile"));

    const revealVisible = () => {
      const vh = window.innerHeight || document.documentElement.clientHeight;
      tiles.forEach((el) => {
        if (el.hasAttribute("data-in")) return;
        const r = el.getBoundingClientRect();
        if (r.top < vh - 40 && r.bottom > 0) el.setAttribute("data-in", "");
      });
    };
    const raf = requestAnimationFrame(revealVisible);

    let io;
    if (typeof IntersectionObserver !== "undefined") {
      io = new IntersectionObserver((entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.setAttribute("data-in", "");
            io.unobserve(e.target);
          }
        });
      }, { rootMargin: "0px 0px -8% 0px", threshold: 0.05 });
      tiles.forEach((el) => io.observe(el));
    }

    // Failsafe: force all tiles visible after 600ms
    const failsafe = setTimeout(() => {
      tiles.forEach((el) => el.setAttribute("data-in", ""));
    }, 600);

    const onScroll = () => revealVisible();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);

    return () => {
      cancelAnimationFrame(raf);
      clearTimeout(failsafe);
      if (io) io.disconnect();
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [images, layout]);

  // Reveal animation: when showAll becomes true, stagger-animate the beyond tiles
  // using direct DOM manipulation so the animation runs in the same frame the
  // tiles become visible (avoids the React ref-timing gap).
  const prevShowAll = React.useRef(false);
  React.useEffect(() => {
    if (showAll && !prevShowAll.current) {
      // Small rAF so the browser has painted the newly-visible tiles first
      requestAnimationFrame(() => {
        const root = rootRef.current;
        if (!root) return;
        const beyondTiles = Array.from(root.querySelectorAll(".folio-tile.beyond"));
        beyondTiles.forEach((el, i) => {
          el.style.animationDelay = `${i * 75}ms`;
          el.classList.add("just-revealed");
          // Clean up after animation is done
          const duration = 900 + i * 75;
          setTimeout(() => {
            el.classList.remove("just-revealed");
            el.style.animationDelay = "";
          }, duration);
        });
      });
    }
    prevShowAll.current = showAll;
  }, [showAll]);

  const pattern = FOLIO_PATTERNS[layout] || FOLIO_PATTERNS.asymmetric;

  return (
    <div
      ref={rootRef}
      className={classes("folio-grid",
        `density-${density}`,
        captionMode === "margin" && "caption-margin",
        dimOthers && "dim-others",
        showSerials && "show-serials",
        (showAll || images.length <= revealLimit) && "show-all",
      )}>
      {images.length === 0 && (
        <div className="folio-empty">— nothing here yet —</div>
      )}
      {images.map((img, i) => {
        const lgSpan = pattern.lg[i % pattern.lg.length];
        const mdSpan = pattern.md[i % pattern.md.length];
        const smSpan = pattern.sm[i % pattern.sm.length];
        const big = lgSpan >= 7;
        const isExpanding = expandedId === img.id;
        const isBeyond = i >= revealLimit;

        const tile = (
          <article
            key={img.id}
            className={classes("folio-tile",
              isExpanding && "expanding",
              isBeyond && "beyond",
            )}
            data-tile-id={img.id}
            data-cat={img.cat}
            style={{
              "--span": lgSpan,
              "--span-md": mdSpan,
              "--span-sm": smSpan,
              "--r": img.ratio,
              "--focal": img.focal,
              "--i": Math.min(i % revealLimit, 6),
              gridColumn: `span ${lgSpan}`,
            }}
            onClick={() => onOpen && onOpen(i)}>
            <span className="serial" aria-hidden>{String(i + 1).padStart(2, "0")}</span>
            <div className="frame">
              <FolioPhoto img={img} big={big} eager={i < 2} />
              <div className="veil" aria-hidden />
              <div className="caption">
                <div className="no">{img.no} · {img.cat}</div>
                <div className="title">{img.title}</div>
              </div>
            </div>
          </article>
        );

        // Insert the "reveal the rest" banner after the limit-th tile
        const showRevealHere = i === revealLimit - 1
          && !showAll
          && images.length > revealLimit;
        if (showRevealHere) {
          return (
            <React.Fragment key={img.id}>
              {tile}
              <div className="folio-reveal" key="__reveal">
                <button className="btn" onClick={onReveal}>
                  Reveal the rest
                  <span className="arrow">↓</span>
                </button>
              </div>
            </React.Fragment>
          );
        }
        return tile;
      })}
    </div>
  );
}

Object.assign(window, { FolioGrid, FOLIO_PATTERNS });
