<template>
  <div class="fc-gallery-grid">
    <div v-if="store.loading && store.images.length === 0" class="fc-gallery-grid__skeleton">
      <div v-for="i in 12" :key="i" class="fc-gallery-grid__skeleton-item" />
    </div>
    <template v-for="group in store.dateGroups" :key="`${group.year}-${group.month}`">
      <h2 class="fc-gallery-grid__date-header" :id="dateHeaderId(group)">
        {{ monthLabel(group.year, group.month) }}
      </h2>
      <div class="fc-gallery-grid__items">
        <GalleryItem
          v-for="img in imagesForGroup(group)"
          :key="img.id" :image="img" @open="$emit('open', img.id)"
        />
      </div>
    </template>

    <div v-if="store.loading" class="fc-gallery-grid__sentinel">
      <v-progress-circular indeterminate color="accent" size="28" />
    </div>
    <div v-else-if="store.hasMore" ref="sentinelEl" class="fc-gallery-grid__sentinel" />
    <div v-else-if="store.images.length" class="fc-gallery-grid__end text-caption">
      End of gallery.
    </div>

    <v-alert
      v-if="store.error" type="error" variant="tonal" closable class="my-4"
    >
      Failed to load: {{ store.error }}
    </v-alert>
  </div>
</template>

<script setup>
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useGalleryStore } from '../../stores/gallery.js'
import GalleryItem from './GalleryItem.vue'

defineEmits(['open'])

const store = useGalleryStore()
const sentinelEl = ref(null)

let observer = null

function attachObserver() {
  if (observer) observer.disconnect()
  if (!sentinelEl.value) return
  observer = new IntersectionObserver(([entry]) => {
    if (entry.isIntersecting && store.hasMore && !store.loading) {
      store.loadMore()
    }
  }, { rootMargin: '600px' })
  observer.observe(sentinelEl.value)
}

watch(sentinelEl, attachObserver)
onMounted(() => attachObserver())
onUnmounted(() => observer && observer.disconnect())

function dateHeaderId(group) { return `fc-month-${group.year}-${group.month}` }

const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
function monthLabel(y, m) { return `${MONTHS[m - 1]} ${y}` }

function imagesForGroup(group) {
  const ids = new Set(group.image_ids)
  return store.images.filter(i => ids.has(i.id))
}
</script>

<style scoped>
.fc-gallery-grid {
  display: flex;
  flex-direction: column;
}
.fc-gallery-grid__date-header {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 20px;
  font-weight: 500;
  margin: 24px 0 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgb(var(--v-theme-surface-light));
  color: rgb(var(--v-theme-on-surface));
}
.fc-gallery-grid__items {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
}
.fc-gallery-grid__sentinel {
  display: flex; justify-content: center; align-items: center;
  padding: 32px 0;
  min-height: 60px;
}
.fc-gallery-grid__end {
  text-align: center;
  padding: 32px 0;
  color: rgb(var(--v-theme-on-surface-variant, var(--v-theme-on-surface)));
}
.fc-gallery-grid__skeleton {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
}
.fc-gallery-grid__skeleton-item {
  aspect-ratio: 1;
  background: linear-gradient(
    90deg,
    rgb(var(--v-theme-surface)) 0%,
    rgb(var(--v-theme-surface-light)) 50%,
    rgb(var(--v-theme-surface)) 100%
  );
  background-size: 200% 100%;
  animation: fc-shimmer 1.4s infinite;
  border-radius: 4px;
}
@keyframes fc-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@media (max-width: 600px) {
  .fc-gallery-grid__items,
  .fc-gallery-grid__skeleton {
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 4px;
  }
}
</style>
