<template>
  <v-container fluid class="pt-2 pb-6">
    <!-- Tabs + the active tab's controls live in one sticky block pinned under
         the 64px TopNav (operator-asked 2026-06-12), so the axis switcher and
         search/sort stay reachable on a long grid. The controls can't sit
         inside v-window (it clips sticky children), so they're hoisted here. -->
    <div class="fc-series__head">
      <v-tabs v-model="tab" density="compact">
        <v-tab value="browse">Browse</v-tab>
        <v-tab value="suggestions">
          Suggestions
          <v-badge
            v-if="sug.suggestions.length" :content="sug.suggestions.length"
            inline color="accent"
          />
        </v-tab>
      </v-tabs>

      <div v-if="tab === 'browse'" class="fc-series-browse__controls">
        <v-text-field
          v-model="query"
          density="compact" variant="outlined" hide-details clearable
          label="Search series" prepend-inner-icon="mdi-magnify"
          style="max-width: 320px;"
        />
        <v-select
          :model-value="store.sort"
          :items="sortItems"
          density="compact" variant="outlined" hide-details
          label="Sort" style="max-width: 220px;"
          @update:model-value="store.setSort($event)"
        />
      </div>

      <div v-else class="fc-sug__controls">
        <v-switch
          :model-value="sug.enabled" color="accent" density="compact"
          hide-details label="Matching on"
          @update:model-value="sug.setEnabled($event)"
        />
        <v-text-field
          :model-value="sug.threshold"
          type="number" min="0" max="1" step="0.05"
          density="compact" variant="outlined" hide-details
          label="Threshold" style="max-width: 130px;"
          @change="sug.setThreshold(Number($event.target.value))"
        />
        <v-spacer />
        <v-btn
          variant="tonal" prepend-icon="mdi-radar" @click="sug.rescan()"
        >Rescan</v-btn>
      </div>
    </div>

    <v-window v-model="tab">
      <!-- ---- Browse ---- -->
      <v-window-item value="browse">
        <v-alert v-if="store.error" type="error" variant="tonal" closable class="my-4">
          Failed to load: {{ store.error }}
        </v-alert>
        <div
          v-else-if="!store.loading && store.series.length === 0"
          class="fc-series-browse__empty"
        >
          No series yet. Make one from a post's “Series ▾ → New series from this
          post”, or create a <code>series:</code> tag and add images.
        </div>
        <div
          v-else-if="!store.loading && visibleSeries.length === 0"
          class="fc-series-browse__empty"
        >
          No series match “{{ query }}”.
        </div>

        <div class="fc-series-browse__grid">
          <article
            v-for="s in visibleSeries" :key="s.id" class="fc-sbcard"
            @click="manage(s.id)"
          >
            <div class="fc-sbcard__cover">
              <img v-if="s.cover_thumbnail_url" :src="s.cover_thumbnail_url" alt="" loading="lazy" />
              <div v-else class="fc-sbcard__cover--empty">
                <v-icon size="large">mdi-book-open-page-variant</v-icon>
              </div>
              <span v-if="s.has_gap" class="fc-sbcard__gap" title="Has a missing-page gap">
                <v-icon size="x-small">mdi-alert-outline</v-icon> gap
              </span>
              <KebabMenu
                class="fc-sbcard__kebab" variant="flat"
                :label="`Actions for ${s.name}`"
              >
                <v-list-item prepend-icon="mdi-pencil" @click.stop="openRename(s)">
                  <v-list-item-title>Rename</v-list-item-title>
                </v-list-item>
                <v-list-item
                  prepend-icon="mdi-delete" base-color="error"
                  @click.stop="openDelete(s)"
                >
                  <v-list-item-title>Delete</v-list-item-title>
                </v-list-item>
              </KebabMenu>
            </div>
            <div class="fc-sbcard__body">
              <h3 class="fc-sbcard__name">{{ s.name }}</h3>
              <div class="fc-sbcard__meta">
                <span v-if="s.artist_name" class="fc-sbcard__artist">{{ s.artist_name }}</span>
                <span class="fc-sbcard__counts">
                  {{ s.chapter_count }} ch · {{ s.page_count }} pg
                </span>
              </div>
              <div class="fc-sbcard__actions">
                <v-btn
                  size="x-small" variant="tonal" color="accent"
                  prepend-icon="mdi-book-open"
                  :disabled="s.page_count === 0"
                  @click.stop="read(s.id)"
                >Read</v-btn>
                <v-btn
                  size="x-small" variant="text"
                  prepend-icon="mdi-pencil" @click.stop="manage(s.id)"
                >Manage</v-btn>
              </div>
            </div>
          </article>
        </div>

        <v-dialog v-model="renameDialog" max-width="420">
          <TagRenameDialog
            v-if="renameTarget"
            :tag="renameTarget"
            @renamed="onRenamed" @cancel="renameDialog = false"
          />
        </v-dialog>

        <v-dialog v-model="deleteDialog" max-width="440">
          <v-card v-if="deleteTarget">
            <v-card-title class="text-body-1">Delete “{{ deleteTarget.name }}”?</v-card-title>
            <v-card-text>
              <p class="text-body-2">
                Removes the series and its chapter/page ordering. The images
                themselves are kept — only the series grouping is deleted.
              </p>
              <v-alert
                v-if="deleteError" type="error" variant="tonal"
                density="compact" class="mt-3"
              >{{ deleteError }}</v-alert>
            </v-card-text>
            <v-card-actions>
              <v-spacer />
              <v-btn :disabled="deleting" @click="deleteDialog = false">Cancel</v-btn>
              <v-btn
                color="error" variant="flat" rounded="pill"
                :loading="deleting" @click="confirmDelete"
              >Delete series</v-btn>
            </v-card-actions>
          </v-card>
        </v-dialog>

        <div v-if="store.loading" class="fc-series-browse__sentinel">
          <v-progress-circular indeterminate color="accent" size="28" />
        </div>
      </v-window-item>

      <!-- ---- Suggestions ---- -->
      <v-window-item value="suggestions">
        <v-alert v-if="sug.error" type="error" variant="tonal" closable class="my-4">
          {{ sug.error }}
        </v-alert>
        <div
          v-else-if="!sug.loading && sug.suggestions.length === 0"
          class="fc-series-browse__empty"
        >
          No pending suggestions. Click <strong>Rescan</strong> to look for posts
          that continue an existing series.
        </div>

        <div class="fc-sug__list">
          <div v-for="s in sug.suggestions" :key="s.id" class="fc-sug__row">
            <div class="fc-sug__main">
              <div class="fc-sug__title">
                {{ s.post_title }}
                <v-icon size="x-small">mdi-arrow-right</v-icon>
                <span class="fc-sug__series">{{ s.series_name }}</span>
              </div>
              <div class="fc-sug__signals">
                <span class="fc-sug__score">{{ pct(s.score) }}</span>
                <span
                  v-for="k in signalKeys(s)" :key="k" class="fc-sug__chip"
                >{{ k }} {{ pct(s.signals[k]) }}</span>
              </div>
            </div>
            <div class="fc-sug__actions">
              <v-btn
                size="small" color="accent" variant="flat"
                @click="sug.accept(s.id)"
              >Add</v-btn>
              <v-btn size="small" variant="text" @click="sug.dismiss(s.id)">Skip</v-btn>
            </div>
          </div>
        </div>
      </v-window-item>
    </v-window>
  </v-container>
