<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>
      <span class="fc-series__count">
        {{ store.chapters.length }} part(s) · {{ store.pageCount }} page(s)
      </span>
      <v-spacer />
      <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">
      Each part is one installment — drag pages to set their order, and set the
      <strong>Part #</strong> to mark where it falls in the story. Use
      <strong>Add pages</strong> to pull images from the gallery into the part
      you're working on.
    </p>

    <!-- Parts, full width -->
    <div class="fc-parts">
      <template v-for="(ch, ci) in store.chapters" :key="ch.id">
        <section class="fc-part">
          <header class="fc-part__head">
            <label class="fc-part__partfield" :title="'Part number for this installment'">
              <span class="fc-part__partlabel">Part</span>
              <input
                class="fc-part__partnum" type="number" min="1"
                :value="partDraft[ch.id] ?? ''"
                :placeholder="String(ch.chapter_number)"
                @input="partDraft[ch.id] = $event.target.value"
                @keydown.enter.prevent="commitPart(ch)"
                @blur="commitPart(ch)"
              >
            </label>

            <v-text-field
              v-model="titleDraft[ch.id]"
              :placeholder="`Untitled — Part ${ch.stated_part ?? ch.chapter_number}`"
              density="compact" variant="plain" hide-details
              class="fc-part__title"
              @keydown.enter="commitTitle(ch)"
              @blur="commitTitle(ch)"
            />

            <span
              v-if="ch.source_post?.title" class="fc-part__src"
              :title="ch.source_post.title"
            >
              <v-icon size="x-small">mdi-link-variant</v-icon>
              {{ ch.source_post.title }}
            </span>

            <span v-if="ch.is_placeholder" class="fc-part__badge">placeholder</span>
            <span v-else class="fc-part__pc">{{ ch.pages.length }} pg</span>

            <v-spacer />

            <v-btn
              v-if="!ch.is_placeholder"
              size="small" variant="tonal" color="accent"
              prepend-icon="mdi-image-plus"
              @click="openPicker(ch.id)"
            >Add pages</v-btn>

            <v-menu location="bottom end">
              <template #activator="{ props }">
                <v-btn
                  v-bind="props" size="small" variant="text"
                  icon="mdi-dots-vertical" title="Part actions"
                />
              </template>
              <v-list density="compact">
                <v-list-item
                  prepend-icon="mdi-chevron-up" title="Move up"
                  :disabled="ci === 0"
                  @click="store.moveChapter(ch.id, -1)"
                />
                <v-list-item
                  prepend-icon="mdi-chevron-down" title="Move down"
                  :disabled="ci === store.chapters.length - 1"
                  @click="store.moveChapter(ch.id, 1)"
                />
                <v-list-item
                  prepend-icon="mdi-arrow-collapse-up" title="Merge into previous"
                  :disabled="ci === 0"
                  @click="store.mergeChapter(ch.id, store.chapters[ci - 1].id)"
                />
                <v-divider />
                <v-list-item
                  prepend-icon="mdi-delete-outline" title="Delete part"
                  base-color="error"
                  @click="confirmDelete(ch)"
                />
              </v-list>
            </v-menu>
          </header>

          <div class="fc-part__statedrow">
            <span class="fc-part__statedlabel">Printed pages</span>
            <input
              class="fc-part__statedin" type="number" min="0"
              :value="ch.stated_page_start ?? ''" placeholder="start"
              @change="onStated(ch, 'start', $event)"
            >
            <span class="fc-part__dash">–</span>
            <input
              class="fc-part__statedin" type="number" min="0"
              :value="ch.stated_page_end ?? ''" placeholder="end"
              @change="onStated(ch, 'end', $event)"
            >
            <span class="fc-part__statedhelp">
              optional — the page numbers printed in this installment
            </span>
          </div>

          <div v-if="ch.is_placeholder" class="fc-part__reserved">
            Reserved slot — a part you don't have yet.
          </div>
          <div v-else-if="ch.pages.length === 0" class="fc-part__empty">
            No pages yet.
            <v-btn
              size="small" variant="text" color="accent"
              prepend-icon="mdi-image-plus" @click="openPicker(ch.id)"
            >Add pages</v-btn>
          </div>
          <div v-else class="fc-part__pages">
            <div
              v-for="(p, pi) in ch.pages" :key="p.image_id"
              class="fc-page" draggable="true"
              :class="{ 'fc-page--dragging': drag && drag.chapterId === ch.id && drag.idx === pi }"
              @dragstart="drag = { chapterId: ch.id, idx: pi }"
              @dragend="drag = null"
              @dragover.prevent
              @drop="onPageDrop(ch, pi)"
            >
              <span class="fc-page__pn">{{ p.page_number }}</span>
              <img :src="p.thumbnail_url" alt="" loading="lazy" />
              <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 size="x-small" variant="flat" icon="mdi-close"
                       title="Remove from series" @click="store.remove(p.image_id)" />
              </div>
            </div>
          </div>
        </section>

        <div v-if="store.partGapAfter(ch.id)" class="fc-gap">
          <v-icon size="x-small">mdi-alert-outline</v-icon>
          Missing
          <template v-if="store.partGapAfter(ch.id).start === store.partGapAfter(ch.id).end">
            Part {{ store.partGapAfter(ch.id).start }}
          </template>
          <template v-else>
            Parts {{ store.partGapAfter(ch.id).start }}–{{ store.partGapAfter(ch.id).end }}
          </template>
        </div>
        <div v-if="store.gapAfter(ch.id)" class="fc-gap">
          <v-icon size="x-small">mdi-alert-outline</v-icon>
          Gap: printed pages {{ store.gapAfter(ch.id).start }}–{{ store.gapAfter(ch.id).end }} missing
        </div>
      </template>

      <div v-if="store.chapters.length === 0" class="fc-series__empty">
        No parts yet — add one, then add pages from the gallery.
      </div>

      <div class="fc-parts__add">
        <v-btn size="small" variant="tonal" prepend-icon="mdi-plus"
               @click="store.createChapter()">Add part</v-btn>
        <v-btn size="small" variant="text" prepend-icon="mdi-bookmark-outline"
               @click="store.createChapter({ isPlaceholder: true })">
          Add placeholder
        </v-btn>
      </div>
    </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>
        <v-select
          v-model="store.targetChapterId"
          :items="chapterItems" item-title="label" item-value="id"
          density="compact" variant="outlined" hide-details
          label="Add to part" class="mt-2"
        />
        <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 part</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-container>
