<template>
  <v-container fluid class="pt-2 pb-10 fc-series">
    <div class="fc-series__head">
      <span class="fc-series__name">{{ store.series?.name || 'Series' }}</span>
      <v-btn
        v-if="store.series"
        icon="mdi-pencil" size="x-small" variant="text"
        :aria-label="`Rename ${store.series.name}`"
        @click="renameOpen = true"
      />
      <span class="fc-series__count">
        {{ store.dividers.length }} chapter(s) · {{ store.pageCount }} page(s)
      </span>
      <v-spacer />
      <v-btn
        size="small" variant="tonal" prepend-icon="mdi-image-plus"
        @click="pickerOpen = true"
      >Add pages</v-btn>
      <v-btn
        v-if="store.pageCount > 0"
        size="small" variant="tonal" color="accent"
        prepend-icon="mdi-book-open"
        @click="$router.push({ name: 'series-read', params: { tagId: store.tagId } })"
      >Read</v-btn>
    </div>

    <p class="fc-series__hint">
      One continuous series. <strong>Each page's number is yours</strong> — type
      it on the card; gaps are fine. Drag a page onto a <strong>gap's edge</strong>
      to give it the adjacent number, or onto the end to append. New pages from a
      post land in the <strong>pending tray</strong> to sort.
    </p>

    <!-- Pending tray: pages staged from a post, grouped by source post. Drop the
         junk, set the start page, then place them numbered from there. -->
    <div v-if="store.pending.length" class="fc-pending">
      <div class="fc-pending__head">
        <v-icon size="small">mdi-tray-arrow-down</v-icon>
        <span>Pending — sort into the series</span>
      </div>
      <p class="fc-pending__hint">
        Drop the pages that don't belong (text-free alternates, bumpers), then
        place the rest — they'll be numbered up from the start page you set.
      </p>
      <section v-for="(grp, gi) in store.pending" :key="gi" class="fc-pgroup">
        <header class="fc-pgroup__head">
          <span class="fc-pgroup__title" :title="grp.post?.title">
            <v-icon size="x-small">mdi-link-variant</v-icon>
            {{ grp.post?.title || 'Unknown post' }}
          </span>
          <span class="fc-pgroup__count">{{ grp.pages.length }} pending</span>
          <label class="fc-pgroup__startfield">
            from page
            <input
              class="fc-pgroup__start" type="number" min="1" placeholder="?"
              :value="startDraft[gi] ?? grp.start_page ?? ''"
              @input="startDraft[gi] = $event.target.value"
            >
          </label>
          <v-spacer />
          <v-btn
            size="small" variant="tonal" color="accent"
            prepend-icon="mdi-playlist-plus"
            :disabled="effectiveStart(grp, gi) == null"
            @click="placeGroup(grp, gi)"
          >Place from page {{ effectiveStart(grp, gi) ?? '?' }}</v-btn>
        </header>
        <div class="fc-pgroup__pages">
          <div v-for="p in grp.pages" :key="p.image_id" class="fc-ppage">
            <img :src="p.thumbnail_url" alt="" loading="lazy" />
            <div class="fc-ppage__actions">
              <v-btn
                size="x-small" variant="flat" icon="mdi-close"
                title="Drop — doesn't belong in the series"
                @click="store.remove(p.image_id)"
              />
            </div>
          </div>
        </div>
      </section>
    </div>

    <!-- One continuous run, ordered by each page's number. Chapter dividers and
         missing-number gap blocks render inline. -->
    <div v-if="store.pages.length" class="fc-run">
      <template v-for="p in store.pages" :key="p.image_id">
        <!-- chapter divider bar, if one is anchored at this page -->
        <div v-if="store.dividerAt(p.image_id)" class="fc-divider">
          <v-icon size="small" class="fc-divider__icon">mdi-format-section</v-icon>
          <label class="fc-divider__partfield" title="Part number for this chapter">
            <span class="fc-divider__partlabel">Part</span>
            <input
              class="fc-divider__partnum" type="number" min="1"
              :value="partDraft[store.dividerAt(p.image_id).id] ?? ''"
              placeholder="—"
              @input="partDraft[store.dividerAt(p.image_id).id] = $event.target.value"
              @keydown.enter.prevent="commitPart(store.dividerAt(p.image_id))"
              @blur="commitPart(store.dividerAt(p.image_id))"
            >
          </label>
          <v-text-field
            :model-value="titleDraft[store.dividerAt(p.image_id).id]"
            placeholder="Untitled chapter"
            density="compact" variant="plain" hide-details
            class="fc-divider__title"
            @update:model-value="titleDraft[store.dividerAt(p.image_id).id] = $event"
            @keydown.enter="commitTitle(store.dividerAt(p.image_id))"
            @blur="commitTitle(store.dividerAt(p.image_id))"
          />
          <v-spacer />
          <v-btn
            size="x-small" variant="text" icon="mdi-close"
            title="Remove this chapter divider"
            @click="store.deleteDivider(store.dividerAt(p.image_id).id)"
          />
        </div>

        <!-- the page -->
        <div
          class="fc-page" draggable="true"
          :class="{ 'fc-page--dragging': drag === p.image_id }"
          @dragstart="drag = p.image_id"
          @dragend="drag = null"
        >
          <label class="fc-page__numfield" title="Page number (gaps allowed)">
            <input
              class="fc-page__num" type="number" min="1"
              :value="numDraft[p.image_id] ?? ''" placeholder="—"
              @input="numDraft[p.image_id] = $event.target.value"
              @keydown.enter.prevent="commitNum(p)"
              @blur="commitNum(p)"
              @mousedown.stop
            >
          </label>
          <img :src="p.thumbnail_url" alt="" loading="lazy" />
          <span
            v-if="p.source_post?.title" class="fc-page__src"
            :title="`From: ${p.source_post.title}`"
          >
            <v-icon size="x-small">mdi-link-variant</v-icon>
            {{ p.source_post.title }}
          </span>
          <div class="fc-page__actions">
            <v-btn
              size="x-small" variant="flat" icon="mdi-image-frame"
              title="Make series cover" @click="store.setCover(p.image_id)"
            />
            <v-btn
              v-if="!store.dividerAt(p.image_id)"
              size="x-small" variant="flat" icon="mdi-format-section"
              title="Start a chapter here" @click="store.createDivider(p.image_id)"
            />
            <v-btn
              size="x-small" variant="flat" icon="mdi-close"
              title="Remove from series" @click="store.remove(p.image_id)"
            />
          </div>
        </div>

        <!-- gap block after this page (drop a page on an edge to number it) -->
        <div
          v-if="store.gapAfter(p.image_id)" class="fc-gapblock"
          @dragover.prevent
        >
          <div
            class="fc-gapblock__edge"
            @drop="onGapDrop(store.gapAfter(p.image_id).from)"
          >← {{ store.gapAfter(p.image_id).from }}</div>
          <span class="fc-gapblock__label">{{ gapLabel(store.gapAfter(p.image_id)) }}</span>
          <div
            class="fc-gapblock__edge"
            @drop="onGapDrop(store.gapAfter(p.image_id).to)"
          >{{ store.gapAfter(p.image_id).to }} →</div>
        </div>
      </template>

      <!-- append zone: drop here to give the page the next number -->
      <div class="fc-append" @dragover.prevent @drop="onAppendDrop">
        <v-icon size="small">mdi-tray-plus</v-icon>
        drop here to append (page {{ nextNumber }})
      </div>
    </div>

    <div v-else class="fc-series__empty">
      No pages yet — use <strong>Add pages</strong> to pull images from the
      gallery into this series.
    </div>

    <!-- Picker slide-over -->
    <v-navigation-drawer
      v-model="pickerOpen" location="right" temporary width="420"
      class="fc-picker"
    >
      <div class="fc-picker__head">
        <div class="fc-picker__title">
          <span>Add pages</span>
          <v-btn size="x-small" variant="text" icon="mdi-close"
                 @click="pickerOpen = false" />
        </div>
        <div class="fc-picker__bar">
          <span>{{ store.pickerSelection.length }} selected</span>
          <v-btn
            size="small" color="accent" variant="flat"
            :disabled="store.pickerSelection.length === 0"
            @click="store.addSelected()"
          >Add to series</v-btn>
        </div>
      </div>
      <div class="fc-picker__grid">
        <div
          v-for="img in store.picker" :key="img.id"
          class="fc-picker__pick"
          :class="{ on: store.pickerSelection.includes(img.id) }"
          @click="store.togglePick(img.id)"
        >
          <img :src="img.thumbnail_url" alt="" loading="lazy" />
          <v-icon
            v-if="store.pickerSelection.includes(img.id)"
            class="fc-picker__check" color="accent" size="small"
          >mdi-check-circle</v-icon>
        </div>
        <div ref="sentinel" class="fc-picker__sentinel" />
      </div>
    </v-navigation-drawer>

    <v-dialog v-model="renameOpen" max-width="420">
      <TagRenameDialog
        v-if="store.series"
        :tag="{ id: store.tagId, name: store.series.name, kind: 'series' }"
        @renamed="onRenamed" @cancel="renameOpen = false"
      />
    </v-dialog>
  </v-container>
