<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { breakpointsTailwind } from '@vueuse/core'
import type { DropdownMenuItem } from '@nuxt/ui'
import type { SearchHit } from '~/types'
import { useFavoritesStore, type FavoriteItem } from '~/stores/favorites'
import PublicationDetail from '~/components/PublicationDetail.vue'
import EntrelineaDetail from '~/components/entrelineas/EntrelineaDetail.vue'

const favorites = useFavoritesStore()
// Refs reactivos para usar en watchers / template (Pinia setup store).
const { items: favItems, total: favTotal, collections: favCollections } = storeToRefs(favorites)
const toast = useToast()
const { locale } = useI18n()
const { documentsApi } = useTypesenseApi()

const TYPESENSE_DOCUMENTS = 'documents'
const TYPESENSE_PARAGRAPHS = 'paragraphs'

/** Identificador de la colección de entrelíneas en el store de favoritos.
 *  Debe coincidir con `FAVORITES_COLLECTION` en `pages/entrelineas.vue`. */
const ENTRELINEAS_COLLECTION = 'entrelineas'

interface ParagraphDoc {
  id?: string
  document_id: string
  text: string
  number: number
  locale: string
  type: string
}

interface TypesenseParagraphHit {
  document: ParagraphDoc
  highlights?: unknown[]
  highlight?: Record<string, unknown>
}

interface DocumentDoc {
  id?: string
  code: string
  locale: string
  type: string
  title: string
  timestamp: number
  date: string
  place?: string
  city?: string
  state?: string
  country?: string
  thumbnail?: string
  files?: { youtube?: string; video?: string; audio?: string; booklet?: string; simple?: string }
  slug?: string
  body?: string
  draft?: boolean
  [key: string]: unknown
}

const detailDocument = ref<DocumentDoc | null>(null)
const detailDocumentLoading = ref(false)
const detailParagraphs = ref<TypesenseParagraphHit[]>([])
const detailParagraphsLoading = ref(false)

async function fetchDetailDocument(hit: SearchHit) {
  const docId = String(hit._id || hit.id || '')
  if (!docId) return
  detailDocumentLoading.value = true
  detailDocument.value = null
  try {
    const res = await documentsApi.multiSearch({
      multiSearchParameters: {},
      multiSearchSearchesParameter: {
        searches: [{ collection: TYPESENSE_DOCUMENTS, q: '*', queryBy: 'title', filterBy: `id:=${docId}`, perPage: 1, page: 1 }]
      }
    })
    const hits = (res?.results?.[0] as { hits?: Array<{ document: DocumentDoc }> })?.hits ?? []
    detailDocument.value = hits[0]?.document ?? null
  } catch (err) {
    console.error('Error fetching document', err)
    detailDocument.value = null
  } finally {
    detailDocumentLoading.value = false
  }
}

async function fetchDetailParagraphs(hit: SearchHit) {
  const docId = String(hit._id || hit.id || '')
  const type = hit.type as string
  if (!docId || !type) return
  detailParagraphsLoading.value = true
  detailParagraphs.value = []
  const PER_PAGE = 250
  let page = 1
  let totalFound = 0
  const all: Array<{ document: ParagraphDoc }> = []
  try {
    do {
      const res = await documentsApi.multiSearch({
        multiSearchParameters: {},
        multiSearchSearchesParameter: {
          searches: [{
            collection: TYPESENSE_PARAGRAPHS,
            q: '*',
            queryBy: '',
            filterBy: `document_id:=${docId} && locale:=${locale.value} && type:=${type}`,
            perPage: PER_PAGE,
            page,
            sortBy: 'number:asc'
          }]
        }
      })
      const result = (res?.results?.[0] as { found?: number; hits?: Array<{ document: ParagraphDoc }> } | undefined)
      if (!result) break
      if (page === 1) totalFound = result.found ?? 0
      all.push(...(result.hits ?? []))
      page++
    } while (all.length < totalFound)
    detailParagraphs.value = all.map(h => ({ document: h.document }))
  } catch (err) {
    console.error('Error fetching paragraphs', err)
    detailParagraphs.value = []
  } finally {
    detailParagraphsLoading.value = false
  }
}

