<script setup lang="ts">
import AppLayout from '@/layouts/AppLayout.vue'
import { Head, usePage } from '@inertiajs/vue3'
import { onMounted, ref, computed, watch } from 'vue'

const page = usePage<{ props: { entityId: number } }>()
const entityId = computed(() => (page.props as any).entityId as number)

type Movie = {
  id: number
  title: string
  year: number | null
  rating: string | null
  description: string | null
  poster_url: string | null
  genres?: { id: number; name: string }[]
}

type MoviesPage = {
  data: Movie[]
  total: number
  current_page: number
  last_page: number
}

type ApiResponse = {
  entity: { id: number; name: string }
  movies: MoviesPage
}

const q = ref('')
const sort = ref<'title_asc' | 'title_desc' | 'newest' | 'oldest' | 'year_asc' | 'year_desc'>('title_asc')
const items = ref<Movie[]>([])
const total = ref(0)
const lastPage = ref(1)
const pageNum = ref(1)
const loading = ref(false)
const error = ref<string | null>(null)
const showToast = ref(false)
const toastText = ref('')
const entity = ref<{ id: number; name: string } | null>(null)

let debounceTimer: any = null

const canLoadMore = computed(() => pageNum.value < lastPage.value && !loading.value)

function snippet(text: string | null, wordLimit = 50) {
  if (!text) return ''
  const words = text.split(/\s+/)
  if (words.length <= wordLimit) return text
  return words.slice(0, wordLimit).join(' ') + '…'
}

async function fetchPage(reset = false) {
  if (loading.value) return
  loading.value = true
  error.value = null
  try {
    const params = new URLSearchParams()
    if (q.value) params.set('q', q.value)
    if (sort.value) params.set('sort', sort.value)
    params.set('per_page', '20')
    params.set('page', String(pageNum.value))
    const res = await fetch(`/api/studios/${entityId.value}?${params.toString()}`, {
      headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
      credentials: 'same-origin',
    })
    if (!res.ok) throw new Error(`Fetch failed: ${res.status}`)
    const data: ApiResponse = await res.json()
    entity.value = data.entity
    total.value = data.movies.total
    lastPage.value = data.movies.last_page
    if (reset) items.value = data.movies.data
    else items.value = items.value.concat(data.movies.data)
  } catch (e: any) {
    error.value = e?.message ?? 'Unexpected error'
    toastText.value = error.value
    showToast.value = true
    setTimeout(() => (showToast.value = false), 2500)
  } finally {
    loading.value = false
  }
}

function onSearchInput() {
  clearTimeout(debounceTimer)
  debounceTimer = setTimeout(() => {
    pageNum.value = 1
    fetchPage(true)
  }, 300)
}

function onControlsChange() {
  const url = new URL(window.location.href)
  q.value ? url.searchParams.set('q', q.value) : url.searchParams.delete('q')
  sort.value ? url.searchParams.set('sort', sort.value) : url.searchParams.delete('sort')
  window.history.replaceState({}, '', url.toString())
  pageNum.value = 1
  fetchPage(true)
}

async function loadMore() {
  if (!canLoadMore.value) return
  pageNum.value += 1
  await fetchPage(false)
}

// Infinite scroll via IntersectionObserver
const sentinel = ref<HTMLElement | null>(null)
let observer: IntersectionObserver | null = null

onMounted(() => {
  // hydrate from query
  const url = new URL(window.location.href)
  q.value = url.searchParams.get('q') ?? ''
  sort.value = (url.searchParams.get('sort') as any) ?? 'title_asc'
  fetchPage(true)
  observer = new IntersectionObserver(
    (entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) loadMore()
      }
    },
    { root: null, rootMargin: '200px', threshold: 0 }
  )
  if (sentinel.value) observer.observe(sentinel.value)
})

watch(sentinel, (el, prev) => {
  if (prev && observer) observer.unobserve(prev)
  if (el && observer) observer.observe(el)
})
</script>