</template>

<script setup>
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useSeriesManageStore } from '../stores/seriesManage.js'
import { useInfiniteScroll } from '../composables/useInfiniteScroll.js'
import TagRenameDialog from '../components/modal/TagRenameDialog.vue'

const route = useRoute()
const store = useSeriesManageStore()
const sentinel = ref(null)
const drag = ref(null)            // image_id of the page being dragged
const numDraft = reactive({})     // image_id -> draft page number (string)
const titleDraft = reactive({})   // dividerId -> draft title
const partDraft = reactive({})    // dividerId -> draft stated_part (string)
const startDraft = reactive({})   // pending group index -> draft start page (string)
const pickerOpen = ref(false)
const renameOpen = ref(false)

function onRenamed(updated) {
  renameOpen.value = false
  if (store.series && updated?.name) store.series.name = updated.name
}

// Keep card/divider drafts in sync with loaded data; edits commit on blur/Enter,
// which refreshes and resets the draft to the saved value.
watch(() => store.pages, (ps) => {
  for (const k of Object.keys(numDraft)) delete numDraft[k]
  for (const p of ps) {
    numDraft[p.image_id] = p.page_number == null ? '' : String(p.page_number)
  }
}, { immediate: true })

watch(() => store.dividers, (divs) => {
  for (const k of Object.keys(titleDraft)) delete titleDraft[k]
  for (const k of Object.keys(partDraft)) delete partDraft[k]
  for (const d of divs) {
    titleDraft[d.id] = d.title || ''
    partDraft[d.id] = d.stated_part == null ? '' : String(d.stated_part)
  }
}, { immediate: true })

