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

type SearchItem = {
  provider: string
  provider_id: string
  title: string | null
  year: string | null
  poster_url: string | null
}

type SearchResponse = {
  results: SearchItem[]
  total: number
  page: number
  has_more: boolean
}

const q = ref('')
const page = ref(1)
const items = ref<SearchItem[]>([])
const total = ref(0)
const hasMore = ref(false)
const loading = ref(false)
const error = ref<string | null>(null)

// Toasts
const showToast = ref(false)
const toastText = ref('')

// Track which provider IDs are currently being accepted to disable buttons
const accepting = ref<Set<string>>(new Set())

// Duplicate modal state
const showDuplicateModal = ref(false)
const duplicateTitle = ref('')
const pendingProviderId = ref('')

let debounceTimer: any = null

const canLoadMore = computed(() => hasMore.value && !loading.value)

async function fetchPage(reset = false) {
  if (loading.value) return
  loading.value = true
  error.value = null
  try {
    const params = new URLSearchParams()
    params.set('q', q.value)
    params.set('page', String(page.value))
    const res = await fetch(`/admin/movies/search?${params.toString()}`, {
      headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
      credentials: 'same-origin',
    })
    if (!res.ok) throw new Error(`Search failed: ${res.status}`)
    const data: SearchResponse = await res.json()
    total.value = data.total
    hasMore.value = data.has_more
    if (reset) {
      items.value = data.results
    } else {
      items.value = items.value.concat(data.results)
    }
  } catch (e: any) {
    error.value = e?.message ?? 'Unexpected error'
  } finally {
    loading.value = false
  }
}

function onSearchInput() {
  // Only trigger search when length is divisible by 3 (3, 6, 9, ...)
  // Per request, we do not hit TMDb otherwise (unless Enter is pressed)
  clearTimeout(debounceTimer)
  const len = q.value.trim().length
  if (len >= 3 && len % 3 === 0) {
    debounceTimer = setTimeout(() => {
      page.value = 1
      fetchPage(true)
    }, 300)
  }
}

function onSearchEnter() {
  // Immediate fetch when user presses Enter regardless of length
  clearTimeout(debounceTimer)
  page.value = 1
  fetchPage(true)
}

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

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

onMounted(() => {
  // Do not auto-fetch on mount; wait for divisible length or Enter key
  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)
})

// Accept flow
async function onAccept(item: SearchItem) {
  try {
    if (accepting.value.has(item.provider_id)) return
    // Check duplicate
    const p = new URLSearchParams({ provider_id: item.provider_id })
    const existsRes = await fetch(`/admin/movies/exists?${p.toString()}`, {
      headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
      credentials: 'same-origin',
    })
    if (!existsRes.ok) throw new Error('Duplicate check failed')
    const existsData = await existsRes.json()
    if (existsData.exists) {
      duplicateTitle.value = item.title ?? 'This movie'
      pendingProviderId.value = item.provider_id
      showDuplicateModal.value = true
      return
    }
    await acceptWithMode(item.provider_id, 'overwrite')
  } catch (e) {
    console.error(e)
    toastText.value = e instanceof Error ? e.message : 'Something went wrong'
    showToast.value = true
    setTimeout(() => (showToast.value = false), 2500)
  }
}

async function acceptWithMode(providerId: string, mode: 'overwrite' | 'duplicate') {
  try {
    accepting.value.add(providerId)
    const res = await fetch('/admin/movies/accept', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-TOKEN': (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement)?.content ?? '',
      },
      body: JSON.stringify({ provider_id: providerId, mode }),
      credentials: 'same-origin',
    })
    if (!res.ok) throw new Error('Accept failed')
  // Optionally refresh first page to reflect addition in local DB-backed UIs later
  // For now, we leave the search results as-is; could show a success message
  try {
    const data = await res.json()
    // Mark accepted in current list if present
    const idx = items.value.findIndex(i => i.provider_id === providerId)
    if (idx !== -1) {
      // attach a transient flag
      ;(items.value as any)[idx]._accepted = true
    }
    toastText.value = mode === 'duplicate' ? 'Saved as duplicate' : 'Accepted (overwrote if existed)'
    showToast.value = true
    setTimeout(() => (showToast.value = false), 2000)
  } catch (_) {}
  } catch (e) {
    console.error(e)
    toastText.value = e instanceof Error ? e.message : 'Accept failed'
    showToast.value = true
    setTimeout(() => (showToast.value = false), 2500)
    throw e
  } finally {
    accepting.value.delete(providerId)
  }
}

function onOverwrite() {
  const pid = pendingProviderId.value
  showDuplicateModal.value = false
  if (pid) acceptWithMode(pid, 'overwrite')
}

function onDuplicate() {
  const pid = pendingProviderId.value
  showDuplicateModal.value = false
  if (pid) acceptWithMode(pid, 'duplicate')
}

function onCancel() {
  showDuplicateModal.value = false
}

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(' ') + '…'
}
</script>

<template>
  <Head title="Movies" />
  <AppLayout :breadcrumbs="[{ title: 'Dashboard', href: '/dashboard' }, { title: 'Movies', href: '/admin/movies' }]">
    <div class="flex h-full flex-1 flex-col gap-4 overflow-x-auto rounded-xl p-4">
      <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="Search movies by title…"
          v-model="q"
          @input="onSearchInput"
          @keyup.enter="onSearchEnter"
        />
      </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">
        <li v-for="item in items" :key="item.provider_id" class="flex gap-3 py-3">
          <div class="h-20 w-14 shrink-0 overflow-hidden rounded bg-muted">
            <img v-if="item.poster_url" :src="item.poster_url" alt="Poster" 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">{{ item.title }}</div>
              <div class="text-xs text-muted-foreground">{{ item.year }}</div>
            </div>
            <!-- We intentionally limit to fields available from OMDb search per your instruction -->
            <div class="mt-2">
              <template v-if="(item as any)._accepted">
                <span class="inline-flex items-center rounded-md border px-3 py-1.5 text-xs">Accepted</span>
              </template>
              <template v-else>
                <button
                  class="inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:opacity-90"
                  :disabled="accepting.has(item.provider_id)"
                  :aria-busy="accepting.has(item.provider_id) ? 'true' : 'false'"
                  @click="onAccept(item)"
                >
                  <span v-if="accepting.has(item.provider_id)">Working…</span>
                  <span v-else>Accept</span>
                </button>
              </template>
            </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="!hasMore && 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>

    <!-- Duplicate modal -->
    <div v-if="showDuplicateModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
      <div class="w-full max-w-md rounded-lg bg-background p-5 shadow-lg">
        <h3 class="text-base font-semibold">{{ duplicateTitle }} is already in our list. How should we proceed?</h3>
        <div class="mt-4 flex justify-end gap-2">
          <button class="rounded-md border px-3 py-1.5 text-sm" @click="onCancel">Cancel</button>
          <button class="rounded-md border px-3 py-1.5 text-sm" @click="onDuplicate">Save as Duplicate</button>
          <button class="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground" @click="onOverwrite">Overwrite</button>
        </div>
      </div>
    </div>
  </AppLayout>

</template>

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