// Nota: la URL de ImageKit y los presets de transformación viven en
// `~/utils/entrelineaImage.ts` (auto-importado). EntrelineaDetail los usa
// internamente, así que aquí no hay que pasar nada relacionado a ImageKit.

const COLLECTION_LABELS: Record<string, string> = {
  activities: 'Actividades',
  conferences: 'Conferencias',
  entrelineas: 'Entre Líneas'
}

function labelFor(c: string): string {
  return COLLECTION_LABELS[c] || c.charAt(0).toUpperCase() + c.slice(1)
}

// Filtros: pestaña por colección o "todos".
const activeCollection = ref<string>('all')

// Reset del filtro si la colección activa se queda sin elementos.
watch(favCollections, (cols) => {
  if (activeCollection.value !== 'all' && !cols.includes(activeCollection.value)) {
    activeCollection.value = 'all'
  }
})

const tabs = computed(() => {
  const items = [{ value: 'all', label: `Todos (${favTotal.value})`, icon: 'i-lucide-bookmark' }]
  for (const c of favCollections.value) {
    const count = favItems.value.filter(it => it.collection === c).length
    items.push({
      value: c,
      label: `${labelFor(c)} (${count})`,
      icon: c === 'activities'
        ? 'i-lucide-calendar-days'
        : c === 'conferences'
          ? 'i-lucide-mic'
          : c === 'entrelineas'
            ? 'i-lucide-book-open'
            : 'i-lucide-folder'
    })
  }
  return items
})

const localQuery = ref('')

const filteredItems = computed<FavoriteItem[]>(() => {
  let list = favItems.value
  if (activeCollection.value !== 'all') {
    list = list.filter(it => it.collection === activeCollection.value)
  }
  const q = localQuery.value.trim().toLowerCase()
  if (q) {
    list = list.filter((it) => {
      const t = (it.hit?.title || '').toLowerCase()
      return t.includes(q)
    })
  }
  return list
})

// ---- Selección y panel de detalle --------------------------------------

const selected = ref<FavoriteItem | null>(null)

const selectedHit = computed<SearchHit | null>(() => selected.value?.hit ?? null)
const selectedCollection = computed<string | undefined>(() => selected.value?.collection)

/** Las entrelíneas tienen un detalle distinto al de actividades/conferencias
 *  (imagen de ImageKit + texto), así que cambiamos el componente según la
 *  colección del item seleccionado. */
const isEntrelinea = computed(() => selectedCollection.value === ENTRELINEAS_COLLECTION)

/** El SearchHit guardado de una entrelínea conserva todos los campos del
 *  documento original (`id`, `image`, `link`, `locale`, `page`, `text`)
 *  porque al guardar hicimos `{ ...doc, _id, title, body }`. Lo casteamos a
 *  un objeto plano para pasárselo a `EntrelineaDetail`. */
const selectedEntrelineaDoc = computed<Record<string, unknown> | null>(() => {
  if (!isEntrelinea.value || !selectedHit.value) return null
  return selectedHit.value as unknown as Record<string, unknown>
})

const isPanelOpen = computed({
  get() { return !!selected.value },
  set(v: boolean) { if (!v) selected.value = null }
})

// Si el item seleccionado se quita de favoritos desde otra pestaña o desde el
// botón del propio detalle, cerrar el panel.
watch(favItems, (items) => {
  if (!selected.value) return
  const stillThere = items.find(it =>
    it.collection === selected.value!.collection
    && String(it._id) === String(selected.value!._id))
  if (!stillThere) selected.value = null
})

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

watch(selected, (item) => {
  if (!item || item.collection === ENTRELINEAS_COLLECTION) {
    detailDocument.value = null
    detailParagraphs.value = []
    return
  }
  fetchDetailDocument(item.hit)
  fetchDetailParagraphs(item.hit)
})