</template>

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

const route = useRoute()
const store = useSeriesManageStore()
const sentinel = ref(null)
const drag = ref(null)            // { chapterId, idx }
const titleDraft = reactive({})   // chapterId -> draft title
const partDraft = reactive({})    // chapterId -> draft stated_part (string)
const pickerOpen = ref(false)

// Keep local drafts in sync with loaded chapters. Edits commit on blur/Enter,
// which refreshes and resets the draft to the saved value.
watch(() => store.chapters, (chs) => {
  for (const k of Object.keys(titleDraft)) delete titleDraft[k]
  for (const k of Object.keys(partDraft)) delete partDraft[k]
  for (const c of chs) {
    titleDraft[c.id] = c.title || ''
    partDraft[c.id] = c.stated_part == null ? '' : String(c.stated_part)
  }
}, { immediate: true })

const chapterItems = computed(() =>
  store.chapters.map(c => ({
    id: c.id,
    label: `Part ${c.stated_part ?? c.chapter_number}` +
      (c.title ? ` — ${c.title}` : '') +
      (c.is_placeholder ? ' (placeholder)' : ` · ${c.pages.length} pg`),
  }))
)

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

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

function onStated(ch, which, ev) {
  const raw = ev.target.value
  const n = raw === '' ? null : parseInt(raw, 10)
  const start = which === 'start' ? n : ch.stated_page_start
  const end = which === 'end' ? n : ch.stated_page_end
  store.setChapterStated(ch.id, start, end)
}