</template>

<script setup>
import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useSeriesBrowseStore } from '../stores/seriesBrowse.js'
import { useSeriesSuggestionsStore } from '../stores/seriesSuggestions.js'
import TagRenameDialog from '../components/modal/TagRenameDialog.vue'
import KebabMenu from '../components/common/KebabMenu.vue'

const router = useRouter()
const store = useSeriesBrowseStore()
const sug = useSeriesSuggestionsStore()
const tab = ref('browse')

const sortItems = [
  { title: 'Recently updated', value: 'recent' },
  { title: 'Name', value: 'name' },
  { title: 'Size', value: 'size' }
]

// Client-side name/artist filter — the whole list is already loaded (homelab
// scale), so search is instant and needs no round-trip.
const query = ref('')
const visibleSeries = computed(() => {
  const q = (query.value || '').trim().toLowerCase()
  if (!q) return store.series
  return store.series.filter(s =>
    s.name?.toLowerCase().includes(q)
    || s.artist_name?.toLowerCase().includes(q)
  )
})

function manage(id) {
  router.push({ name: 'series-manage', params: { tagId: id } })
}
function read(id) {
  router.push({ name: 'series-read', params: { tagId: id } })
}

// --- per-series kebab: rename + delete ----------------------------------
const renameDialog = ref(false)
const renameTarget = ref(null)
function openRename(s) {
  // TagRenameDialog renames the underlying Tag(kind=series); pass the kind so
  // its collision copy reads correctly.
  renameTarget.value = { id: s.id, name: s.name, kind: 'series' }
  renameDialog.value = true
}
function onRenamed(updated) {
  renameDialog.value = false
  if (updated?.id != null && updated?.name) {
    store.applyRename(updated.id, updated.name)
  }
}

