---
interface Props {
  initial?: number;
  batch?: number;
}
const { initial, batch } = Astro.props;
const dataInitial = initial != null ? String(initial) : undefined;
const dataBatch = batch != null ? String(batch) : undefined;
---

<div
  class="masonry-source"
  data-initial={dataInitial}
  data-batch={dataBatch}
>
  <slot />
</div>

<script>
  const sources = document.querySelectorAll<HTMLDivElement>('.masonry-source');
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  function colCount(): number {
    const w = window.innerWidth;
    if (w <= 520) return 1;
    if (w <= 880) return 2;
    return 3;
  }

  for (const source of sources) {
    const tiles = Array.from(source.children) as HTMLElement[];
    for (const t of tiles) t.classList.add('masonry-tile');

    const initialAttr = source.dataset.initial;
    const batchAttr = source.dataset.batch;
    const progressive = initialAttr !== undefined && batchAttr !== undefined;
    const initial = progressive ? Number(initialAttr) : tiles.length;
    const batch = progressive ? Number(batchAttr) : tiles.length;

    const masonry = document.createElement('div');
    masonry.className = 'masonry';
    source.parentElement?.insertBefore(masonry, source);
    source.remove();

    const sentinel = document.createElement('div');
    sentinel.className = 'masonry-sentinel';
    sentinel.setAttribute('aria-hidden', 'true');
    masonry.parentElement?.insertBefore(sentinel, masonry.nextSibling);

    let cols: HTMLDivElement[] = [];
    let stash: HTMLElement[] = tiles.slice();
    let placed: HTMLElement[] = [];
    let currentCols = colCount();

    function buildColumns() {
      masonry.replaceChildren();
      cols = [];
      for (let i = 0; i < currentCols; i++) {
        const c = document.createElement('div');
        c.className = 'masonry-col';
        masonry.appendChild(c);
        cols.push(c);
      }
    }

    function shortestColumn(): HTMLDivElement {
      let min = Infinity;
      let idx = 0;
      for (let i = 0; i < cols.length; i++) {
        const h = cols[i].offsetHeight;
        if (h < min) { min = h; idx = i; }
      }
      return cols[idx];
    }

    function placeOne(tile: HTMLElement, animate: boolean) {
      if (animate && !prefersReducedMotion) {
        tile.classList.add('revealing');
        tile.addEventListener(
          'animationend',
          () => tile.classList.remove('revealing'),
          { once: true }
        );
      }
      shortestColumn().appendChild(tile);
    }

    function placeBatch(n: number, animate: boolean) {
      const slice = stash.splice(0, n);
      for (const t of slice) {
        placed.push(t);
        placeOne(t, animate);
      }
    }

    function rebuild() {
      stash = placed.concat(stash);
      placed = [];
      buildColumns();
      placeBatch(stash.length, false);
    }

    buildColumns();
    placeBatch(Math.min(initial, stash.length), false);

    if (progressive && stash.length > 0) {
      let busy = false;
      const io = new IntersectionObserver((entries) => {
        if (busy || stash.length === 0) return;
        if (!entries[0].isIntersecting) return;
        busy = true;
        placeBatch(Math.min(batch, stash.length), true);
        setTimeout(() => { busy = false; }, 120);
        if (stash.length === 0) {
          io.disconnect();
          sentinel.remove();
        }
      }, { rootMargin: '600px 0px' });
      io.observe(sentinel);
    } else {
      sentinel.remove();
    }

    let resizeTimer: number | undefined;
    window.addEventListener('resize', () => {
      if (resizeTimer) window.clearTimeout(resizeTimer);
      resizeTimer = window.setTimeout(() => {
        const next = colCount();
        if (next !== currentCols) {
          currentCols = next;
          rebuild();
        }
      }, 150);
    });
  }
</script>

<style is:global>
  /* No-JS fallback: column-count masonry. JS replaces this with .masonry. */
  .masonry-source {
    column-count: 3;
    column-gap: 1rem;
  }
  @media (max-width: 880px) { .masonry-source { column-count: 2; } }
  @media (max-width: 520px) { .masonry-source { column-count: 1; } }
  .masonry-source > * {
    break-inside: avoid;
    display: block;
    margin: 0 0 1rem;
  }

  /* JS-built masonry: real columns, never reflow placed tiles. */
  .masonry {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
  }
  .masonry-col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }
  .masonry-tile.revealing {
    animation: masonry-tile-in 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
    will-change: transform, opacity;
  }
  @keyframes masonry-tile-in {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .masonry-tile.revealing { animation: none; }
  }
  .masonry-sentinel {
    height: 1px;
    margin-top: -1px;
    pointer-events: none;
  }
</style>