function onPageDrop(chapter, toIdx) {
  if (!drag.value || drag.value.chapterId !== chapter.id) { drag.value = null; return }
  if (drag.value.idx === toIdx) { drag.value = null; return }
  const ordered = moveItem(
    chapter.pages.map(p => p.image_id), drag.value.idx, toIdx
  )
  drag.value = null
  store.reorderPages(chapter.id, ordered)
}

function confirmDelete(ch) {
  const label = `Part ${ch.stated_part ?? ch.chapter_number}`
  const n = ch.pages.length
  const msg = n
    ? `Delete ${label} and remove its ${n} page(s) from the series?`
    : `Delete ${label}?`
  if (window.confirm(msg)) store.deleteChapter(ch.id)
}

function openPicker(chapterId) {
  store.targetChapterId = chapterId
  pickerOpen.value = true
}

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: 760px;
}

/* Parts — full width, stacked */
.fc-parts { display: flex; flex-direction: column; gap: 14px; max-width: 1100px; }
.fc-part {
  border: 1px solid rgb(var(--v-theme-surface-light));
  border-radius: 10px; padding: 12px 14px;
  background: rgb(var(--v-theme-surface));
}
.fc-part__head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.fc-part__partfield {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgb(var(--v-theme-accent), 0.12);
  border: 1px solid rgb(var(--v-theme-accent), 0.35);
  border-radius: 8px; padding: 4px 8px;
}
.fc-part__partlabel {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
  color: rgb(var(--v-theme-accent));
}
.fc-part__partnum {
  width: 46px; text-align: center; font-size: 18px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: transparent; border: none; color: rgb(var(--v-theme-on-surface));
}
.fc-part__partnum:focus { outline: none; }
.fc-part__partnum::placeholder { color: rgb(var(--v-theme-on-surface-variant)); opacity: 0.6; }

.fc-part__title { flex: 1 1 180px; min-width: 120px; font-size: 15px; }
.fc-part__src {
  display: inline-flex; align-items: center; gap: 4px; max-width: 240px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
}
.fc-part__badge {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
  color: rgb(var(--v-theme-on-surface-variant));
}
.fc-part__pc {
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
  font-variant-numeric: tabular-nums;
}

.fc-part__statedrow {
  display: flex; align-items: center; gap: 6px; margin: 8px 0 2px;
  font-size: 12px; color: rgb(var(--v-theme-on-surface-variant));
}
.fc-part__statedlabel { text-transform: uppercase; letter-spacing: 0.04em; font-size: 11px; }
.fc-part__statedin {
  width: 56px; text-align: center; font-size: 12px;
  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-part__statedin:focus { outline: none; border-color: rgb(var(--v-theme-accent)); }
.fc-part__dash { opacity: 0.6; }
.fc-part__statedhelp { font-size: 11px; opacity: 0.7; }

.fc-part__reserved, .fc-part__empty {
  padding: 18px; text-align: center; font-size: 13px;
  color: rgb(var(--v-theme-on-surface-variant));
}

/* Big page grid */
.fc-part__pages {
  display: grid; gap: 10px; margin-top: 10px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.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__pn {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  min-width: 24px; height: 24px; padding: 0 6px; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: rgb(var(--v-theme-accent)); color: rgb(var(--v-theme-on-accent, 0 0 0));
}
.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-gap {
  display: flex; align-items: center; gap: 6px; padding: 2px 10px;
  font-size: 12px; color: rgb(var(--v-theme-warning, var(--v-theme-accent)));
}
.fc-parts__add { display: flex; gap: 8px; margin-top: 4px; }
.fc-series__empty {
  padding: 40px; text-align: center; color: rgb(var(--v-theme-on-surface-variant));
}

/* 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>