// ---- Helpers de fila ---------------------------------------------------

function safeDate(hit: SearchHit) {
  const d = hit.date ?? hit.isodate
  if (!d) return ''
  const ts = typeof d === 'string' ? new Date(d).getTime() : (d as number) * 1000
  if (!Number.isFinite(ts)) return ''
  return formatDate(ts / 1000)
}

function hasDate(hit: SearchHit) {
  return hit.date != null || hit.isodate != null
}

function removeItem(it: FavoriteItem, ev: Event) {
  ev.stopPropagation()
  favorites.remove(it.collection, it._id)
  toast.add({
    title: 'Eliminado de tu lista',
    description: it.hit?.title,
    icon: 'i-lucide-bookmark-x',
    color: 'neutral',
    duration: 1500
  })
}

// ---- Exportar / Compartir ----------------------------------------------

async function copyList() {
  if (!favTotal.value) return
  const ok = await favorites.copyJsonToClipboard()
  toast.add({
    title: ok ? 'Lista copiada al portapapeles' : 'No se pudo copiar la lista',
    description: ok ? 'Pégala en otro lugar para compartirla.' : 'Tu navegador bloqueó el acceso al portapapeles.',
    icon: ok ? 'i-lucide-clipboard-check' : 'i-lucide-triangle-alert',
    color: ok ? 'primary' : 'error',
    duration: 2400
  })
}

function downloadList() {
  if (!favTotal.value) return
  favorites.downloadJson()
  toast.add({
    title: 'Descargando tu lista',
    icon: 'i-lucide-download',
    color: 'primary',
    duration: 1500
  })
}

const showClearConfirm = ref(false)
function confirmClear() {
  favorites.clear()
  showClearConfirm.value = false
  toast.add({
    title: 'Lista vacía',
    icon: 'i-lucide-trash-2',
    color: 'neutral',
    duration: 1500
  })
}

// ---- Importar ----------------------------------------------------------

const showImport = ref(false)
const importMode = ref<'merge' | 'replace'>('merge')
const importText = ref('')
const importFileInput = ref<HTMLInputElement | null>(null)
const importing = ref(false)

function openImport() {
  importText.value = ''
  importMode.value = 'merge'
  showImport.value = true
}

function pickImportFile() {
  importFileInput.value?.click()
}

async function onImportFile(ev: Event) {
  const input = ev.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) return
  importing.value = true
  try {
    const result = await favorites.importFromFile(file, importMode.value)
    toast.add({
      title: `Importados ${result.added}`,
      description: result.skipped
        ? `${result.skipped} ya estaban en tu lista${result.invalid ? `, ${result.invalid} inválidos` : ''}.`
        : result.invalid
          ? `${result.invalid} inválidos.`
          : 'Tu lista se actualizó.',
      icon: 'i-lucide-bookmark-plus',
      color: 'primary',
      duration: 2800
    })
    showImport.value = false
  } catch (e) {
    toast.add({
      title: 'No se pudo importar',
      description: (e as Error)?.message || 'Archivo inválido.',
      icon: 'i-lucide-triangle-alert',
      color: 'error',
      duration: 3200
    })
  } finally {
    importing.value = false
    input.value = ''
  }
}

function applyTextImport() {
  if (!importText.value.trim()) return
  importing.value = true
  try {
    const result = favorites.importFromJson(importText.value, importMode.value)
    toast.add({
      title: `Importados ${result.added}`,
      description: result.skipped
        ? `${result.skipped} ya estaban en tu lista${result.invalid ? `, ${result.invalid} inválidos` : ''}.`
        : result.invalid
          ? `${result.invalid} inválidos.`
          : 'Tu lista se actualizó.',
      icon: 'i-lucide-bookmark-plus',
      color: 'primary',
      duration: 2800
    })
    showImport.value = false
  } catch (e) {
    toast.add({
      title: 'No se pudo importar',
      description: (e as Error)?.message || 'JSON inválido.',
      icon: 'i-lucide-triangle-alert',
      color: 'error',
      duration: 3200
    })
  } finally {
    importing.value = false
  }
}