// Next number = one past the highest assigned page number (1 if none).
const nextNumber = computed(() => {
  let max = 0
  for (const p of store.pages) {
    if (p.page_number != null && p.page_number > max) max = p.page_number
  }
  return max + 1
})

function commitNum(p) {
  const raw = (numDraft[p.image_id] ?? '').trim()
  const next = raw === '' ? null : parseInt(raw, 10)
  if (raw !== '' && (Number.isNaN(next) || next < 1)) {
    numDraft[p.image_id] = p.page_number == null ? '' : String(p.page_number)
    return
  }
  if (next === (p.page_number ?? null)) return
  store.setPageNumber(p.image_id, next)
}

function onGapDrop(number) {
  if (drag.value == null) return
  const id = drag.value
  drag.value = null
  store.setPageNumber(id, number)
}

function onAppendDrop() {
  if (drag.value == null) return
  const id = drag.value
  drag.value = null
  store.setPageNumber(id, nextNumber.value)
}

function gapLabel(g) {
  return g.from === g.to
    ? `page ${g.from} missing`
    : `pages ${g.from}–${g.to} missing`
}

function commitTitle(d) {
  const v = (titleDraft[d.id] || '').trim()
  if (v === (d.title || '')) return
  store.renameDivider(d.id, v || null)
}

function commitPart(d) {
  const raw = (partDraft[d.id] ?? '').trim()
  const next = raw === '' ? null : parseInt(raw, 10)
  if (raw !== '' && (Number.isNaN(next) || next < 1)) {
    partDraft[d.id] = d.stated_part == null ? '' : String(d.stated_part)
    return
  }
  if (next === (d.stated_part ?? null)) return
  store.setDividerPart(d.id, next)
}

