<script setup lang="ts">
import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue'
import { breakpointsTailwind, useDebounce } from '@vueuse/core'
import type { SearchHit } from '~/types'
import InboxActivity from '~/components/inbox/InboxActivity.vue'

const { $i18n } = useNuxtApp();
    const t = $i18n.t;

const PAGE_SIZE = 15
const REQUEST_TIMEOUT_MS = 15000

const query = ref('')
const debouncedQuery = useDebounce(query, 150)
const loading = ref(false)
const loadingMore = ref(false)
const errorMsg = ref<string | null>(null)

// Use the raw Meilisearch client so we can pass an AbortSignal.
const meili = useMeiliSearchRef()

const hits = ref<SearchHit[]>([])
const total = ref(0)

const hasMore = computed(() => hits.value.length < total.value)

let searchSeq = 0
let abortController: AbortController | null = null

async function runSearch(q: string, append = false) {
  // Cancel any in-flight search; saves bandwidth and prevents pile-ups.
  abortController?.abort()
  const ac = new AbortController()
  abortController = ac

  const seq = ++searchSeq
  if (append) loadingMore.value = true
  else loading.value = true
  errorMsg.value = null

  // Safety net in case the network just hangs.
  const timeoutId = setTimeout(() => ac.abort(), REQUEST_TIMEOUT_MS)

  try {
    const res = await meili.index('activities_ES').search(q || '', {
      attributesToRetrieve: ['*'],
      showMatchesPosition: true,
      limit: PAGE_SIZE,
      offset: append ? hits.value.length : 0,
      sort: q ? undefined : ['date:desc']
    }, { signal: ac.signal })

    if (seq !== searchSeq) return

    const newHits = (res?.hits ?? []) as SearchHit[]
    hits.value = append ? hits.value.concat(newHits) : newHits
    total.value = res?.estimatedTotalHits ?? hits.value.length
  } catch (err: unknown) {
    const name = (err as { name?: string })?.name
    // Aborts are expected when the user types fast; don't surface them.
    if (name === 'AbortError') return
    if (seq !== searchSeq) return
    console.error('Meilisearch error', err)
    errorMsg.value = (err as Error)?.message || 'Error al buscar.'
    if (!append) {
      hits.value = []
      total.value = 0
    }
  } finally {
    clearTimeout(timeoutId)
    if (seq === searchSeq) {
      loading.value = false
      loadingMore.value = false
    }
  }
}

function loadMore() {
  if (loadingMore.value || loading.value || !hasMore.value) return
  runSearch(query.value, true)
}

// Initial fetch on the client (avoids blocking SSR).
onMounted(() => runSearch(''))

onBeforeUnmount(() => {
  abortController?.abort()
})

watch(debouncedQuery, (q) => {
  hits.value = []
  total.value = 0
  runSearch(q, false)
})

const selectedActivity = ref<SearchHit | null>(null)

const isActivityPanelOpen = computed({
  get() { return !!selectedActivity.value },
  set(value: boolean) { if (!value) selectedActivity.value = null }
})

watch(hits, () => {
  if (!selectedActivity.value) return
  const stillThere = hits.value.find(h => h._id === selectedActivity.value?._id)
  if (!stillThere) selectedActivity.value = null
})

const breakpoints = useBreakpoints(breakpointsTailwind)
const isMobile = breakpoints.smaller('lg')

useDetailHistory(isActivityPanelOpen, isMobile)

function retry() {
  runSearch(query.value, false)
}
</script>

<template>
  <UDashboardPanel
    id="activities-list"
    :default-size="28"
    :min-size="22"
    :max-size="40"
    resizable
  >
    <UDashboardNavbar :title="t('nav.bible_studies')">
      <template #leading>
        <UDashboardSidebarCollapse />
      </template>

      <template #trailing>
        <UBadge :label="total" variant="subtle" />
      </template>
    </UDashboardNavbar>

    <div class="px-4 sm:px-6 py-3 border-b border-default">
      <UInput
        v-model="query"
        icon="i-lucide-search"
        :placeholder="t('Search.placeholder')"
        :loading="loading"
        size="md"
        class="w-full"
      />
      <p class="mt-1.5 flex items-center gap-1 text-[11px] text-dimmed">
        <UIcon name="i-lucide-lightbulb" class="size-3" />
        <span v-html="t('Search.tip')" />
      </p>
    </div>

    <UAlert
      v-if="errorMsg"
      :title="errorMsg"
      color="error"
      variant="subtle"
      icon="i-lucide-triangle-alert"
      class="mx-4 my-2"
      :actions="[{ label: 'Reintentar', color: 'neutral', variant: 'outline', onClick: retry }]"
    />

    <InboxList
      v-model="selectedActivity"
      :activities="hits"
      :query="debouncedQuery"
      :has-more="hasMore"
      :loading="loading"
      :loading-more="loadingMore"
      collection="activities"
      @load-more="loadMore"
    />
  </UDashboardPanel>

  <InboxActivity
    v-if="selectedActivity"
    :activity="selectedActivity"
    collection="activities"
    :query="debouncedQuery"
    @close="selectedActivity = null"
  />
  <div v-else class="hidden lg:flex flex-1 items-center justify-center">
    <div class="flex flex-col items-center gap-2 text-dimmed">
      <UIcon name="i-lucide-search" class="size-16" />
      <p class="text-sm">
        {{ query ? 'Selecciona una coincidencia para ver el detalle' : 'Escribe arriba para buscar' }}
      </p>
    </div>
  </div>

  <ClientOnly>
    <USlideover v-if="isMobile" v-model:open="isActivityPanelOpen">
      <template #content>
        <InboxActivity
          v-if="selectedActivity"
          :activity="selectedActivity"
          collection="activities"
          :query="debouncedQuery"
          @close="selectedActivity = null"
        />
      </template>
    </USlideover>
  </ClientOnly>
</template>