<template>
  <Head :title="entity?.name ? `${entity.name} · Studio` : 'Studio'" />
  <AppLayout :breadcrumbs="[{ title: 'Studios', href: '/studios' }, { title: entity?.name ?? '…', href: `/studios/${entityId}` }]">
    <div class="flex h-full flex-1 flex-col gap-4 overflow-x-auto rounded-xl p-4">
      <!-- Loading skeleton for header -->
      <div v-if="!entity && loading" class="flex items-center gap-3">
        <div class="h-12 w-12 shrink-0 rounded bg-muted animate-pulse"></div>
        <div class="h-4 w-40 rounded bg-muted animate-pulse"></div>
      </div>
      <div class="flex items-center gap-3">
        <!-- Neutral placeholder for studio logo -->
        <div class="h-12 w-12 shrink-0 rounded bg-muted"></div>
        <div class="min-w-0 flex-1">
          <h1 class="truncate text-lg font-semibold">{{ entity?.name ?? '…' }}</h1>
        </div>
      </div>

      <div class="flex items-center gap-2">
        <input
          class="w-full rounded-md border border-gray-300 bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary"
          type="text"
          placeholder="Filter movies by title…"
          aria-label="Filter movies by title"
          v-model="q"
          @input="onSearchInput"
        />
        <select v-model="sort" @change="onControlsChange" class="w-44 shrink-0 rounded-md border px-3 py-2 text-sm" aria-label="Sort movies">
          <option value="title_asc">Title A → Z</option>
          <option value="title_desc">Title Z → A</option>
          <option value="newest">Newest</option>
          <option value="oldest">Oldest</option>
          <option value="year_asc">Year ↑</option>
          <option value="year_desc">Year ↓</option>
        </select>
      </div>

      <div v-if="error" class="rounded-md border border-red-300 bg-red-50 p-3 text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-200">
        {{ error }}
      </div>

      <ul class="divide-y divide-muted-foreground/20" :aria-busy="loading ? 'true' : 'false'">
        <li v-for="m in items" :key="m.id" class="py-1">
          <a :href="`/movies/${m.id}`" class="flex gap-3 rounded px-2 py-2 hover:bg-muted/40 focus:bg-muted/40 focus:outline-none">
            <div class="h-20 w-14 shrink-0 overflow-hidden rounded bg-muted">
              <img v-if="m.poster_url" :src="m.poster_url" :alt="`Poster for ${m.title}`" loading="lazy" class="h-full w-full object-cover" />
            </div>
            <div class="flex min-w-0 flex-1 flex-col">
              <div class="flex items-center justify-between gap-2">
                <div class="truncate font-medium">{{ m.title }}</div>
                <div class="text-xs text-muted-foreground">{{ m.year ?? '' }}</div>
              </div>
              <div class="mt-1 text-xs text-muted-foreground">
                <span v-if="m.rating" class="mr-2 rounded border px-1 py-0.5">{{ m.rating }}</span>
                <span v-if="m.genres?.length">{{ m.genres.map(g => g.name).join(', ') }}</span>
              </div>
              <div class="mt-2 text-sm text-foreground/90">{{ snippet(m.description) }}</div>
            </div>
          </a>
        </li>
        <!-- Skeleton rows when loading and list empty -->
        <li v-if="loading && items.length === 0" v-for="n in 5" :key="`s-${n}`" class="py-1">
          <div class="flex gap-3 rounded px-2 py-2">
            <div class="h-20 w-14 shrink-0 overflow-hidden rounded bg-muted animate-pulse"></div>
            <div class="flex min-w-0 flex-1 flex-col gap-2">
              <div class="h-4 w-1/3 rounded bg-muted animate-pulse"></div>
              <div class="h-3 w-1/2 rounded bg-muted animate-pulse"></div>
              <div class="h-3 w-2/3 rounded bg-muted animate-pulse"></div>
            </div>
          </div>
        </li>
      </ul>

      <div ref="sentinel" class="py-6 text-center text-sm text-muted-foreground">
        <template v-if="loading">Loading…</template>
        <template v-else-if="!canLoadMore && items.length > 0">End of results</template>
        <template v-else-if="!items.length && !loading">No results</template>
      </div>
      <transition name="fade">
        <div
          v-if="showToast"
          class="fixed bottom-4 left-1/2 z-50 -translate-x-1/2 rounded bg-foreground px-3 py-2 text-sm text-background shadow-lg"
        >
          {{ toastText }}
        </div>
      </transition>
    </div>
  </AppLayout>
</template>

<style scoped>
.fade-enter-active, .fade-leave-active { transition: opacity .2s }
.fade-enter-from, .fade-leave-to { opacity: 0 }
</style>