// Pending: effective start page = the typed override, else the post's parsed
// start. null when neither is set (operator must type one).
function effectiveStart(grp, gi) {
  const raw = (startDraft[gi] ?? '').toString().trim()
  if (raw !== '') {
    const n = parseInt(raw, 10)
    return Number.isNaN(n) || n < 1 ? null : n
  }
  return grp.start_page ?? null
}

function placeGroup(grp, gi) {
  const start = effectiveStart(grp, gi)
  if (start == null) return
  store.placePending(grp.pages.map(p => p.image_id), start)
}

useInfiniteScroll(sentinel, () => store.loadPicker())

onMounted(async () => {
  await store.load(parseInt(route.params.tagId, 10))
  await store.loadPicker(true)
})
</script>

<style scoped>
.fc-series__head {
  display: flex; align-items: baseline; gap: 12px; margin-bottom: 4px;
}
.fc-series__name { font-family: 'Fraunces', Georgia, serif; font-size: 24px; }
.fc-series__count {
  font-size: 13px; color: rgb(var(--v-theme-on-surface-variant));
}
.fc-series__hint {
  font-size: 13px; color: rgb(var(--v-theme-on-surface-variant));
  margin-bottom: 18px; max-width: 820px;
}

/* One continuous run with inline dividers + gap blocks. */
.fc-run {
  display: grid; gap: 10px; max-width: 1100px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

.fc-divider {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 12px;
  margin-top: 6px; padding: 6px 12px;
  border-left: 3px solid rgb(var(--v-theme-accent));
  background: rgb(var(--v-theme-accent), 0.10);
  border-radius: 0 8px 8px 0;
}
.fc-divider__icon { color: rgb(var(--v-theme-accent)); }
.fc-divider__partfield {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgb(var(--v-theme-accent), 0.14);
  border: 1px solid rgb(var(--v-theme-accent), 0.35);
  border-radius: 8px; padding: 2px 8px;
}
.fc-divider__partlabel {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: rgb(var(--v-theme-accent));
}
.fc-divider__partnum {
  width: 42px; text-align: center; font-size: 16px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: transparent; border: none; color: rgb(var(--v-theme-on-surface));
}
.fc-divider__partnum:focus { outline: none; }
.fc-divider__partnum::placeholder {
  color: rgb(var(--v-theme-on-surface-variant)); opacity: 0.6;
}
.fc-divider__title { flex: 1 1 200px; min-width: 120px; font-size: 15px; }

/* Gap block — one per gap; drop a page on an edge to number it. */
.fc-gapblock {
  grid-column: 1 / -1;
  display: flex; align-items: stretch; gap: 8px;
  padding: 4px; border: 1px dashed rgb(var(--v-theme-on-surface-variant), 0.5);
  border-radius: 8px;
}
.fc-gapblock__edge {
  display: flex; align-items: center; justify-content: center;
  min-width: 80px; padding: 6px 10px; border-radius: 6px;
  font-variant-numeric: tabular-nums; font-size: 13px;
  background: rgb(var(--v-theme-surface-light));
  color: rgb(var(--v-theme-on-surface-variant)); cursor: copy;
}
.fc-gapblock__edge:hover {
  background: rgb(var(--v-theme-accent), 0.2);
  color: rgb(var(--v-theme-accent));
}
.fc-gapblock__label {
  flex: 1; display: flex; align-items: center; justify-content: center;
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
}

.fc-append {
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px; border: 1px dashed rgb(var(--v-theme-on-surface-variant), 0.4);
  border-radius: 8px; font-size: 13px;
  color: rgb(var(--v-theme-on-surface-variant));
}

/* Page tiles */
.fc-page {
  position: relative; border-radius: 8px; overflow: hidden; cursor: grab;
  background: rgb(var(--v-theme-surface-light));
  border: 1px solid rgb(var(--v-theme-surface-light));
}
.fc-page--dragging { opacity: 0.4; }
.fc-page img {
  width: 100%; aspect-ratio: 3 / 4; object-fit: contain;
  display: block; background: rgb(var(--v-theme-background));
}
.fc-page__numfield {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  background: rgb(var(--v-theme-accent)); border-radius: 12px;
  padding: 0 4px; height: 24px; display: flex; align-items: center;
}
.fc-page__num {
  width: 34px; height: 22px; text-align: center;
  font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: transparent; border: none;
  color: rgb(var(--v-theme-on-accent, 0 0 0));
}
.fc-page__num:focus { outline: none; }
.fc-page__num::placeholder { color: rgb(var(--v-theme-on-accent, 0 0 0)); opacity: 0.5; }
.fc-page__src {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 2;
  display: flex; align-items: center; gap: 4px; padding: 3px 6px;
  font-size: 11px; color: #fff; background: rgba(0, 0, 0, 0.55);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fc-page__actions {
  position: absolute; top: 4px; right: 4px; z-index: 2;
  display: flex; gap: 2px; opacity: 0; transition: opacity 0.12s;
}
.fc-page:hover .fc-page__actions, .fc-page:focus-within .fc-page__actions { opacity: 1; }
.fc-page__actions :deep(.v-btn) {
  background: rgba(0, 0, 0, 0.55); color: #fff;
}

.fc-series__empty {
  padding: 40px; text-align: center; color: rgb(var(--v-theme-on-surface-variant));
  max-width: 1100px;
}

/* Pending tray */
.fc-pending {
  max-width: 1100px; margin-bottom: 18px;
  border: 1px solid rgb(var(--v-theme-accent), 0.4);
  border-radius: 10px; padding: 10px 14px;
  background: rgb(var(--v-theme-accent), 0.06);
}
.fc-pending__head {
  display: flex; align-items: center; gap: 8px;
  font-family: 'Fraunces', Georgia, serif; font-size: 16px;
  color: rgb(var(--v-theme-accent));
}
.fc-pending__hint {
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
  margin: 4px 0 10px;
}
.fc-pgroup { margin-bottom: 12px; }
.fc-pgroup__head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 6px;
  flex-wrap: wrap;
}
.fc-pgroup__title {
  display: inline-flex; align-items: center; gap: 4px; max-width: 320px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13px; font-weight: 600;
}
.fc-pgroup__count {
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
}
.fc-pgroup__startfield {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
}
.fc-pgroup__start {
  width: 56px; text-align: center; font-size: 13px;
  background: rgb(var(--v-theme-surface-light));
  border: 1px solid transparent; border-radius: 4px; padding: 2px 4px;
  color: rgb(var(--v-theme-on-surface));
}
.fc-pgroup__start:focus { outline: none; border-color: rgb(var(--v-theme-accent)); }
.fc-pgroup__pages {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
}
.fc-ppage {
  position: relative; border-radius: 6px; overflow: hidden;
  background: rgb(var(--v-theme-surface-light));
  border: 1px dashed rgb(var(--v-theme-accent), 0.5);
}
.fc-ppage img {
  width: 100%; aspect-ratio: 3 / 4; object-fit: contain; display: block;
  background: rgb(var(--v-theme-background));
}
.fc-ppage__actions {
  position: absolute; top: 4px; right: 4px; display: flex; gap: 2px;
  opacity: 0; transition: opacity 0.12s;
}
.fc-ppage:hover .fc-ppage__actions,
.fc-ppage:focus-within .fc-ppage__actions { opacity: 1; }
.fc-ppage__actions :deep(.v-btn) {
  background: rgba(0, 0, 0, 0.55); color: #fff;
}

/* Picker slide-over */
.fc-picker__head {
  position: sticky; top: 0; z-index: 1; padding: 12px 14px;
  background: rgb(var(--v-theme-surface));
  border-bottom: 1px solid rgb(var(--v-theme-surface-light));
}
.fc-picker__title {
  display: flex; align-items: center; justify-content: space-between;
  font-family: 'Fraunces', Georgia, serif; font-size: 18px;
}
.fc-picker__bar {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 10px; font-size: 13px;
}
.fc-picker__grid {
  display: grid; gap: 6px; padding: 12px;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
}
.fc-picker__pick {
  position: relative; cursor: pointer; aspect-ratio: 1; overflow: hidden;
  border-radius: 6px; outline: 2px solid transparent;
}
.fc-picker__pick img { width: 100%; height: 100%; object-fit: cover; }
.fc-picker__pick.on { outline-color: rgb(var(--v-theme-accent)); }
.fc-picker__check {
  position: absolute; top: 4px; right: 4px;
  background: rgba(0, 0, 0, 0.5); border-radius: 50%;
}
.fc-picker__sentinel { grid-column: 1 / -1; height: 40px; }
</style>
