<template>
  <v-card
    class="fc-gallery-item" elevation="0"
    :class="{ 'fc-gallery-item--selected': selected }"
    @click="onClick"
    :aria-label="`Image ${image.id}`" tabindex="0"
    @keydown.enter="onClick"
    @keydown.space.prevent="onClick"
  >
    <div class="fc-gallery-item__media">
      <img
        v-if="!isVideo" ref="imgEl" :src="image.thumbnail_url"
        :alt="`Image ${image.id}`" loading="lazy"
        :class="{ 'is-loaded': loaded }"
        @load="loaded = true" @error="onThumbError"
      >
      <div v-else class="fc-gallery-item__video-thumb">
        <img
          ref="imgEl" :src="image.thumbnail_url" :alt="`Video ${image.id}`"
          loading="lazy" :class="{ 'is-loaded': loaded }" @load="loaded = true"
        >
        <v-icon class="fc-gallery-item__video-badge" icon="mdi-play-circle" />
      </div>
      <div v-if="thumbError" class="fc-gallery-item__placeholder">
        <v-icon size="32" icon="mdi-image-broken-variant" />
      </div>
      <template v-if="sel.isSelectMode">
        <div class="fc-gallery-item__checkbox" :class="{ on: selected }">
          <v-icon v-if="selected" size="16" icon="mdi-check" />
        </div>
        <div v-if="selected" class="fc-gallery-item__order">{{ orderNum }}</div>
      </template>
      <div v-if="image.artist" class="fc-gallery-item__artist">
        <RouterLink
          v-if="!sel.isSelectMode"
          :to="`/artist/${image.artist.slug}`"
          @click.stop
        >{{ image.artist.name }}</RouterLink>
        <span v-else>{{ image.artist.name }}</span>
      </div>
    </div>
  </v-card>
</template>

<script setup>
import { computed, onMounted, ref } from 'vue'
import { useGallerySelectionStore } from '../../stores/gallerySelection.js'

const props = defineProps({ image: { type: Object, required: true } })
const emit = defineEmits(['open'])

const sel = useGallerySelectionStore()
const thumbError = ref(false)

// Reveal each tile when ITS OWN thumbnail finishes loading (`@load`), not
// when its metadata batch lands — so tiles fade/flip in individually in
// load order instead of popping in together (operator-flagged 2026-06-04).
const loaded = ref(false)
const imgEl = ref(null)
onMounted(() => {
  // A cached thumbnail can already be complete before @load binds; reflect
  // that so the tile reveals instead of sitting invisible at opacity 0.
  const el = imgEl.value
  if (el && el.complete && el.naturalWidth > 0) loaded.value = true
})
const isVideo = computed(
  () => props.image.mime && props.image.mime.startsWith('video/')
)
const selected = computed(() => sel.isSelected(props.image.id))
const orderNum = computed(() => sel.order.indexOf(props.image.id) + 1)

function onClick() {
  if (sel.isSelectMode) sel.toggle(props.image.id)
  else emit('open', props.image.id)
}
function onThumbError() { thumbError.value = true }
</script>

<style scoped>
.fc-gallery-item {
  cursor: pointer;
  overflow: hidden;
  background: rgb(var(--v-theme-surface));
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.fc-gallery-item:hover, .fc-gallery-item:focus-visible {
  transform: translateY(-1px);
  outline: 2px solid rgb(var(--v-theme-accent));
}
.fc-gallery-item--selected {
  outline: 3px solid rgb(var(--v-theme-accent));
  outline-offset: -3px;
}
.fc-gallery-item__media {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  background: rgb(var(--v-theme-surface-light));
}
.fc-gallery-item__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  opacity: 0;
}
/* Entrance: each thumbnail flips up out of a slight backward tilt and
   settles into place, played WHEN ITS OWN image finishes loading (the
   `is-loaded` class) rather than on a batch/timer — so tiles cascade in
   natural load order instead of popping in together. Mirrors the showcase
   MasonryGrid entrance styling (operator-flagged 2026-06-04). */
.fc-gallery-item__media img.is-loaded {
  animation: fc-gallery-item-in 0.5s cubic-bezier(0.34, 1.45, 0.64, 1) both;
}
@keyframes fc-gallery-item-in {
  0% {
    opacity: 0;
    transform: perspective(1000px) rotateX(-22deg) translateY(20px) scale(0.96);
  }
  55% { opacity: 1; }
  100% {
    opacity: 1;
    transform: perspective(1000px) rotateX(0) translateY(0) scale(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  .fc-gallery-item__media img { opacity: 1; }
  .fc-gallery-item__media img.is-loaded { animation: none; }
}
.fc-gallery-item__video-thumb { position: relative; height: 100%; }
.fc-gallery-item__video-badge {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  color: rgba(232, 228, 216, 0.85);
  font-size: 36px;
  pointer-events: none;
}
.fc-gallery-item__placeholder {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgb(var(--v-theme-surface-light));
}
.fc-gallery-item__checkbox {
  position: absolute; top: 8px; left: 8px;
  width: 22px; height: 22px; border-radius: 4px;
  border: 2px solid rgba(232, 228, 216, 0.8);
  background: rgba(var(--v-theme-background), 0.45);
  display: grid; place-items: center;
  color: rgb(var(--v-theme-background)); z-index: 11;
}
.fc-gallery-item__checkbox.on {
  background: rgb(var(--v-theme-accent));
  border-color: rgb(var(--v-theme-accent));
}
.fc-gallery-item__order {
  position: absolute; top: 6px; right: 6px;
  min-width: 22px; height: 22px; padding: 0 5px;
  border-radius: 11px;
  background: rgb(var(--v-theme-accent));
  color: rgb(var(--v-theme-background)); font-size: 12px; font-weight: 700;
  display: grid; place-items: center; z-index: 11;
  pointer-events: none;
}
.fc-gallery-item__artist {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 14px 8px 6px;
  background: linear-gradient(
    to top, rgba(var(--v-theme-background), 0.78),
    rgba(var(--v-theme-background), 0)
  );
  font-size: 12px; line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
}
.fc-gallery-item__artist a {
  color: rgb(var(--v-theme-parchment, 232 228 216));
  text-decoration: none;
  pointer-events: auto;
}
.fc-gallery-item__artist span { color: rgba(232, 228, 216, 0.85); }
</style>