const deleteDialog = ref(false)
const deleteTarget = ref(null)
const deleting = ref(false)
const deleteError = ref(null)
function openDelete(s) {
  deleteTarget.value = s
  deleteError.value = null
  deleteDialog.value = true
}
async function confirmDelete() {
  deleting.value = true
  deleteError.value = null
  try {
    await store.remove(deleteTarget.value.id)
    deleteDialog.value = false
  } catch (e) {
    deleteError.value = e.message
  } finally {
    deleting.value = false
  }
}

function pct(v) { return `${Math.round((v || 0) * 100)}%` }
// Only the signals that actually fired (strength > 0), in a stable order.
function signalKeys(s) {
  return ['title', 'artist', 'pages', 'tags'].filter(k => (s.signals?.[k] || 0) > 0)
}

onMounted(() => {
  store.load()
  sug.loadSettings()
  sug.load()
})
</script>

<style scoped>
/* Sticky header (tabs + active-tab controls) pinned under the 64px TopNav, so
   content scrolls cleanly beneath it. Surface bg matches SettingsView. */
.fc-series__head {
  position: sticky;
  top: 64px;
  z-index: 4;
  background: rgb(var(--v-theme-surface));
  padding-bottom: 12px;
}
.fc-series-browse__controls {
  display: flex; flex-wrap: wrap; gap: 12px; padding-top: 12px;
}
.fc-series-browse__empty {
  padding: 48px; text-align: center;
  color: rgb(var(--v-theme-on-surface-variant));
}
.fc-series-browse__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
}
.fc-sbcard {
  border: 1px solid rgb(var(--v-theme-surface-light));
  border-radius: 8px; overflow: hidden; cursor: pointer;
  background: rgb(var(--v-theme-surface));
  transition: border-color 120ms ease, transform 120ms ease;
}
.fc-sbcard:hover {
  border-color: rgb(var(--v-theme-accent), 0.6);
  transform: translateY(-2px);
}
.fc-sbcard__cover {
  position: relative; aspect-ratio: 3 / 2; background: rgb(var(--v-theme-surface-light));
}
.fc-sbcard__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fc-sbcard__cover--empty {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: rgb(var(--v-theme-on-surface-variant));
}
.fc-sbcard__gap {
  position: absolute; top: 6px; left: 6px;
  display: inline-flex; align-items: center; gap: 2px;
  padding: 1px 6px; border-radius: 999px; font-size: 11px;
  background: rgba(20, 23, 26, 0.75);
  color: rgb(var(--v-theme-warning, var(--v-theme-accent)));
}
/* Kebab sits top-right of the cover, opposite the gap badge. Tinted backing so
   it stays legible over any cover image. */
.fc-sbcard__kebab {
  position: absolute; top: 4px; right: 4px;
  background: rgba(20, 23, 26, 0.6) !important;
  border-radius: 50%;
  color: rgb(var(--v-theme-on-surface));
}
.fc-sbcard__kebab:hover { background: rgba(20, 23, 26, 0.85) !important; }
.fc-sbcard__body { padding: 8px 10px; }
.fc-sbcard__name {
  font-family: 'Fraunces', Georgia, serif; font-size: 15px; font-weight: 600;
  margin: 0 0 4px; color: rgb(var(--v-theme-on-surface));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fc-sbcard__meta {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
  margin-bottom: 8px;
}
.fc-sbcard__artist { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fc-sbcard__counts { flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.fc-sbcard__actions { display: flex; gap: 6px; }

/* Suggestions tab */
.fc-sug__controls {
  display: flex; align-items: center; gap: 16px; padding-top: 12px;
}
.fc-sug__list { display: flex; flex-direction: column; gap: 8px; }
.fc-sug__row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: 8px;
  border: 1px solid rgb(var(--v-theme-surface-light));
  background: rgb(var(--v-theme-surface));
}
.fc-sug__main { flex: 1; min-width: 0; }
.fc-sug__title {
  display: flex; align-items: center; gap: 6px;
  font-size: 14px; color: rgb(var(--v-theme-on-surface));
  overflow: hidden;
}
.fc-sug__series { color: rgb(var(--v-theme-accent)); font-weight: 600; }
.fc-sug__signals {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px;
}
.fc-sug__score {
  font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700;
  color: rgb(var(--v-theme-accent));
}
.fc-sug__chip {
  font-size: 11px; padding: 1px 7px; border-radius: 999px;
  background: rgb(var(--v-theme-surface-light));
  color: rgb(var(--v-theme-on-surface-variant));
}
.fc-sug__actions { flex: 0 0 auto; display: flex; gap: 6px; }
</style>