// Menú compacto para móvil — los 4 iconos sueltos del navbar son ambiguos sin
// tooltip (el tooltip no aparece en touch), así que en pantallas chicas los
// agrupamos en un dropdown con etiquetas explícitas.
const mobileActions = computed<DropdownMenuItem[][]>(() => [[
  {
    label: 'Importar lista',
    icon: 'i-lucide-upload',
    onSelect: () => openImport()
  },
  {
    label: 'Copiar al portapapeles',
    icon: 'i-lucide-clipboard-copy',
    disabled: !favTotal.value,
    onSelect: () => copyList()
  },
  {
    label: 'Descargar (.json)',
    icon: 'i-lucide-download',
    disabled: !favTotal.value,
    onSelect: () => downloadList()
  }
], [
  {
    label: 'Vaciar mi lista',
    icon: 'i-lucide-trash-2',
    color: 'error',
    disabled: !favTotal.value,
    onSelect: () => { showClearConfirm.value = true }
  }
]])
</script>

<template>
  <UDashboardPanel
    id="favorites-list"
    :default-size="32"
    :min-size="24"
    :max-size="44"
    resizable
  >
    <UDashboardNavbar title="Mi lista">
      <template #leading>
        <UDashboardSidebarCollapse />
      </template>

      <template #trailing>
        <UBadge v-if="favTotal" :label="favTotal" variant="subtle" />
      </template>

      <template #right>
        <!-- Desktop: iconos individuales (rápidos y con tooltip) -->
        <div class="hidden sm:flex items-center gap-0.5">
          <UTooltip text="Importar lista">
            <UButton
              icon="i-lucide-upload"
              color="neutral"
              variant="ghost"
              size="sm"
              aria-label="Importar lista"
              @click="openImport"
            />
          </UTooltip>
          <UTooltip text="Copiar lista al portapapeles">
            <UButton
              icon="i-lucide-clipboard-copy"
              color="neutral"
              variant="ghost"
              size="sm"
              :disabled="!favTotal"
              aria-label="Copiar lista"
              @click="copyList"
            />
          </UTooltip>
          <UTooltip text="Descargar lista (.json)">
            <UButton
              icon="i-lucide-download"
              color="neutral"
              variant="ghost"
              size="sm"
              :disabled="!favTotal"
              aria-label="Descargar lista"
              @click="downloadList"
            />
          </UTooltip>
          <UTooltip text="Vaciar la lista">
            <UButton
              icon="i-lucide-trash-2"
              color="error"
              variant="ghost"
              size="sm"
              :disabled="!favTotal"
              aria-label="Vaciar lista"
              @click="showClearConfirm = true"
            />
          </UTooltip>
        </div>

        <!-- Móvil: un único botón con menú etiquetado -->
        <UDropdownMenu :items="mobileActions" :content="{ align: 'end' }" class="sm:hidden">
          <UButton
            icon="i-lucide-ellipsis-vertical"
            color="neutral"
            variant="ghost"
            size="sm"
            aria-label="Más acciones"
          />
        </UDropdownMenu>
      </template>
    </UDashboardNavbar>

    <div class="px-4 sm:px-6 py-3 border-b border-default flex flex-col gap-2.5">
      <UInput
        v-model="localQuery"
        icon="i-lucide-search"
        placeholder="Filtrar mi lista por título..."
        size="md"
        class="w-full"
      />
      <!-- Filtros por colección. En móvil van con scroll horizontal para que
           los botones siempre aparezcan completos en una sola línea. -->
      <div class="-mx-4 sm:mx-0 px-4 sm:px-0 flex gap-1.5 overflow-x-auto pb-0.5 scrollbar-thin">
        <UButton
          v-for="t in tabs"
          :key="t.value"
          :icon="t.icon"
          :label="t.label"
          size="xs"
          class="shrink-0"
          :color="activeCollection === t.value ? 'primary' : 'neutral'"
          :variant="activeCollection === t.value ? 'soft' : 'ghost'"
          @click="activeCollection = t.value"
        />
      </div>
    </div>

    <div class="overflow-y-auto divide-y divide-default flex-1">
      <div
        v-if="!filteredItems.length"
        class="flex flex-col items-center justify-center gap-3 py-12 sm:py-16 text-dimmed text-sm px-6 text-center"
      >
        <UIcon name="i-lucide-bookmark" class="size-10" />
        <template v-if="!favTotal">
          <p class="font-medium text-toned">
            Tu lista está vacía
          </p>
          <p class="max-w-xs">
            Toca el icono
            <UIcon name="i-lucide-bookmark-plus" class="size-4 inline-block align-text-bottom mx-0.5" />
            que aparece junto a cada resultado para guardarlo aquí.
          </p>
          <UButton
            icon="i-lucide-upload"
            label="Importar una lista compartida"
            variant="soft"
            size="sm"
            class="mt-1"
            @click="openImport"
          />
        </template>
        <p v-else>
          No hay coincidencias en tu lista para este filtro.
        </p>
      </div>

      <div
        v-for="(it, index) in filteredItems"
        :key="`${it.collection}-${it._id}-${index}`"
      >
        <div
          class="p-4 sm:px-6 text-sm cursor-pointer border-l-2 transition-colors text-toned"
          :class="selected && selected.collection === it.collection && String(selected._id) === String(it._id)
            ? 'border-primary bg-primary/10'
            : 'border-transparent hover:border-primary hover:bg-primary/5'"
          @click="selected = it"
        >
          <div class="flex items-start justify-between gap-2 mb-1">
            <div class="min-w-0 flex-1">
              <UBadge
                :label="labelFor(it.collection)"
                size="sm"
                variant="subtle"
                
                class="mb-1 uppercase font-semibold"
                :color="it.collection=='activities'?'success':'info'"
              />
              <div class="text-sm font-semibold line-clamp-2">
                {{ it.hit?.title || 'Sin título' }}
              </div>
            </div>
            <UButton
              icon="i-lucide-bookmark-x"
              color="neutral"
              variant="ghost"
              size="sm"
              class="-mt-1 -me-1 shrink-0"
              aria-label="Quitar de mi lista"
              @click="(ev: MouseEvent) => removeItem(it, ev)"
            />
          </div>

          <p class="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-muted">
            <span v-if="hasDate(it.hit)">{{ safeDate(it.hit) }}</span>
            <USeparator v-if="hasDate(it.hit) && formatLocation(it.hit)" orientation="vertical" class="h-3 hidden sm:block" />
            <span class="truncate">{{ formatLocation(it.hit) }}</span>
          </p>
        </div>
      </div>
    </div>
  </UDashboardPanel>

  <!-- Entrelíneas → componente propio (imagen ImageKit + texto). -->
  <EntrelineaDetail
    v-if="selected && !isMobile && isEntrelinea"
    :document="selectedEntrelineaDoc!"
    :collection="selectedCollection"
    @close="selected = null"
  />
  <!-- Resto (actividades, conferencias) → detalle con párrafos de Typesense. -->
  <PublicationDetail
    v-else-if="selected && !isMobile"
    :document="detailDocument"
    :document-loading="detailDocumentLoading"
    :paragraphs="detailParagraphs"
    :paragraphs-loading="detailParagraphsLoading"
    :collection="selectedCollection!"
    @close="selected = null"
  />
  <div v-else-if="!selected" 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-bookmark" class="size-16" />
      <p class="text-sm">
        Selecciona un elemento para ver el detalle
      </p>
    </div>
  </div>

  <ClientOnly>
    <USlideover v-if="isMobile" v-model:open="isPanelOpen">
      <template #content>
        <EntrelineaDetail
          v-if="selected && isEntrelinea"
          :document="selectedEntrelineaDoc!"
          :collection="selectedCollection"
          @close="selected = null"
        />
        <PublicationDetail
          v-else-if="selected"
          :document="detailDocument"
          :document-loading="detailDocumentLoading"
          :paragraphs="detailParagraphs"
          :paragraphs-loading="detailParagraphsLoading"
          :collection="selectedCollection!"
          @close="selected = null"
        />
      </template>
    </USlideover>
  </ClientOnly>

  <!-- Confirmación de vaciado ------------------------------------------- -->
  <UModal v-model:open="showClearConfirm" title="Vaciar mi lista">
    <template #body>
      <p class="text-sm text-toned">
        Esto eliminará los <strong>{{ favTotal }}</strong> elementos guardados en este dispositivo.
        La acción no se puede deshacer (a menos que tengas un export previo).
      </p>
    </template>
    <template #footer>
      <div class="flex flex-col-reverse sm:flex-row sm:justify-end gap-2 w-full">
        <UButton
          color="neutral"
          variant="ghost"
          label="Cancelar"
          block
          class="sm:w-auto"
          @click="showClearConfirm = false"
        />
        <UButton
          color="error"
          label="Vaciar lista"
          icon="i-lucide-trash-2"
          block
          class="sm:w-auto"
          @click="confirmClear"
        />
      </div>
    </template>
  </UModal>

  <!-- Importar ---------------------------------------------------------- -->
  <UModal v-model:open="showImport" title="Importar lista" :ui="{ footer: 'justify-end' }">
    <template #body>
      <div class="flex flex-col gap-4">
        <p class="text-sm text-muted">
          Carga una lista compartida desde un archivo
          <code class="px-1 rounded bg-elevated text-toned font-mono text-xs">.json</code>
          o pega su contenido. Funciona para cualquier tipo de búsqueda
          (actividades, conferencias y futuras secciones).
        </p>

        <UButton
          color="primary"
          variant="soft"
          icon="i-lucide-file-up"
          label="Subir archivo .json"
          size="md"
          block
          @click="pickImportFile"
        />

        <USeparator label="o pega el contenido" />

        <div>
          <p class="text-xs font-medium text-toned mb-1.5">
            ¿Qué hacer si ya tienes favoritos guardados?
          </p>
          <div class="grid grid-cols-2 gap-1.5">
            <UButton
              label="Combinar"
              icon="i-lucide-merge"
              size="sm"
              block
              :color="importMode === 'merge' ? 'primary' : 'neutral'"
              :variant="importMode === 'merge' ? 'soft' : 'outline'"
              @click="importMode = 'merge'"
            />
            <UButton
              label="Reemplazar"
              icon="i-lucide-replace"
              size="sm"
              block
              :color="importMode === 'replace' ? 'primary' : 'neutral'"
              :variant="importMode === 'replace' ? 'soft' : 'outline'"
              @click="importMode = 'replace'"
            />
          </div>
          <p class="text-xs text-dimmed mt-1.5">
            {{ importMode === 'merge'
              ? 'Conserva los favoritos actuales y añade los nuevos.'
              : 'Borra tu lista actual y la sustituye por la importada.' }}
          </p>
        </div>

        <UTextarea
          v-model="importText"
          :rows="6"
          placeholder='{"version":1,"items":[ ... ]}'
          class="w-full font-mono text-xs"
        />

        <input
          ref="importFileInput"
          type="file"
          accept="application/json,.json"
          class="hidden"
          @change="onImportFile"
        >
      </div>
    </template>
    <template #footer>
      <div class="flex flex-col-reverse sm:flex-row sm:justify-end gap-2 w-full">
        <UButton
          color="neutral"
          variant="ghost"
          label="Cancelar"
          block
          class="sm:w-auto"
          @click="showImport = false"
        />
        <UButton
          color="primary"
          icon="i-lucide-bookmark-plus"
          label="Importar desde texto"
          :loading="importing"
          :disabled="!importText.trim() || importing"
          block
          class="sm:w-auto"
          @click="applyTextImport"
        />
      </div>
    </template>
  </UModal>
</template>
