<!-- pages/articles/[slug].vue -->
<template>
  <article class="doc">
    <!-- 加载中 -->
    <div v-if="pending" class="state">加载中…</div>

    <!-- 未找到 -->
    <div v-else-if="!article" class="state">文章不存在或已被删除</div>

    <!-- 内容 -->
    <div v-else>
      <!-- 顶部 Hero -->
      <section class="hero" ref="heroRef" :data-transition-slug="article.slug">
        <div class="hero__inner" :class="{ 'editing-mode': isEditing }">
          <div v-if="isEditing" class="edit-hero-card">
            <div class="edit-grid">
              <div class="edit-form">
                <label class="field">
                  <span>标题</span>
                  <input
                    v-model.trim="editorForm.title"
                    type="text"
                    class="input edit-title"
                    placeholder="输入标题…"
                  />
                </label>
                <label class="field">
                  <span>摘要</span>
                  <textarea
                    v-model.trim="editorForm.description"
                    rows="3"
                    class="input edit-desc"
                    placeholder="一句话描述你的文章…"
                  />
                </label>
                <label class="field">
                  <span>标签</span>
                  <div class="tag-input-row">
                    <input
                      v-model.trim="tagInput"
                      class="input"
                      type="text"
                      placeholder="用逗号分隔多个标签"
                      @keydown.enter.prevent="addTag"
                      @blur="addTag"
                    />
                    <button class="btn ghost small" type="button" @click="addTag">添加</button>
                  </div>
                  <div v-if="editorForm.tagList.length" class="tag-chips">
                    <span v-for="(tag, idx) in editorForm.tagList" :key="tag + idx" class="chip">
                      {{ tag }}
                      <button type="button" @click="removeTag(idx)">×</button>
                    </span>
                  </div>
                </label>
                <div class="edit-actions">
                  <button class="btn ghost" type="button" @click="cancelEdit" :disabled="submitting">取消</button>
                  <button class="btn primary" type="button" @click="saveArticle" :disabled="submitting">
                    {{ submitting ? '保存中…' : '保存修改' }}
                  </button>
                </div>
              </div>
              <div class="edit-cover">
                <div class="cover-thumb" :style="{ backgroundImage: `url(${editorForm.coverPreview || editorForm.cover || '/cover.jpg'})` }"></div>
                <div class="cover-actions">
                  <label class="btn ghost small">
                    上传封面
                    <input type="file" accept="image/*" @change="onPickCover" hidden />
                  </label>
                  <button v-if="editorForm.cover || editorForm.coverPreview" class="btn ghost small" type="button" @click="removeCover">
                    移除
                  </button>
                </div>
              </div>
            </div>
          </div>
          <template v-else>
            <h1 class="hero__title" :data-hero-title="article.slug">
              {{ article.title || '未命名文章' }}
            </h1>
            <div v-if="(article.tagList || []).length" class="hero__caps">
              <span
                v-for="t in article.tagList"
                :key="t"
                class="cap"
                :class="{ rec: isRec(t) }"
              >
                {{ t }}
              </span>
            </div>
            <div class="hero__meta">
              <span v-if="article.author?.username" class="m">
                作者：{{ article.author.username }}
              </span>
              <span v-if="article.createdAt" class="m">
                发布于：{{ fmtDate(article.createdAt) }}
              </span>
              <span v-if="article.updatedAt" class="m">
                更新于：{{ fmtDate(article.updatedAt) }}
              </span>
              <span v-if="article.views !== undefined" class="m">
                阅读量：{{ article.views ?? 0 }}
              </span>
              <span v-if="article.description" class="m m-desc">
                {{ article.description }}
              </span>
              <template v-if="!isEditing">
                <button
                  type="button"
                  class="fav-action"
                  :class="{ active: article.favorited }"
                  @click="toggleFavorite"
                  :disabled="favoritePending"
                >
                  <svg viewBox="0 0 24 24" aria-hidden="true">
                    <path
                      d="M12 21s-6.7-4.5-9.4-7.2A6 6 0 1 1 12 6a6 6 0 1 1 9.4 7.8C18.7 16.5 12 21 12 21Z"
                    />
                  </svg>
                  {{ article.favorited ? '已收藏' : '收藏' }} ·
                  {{ article.favoritesCount || 0 }}
                </button>
                <button
                  v-if="canEdit"
                  type="button"
                  class="edit-action"
                  @click="goEditArticle"
                >
                  编辑文章
                </button>
              </template>
            </div>
          </template>
        </div>
      </section>

      <!-- 正文 -->
      <section class="content">
        <template v-if="isEditing">
          <div class="editor-main">
            <div class="editor-main-head">
              <h3>正文编辑</h3>
              <span class="muted">所见即所得，直接改正文</span>
            </div>
            <RichEditor v-model="editorForm.body" />
          </div>
        </template>
        <template v-else>
          <div class="content-body" v-html="article.body" />
        </template>
      </section>
    </div>
  </article>
</template>

<script setup>
definePageMeta({
  layout: 'article',
  pageTransition: false  // 禁用默认转场，使用自定义共享元素转场
})

import { ref, watch, computed, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { navigateTo } from '#app'
import { useApi, useAuthToken } from '@/composables/useApi'
import { useAuth } from '@/composables/useAuth'
import { useRightDrawer } from '@/composables/useRightDrawer'
import { useUpload } from '@/composables/useUpload'
import { useArticleNavContext } from '@/composables/useArticleNavContext'
import { useSharedTransition } from '@/composables/useSharedTransition'
import { useImageExtractor } from '@/composables/useImageExtractor'
import { useToast } from '@/composables/useToast'
import RichEditor from '@/components/RichEditor.vue'

const route = useRoute()
const api = useApi()
const toast = useToast()
const routerInstance = useRouter()
const { token } = useAuthToken()
const { user: authUser } = useAuth()
const { uploadImage } = useUpload()
const { extractFirstImage } = useImageExtractor()
const isLoggedIn = computed(() => !!token.value)
const drawer = useRightDrawer()
const navCtx = useArticleNavContext()
const transition = useSharedTransition()

const article = ref(null)
const pending = ref(true)
const favoritePending = ref(false)
const isEditing = ref(false)
const submitting = ref(false)
const editorTip = ref('')
const editorForm = ref({
  title: '',
  description: '',
  body: '',
  tagList: [],
  cover: '',
  coverPreview: '',
})
const coverFile = ref(null)
const tagInput = ref('')
const heroRef = ref(null)

/** 推荐/精选/热门/置顶/rec -> 炫彩标签 */
function isRec(tag) {
  const t = String(tag || '').toLowerCase()
  return ['推荐', '精选', '热门', '置顶', 'rec'].some(k =>
    t.includes(k.toLowerCase()),
  )
}

function fmtDate(d) {
  try {
    return new Date(d).toLocaleDateString()
  } catch {
    return d
  }
}

/**
 * 同步当前文章信息到右侧抽屉
 */
function syncDrawer(a) {
  if (!a) return

  drawer.setFromDoc({
    title: a.title || '未命名文章',
    description: a.description || '',
    tags: Array.isArray(a.tagList) ? a.tagList : [],
    path: route.fullPath,
    slug: a.slug || route.params.slug || '',
    favorited: a.favorited,
    favoritesCount: a.favoritesCount,
    prevSlug: prevSlug.value,
    nextSlug: nextSlug.value,
    authorUsername: a.author?.username || ''
  })
}

async function fetchArticle(forceSlug) {
  pending.value = true
  article.value = null

  try {
    const slug = forceSlug || route.params.slug
    if (!slug) {
      pending.value = false
      return
    }

    const res = await api.get(`/articles/${slug}`)
    // 后端返回 { article: { ... } }
    const data = res?.article || res || null
    article.value = data
    // 确保有可用的列表上下文（如果是直接打开详情页，回退到全站列表）
    await ensureNavContext(slug)
    navCtx.setCurrent(slug)
    syncDrawer(data)
    syncEditorForm(data)

    // 等待 DOM 更新后执行进入动画
    await nextTick()
    if (heroRef.value && slug) {
      transition.animateEnter(slug, heroRef.value)
    }
  } catch (e) {
    console.error('[ArticleDetail] fetch failed:', e)
    article.value = null
    // 出错时清空抽屉标题
    drawer.setFromDoc({
      title: '文章加载失败',
      description: '',
      tags: [],
      path: route.fullPath,
      slug: route.params.slug || ''
    })
  } finally {
    pending.value = false
  }
}

// 首次 + 路由参数变化时拉取文章
watch(
  () => route.params.slug,
  (val) => fetchArticle(val),
  { immediate: true }
)

const currentSlug = computed(() => String(route.params.slug || ''))
const prevSlug = computed(() => {
  const slugs = navCtx.state.value?.slugs || []
  const idx = slugs.indexOf(currentSlug.value)
  return idx > 0 ? slugs[idx - 1] : ''
})
const nextSlug = computed(() => {
  const slugs = navCtx.state.value?.slugs || []
  const idx = slugs.indexOf(currentSlug.value)
  return idx >= 0 && idx < slugs.length - 1 ? slugs[idx + 1] : ''
})

function goPrev() {
  if (!prevSlug.value || !String(prevSlug.value).trim()) {
    toast.info('已经是第一篇了', '没有上一篇文章')
    return
  }
  navCtx.setCurrent(prevSlug.value)
  routerInstance.push(`/articles/${prevSlug.value}`)
    .then(() => fetchArticle(prevSlug.value))
    .catch((err) => {
      console.error('[ArticleDetail] goPrev failed', err)
    })
}

function goNext() {
  if (!nextSlug.value || !String(nextSlug.value).trim()) {
    toast.info('已经是最后一篇了', '没有下一篇文章')
    return
  }
  navCtx.setCurrent(nextSlug.value)
  routerInstance.push(`/articles/${nextSlug.value}`)
    .then(() => fetchArticle(nextSlug.value))
    .catch((err) => {
      console.error('[ArticleDetail] goNext failed', err)
    })
}

drawer.setActions({
  favorite: () => toggleFavorite(),
  prev: () => goPrev(),
  next: () => goNext()
})

// nav 上下文变化时同步抽屉上的前后按钮
watch([prevSlug, nextSlug], () => {
  if (!article.value) return
  drawer.setFromDoc({
    title: article.value.title || '未命名文章',
    description: article.value.description || '',
    tags: Array.isArray(article.value.tagList) ? article.value.tagList : [],
    path: route.fullPath,
    slug: article.value.slug || route.params.slug || '',
    favorited: article.value.favorited,
    favoritesCount: article.value.favoritesCount,
    prevSlug: prevSlug.value,
    nextSlug: nextSlug.value,
    authorUsername: article.value.author?.username || ''
  })
})

// 当没有列表上下文时，回退拉取一页全站文章作为导航序列
async function ensureNavContext(slug) {
  const hasList = Array.isArray(navCtx.state.value?.slugs) && navCtx.state.value.slugs.length > 0
  const inList = slug && navCtx.state.value?.slugs?.includes(slug)

  console.log('[ArticleDetail] ensureNavContext 检查:', {
    slug,
    hasList,
    inList,
    currentListId: navCtx.state.value?.listId,
    currentSlugs: navCtx.state.value?.slugs,
    slugsLength: navCtx.state.value?.slugs?.length
  })

  if (hasList && inList) {
    console.log('[ArticleDetail] ensureNavContext - 已有列表上下文，跳过加载')
    return
  }

  console.log('[ArticleDetail] ensureNavContext - 没有列表上下文，加载全站文章')
  try {
    const res = await api.get('/articles', { limit: 100, offset: 0 })
    const list = Array.isArray(res?.articles) ? res.articles : []
    const slugs = list.map((a) => a.slug).filter(Boolean)
    if (slug && !slugs.includes(slug)) slugs.push(slug)
    console.log('[ArticleDetail] ensureNavContext - 加载了', slugs.length, '篇文章')
    if (slugs.length) navCtx.setList('fallback', slugs)
  } catch (err) {
    console.warn('[ArticleDetail] ensureNavContext failed', err)
  } finally {
    navCtx.setCurrent(slug || route.params.slug || '')
  }
}

// 如果在同一组件里切换 slug（<NuxtLink> 跳到新文章），自动重新拉取 + 同步抽屉
watch(
  () => route.params.slug,
  () => {
    fetchArticle()
  }
)

async function toggleFavorite() {
  if (!article.value?.slug) return
  if (!isLoggedIn.value) {
    navigateTo('/login')
    return
  }
  if (favoritePending.value) return
  favoritePending.value = true

  const slug = article.value.slug
  const currentlyFav = Boolean(article.value.favorited)
  try {
    if (currentlyFav) {
      await api.del(`/articles/${slug}/favorite`)
    } else {
      await api.post(`/articles/${slug}/favorite`)
    }

    article.value.favorited = !currentlyFav
    const delta = article.value.favorited ? 1 : -1
    article.value.favoritesCount = Math.max(
      0,
      (article.value.favoritesCount || 0) + delta,
    )
    syncDrawer(article.value)
  } catch (err) {
    console.error('[ArticleDetail] toggle favorite failed:', err)
    toast.error('收藏操作失败', err?.statusMessage || err?.message || '请稍后重试')
  } finally {
    favoritePending.value = false
  }
}

function addTag() {
  if (!tagInput.value?.trim()) return
  tagInput.value
    .split(/[，,]/)
    .map(s => s.trim())
    .filter(Boolean)
    .forEach((tag) => {
      if (!editorForm.value.tagList.includes(tag)) {
        editorForm.value.tagList.push(tag)
      }
    })
  tagInput.value = ''
}

function removeTag(index) {
  editorForm.value.tagList.splice(index, 1)
}

function onPickCover(event) {
  const file = event.target.files?.[0]
  event.target.value = ''
  if (!file) return
  if (editorForm.value.coverPreview) {
    URL.revokeObjectURL(editorForm.value.coverPreview)
  }
  coverFile.value = file
  editorForm.value.coverPreview = URL.createObjectURL(file)
}

function removeCover() {
  if (editorForm.value.coverPreview) {
    URL.revokeObjectURL(editorForm.value.coverPreview)
  }
  coverFile.value = null
  editorForm.value.coverPreview = ''
  editorForm.value.cover = ''
}

async function saveArticle() {
  if (!article.value?.slug) return
  if (!editorForm.value.title?.trim() || !editorForm.value.body?.trim()) {
    editorTip.value = '标题与正文均不能为空'
    return
  }
  submitting.value = true
  editorTip.value = ''
  try {
    let coverUrl = editorForm.value.cover || ''
    if (coverFile.value) {
      coverUrl = await uploadImage(coverFile.value)
    }

    // 如果没有封面，尝试从正文中提取第一张图片
    if (!coverUrl && editorForm.value.body) {
      const firstImage = extractFirstImage(editorForm.value.body)
      if (firstImage) {
        coverUrl = firstImage
        console.log('📸 自动提取封面图片:', firstImage)
      }
    }

    const payload = {
      article: {
        title: editorForm.value.title.trim(),
        description: editorForm.value.description?.trim() || '',
        body: editorForm.value.body,
        tagList: editorForm.value.tagList,
        cover: coverUrl || null,
      },
    }
    const res = await api.put(`/articles/${article.value.slug}`, payload)
    const updated = res?.article || res
    article.value = updated
    syncDrawer(updated)
    syncEditorForm(updated)
    isEditing.value = false
  } catch (error) {
    console.error('[ArticleDetail] save article failed:', error)
    editorTip.value = error?.statusMessage || '保存失败，请稍后重试'
  } finally {
    submitting.value = false
  }
}

const previewHtml = computed(() => {
  return editorForm.value.body || article.value?.body || ''
})

const canEdit = computed(() => {
  return (
    isLoggedIn.value &&
    !!article.value?.author?.username &&
    authUser.value?.username === article.value?.author?.username
  )
})

function syncEditorForm(data) {
  if (!data) return
  editorForm.value.title = data.title || ''
  editorForm.value.description = data.description || ''
  editorForm.value.body = data.body || ''
  editorForm.value.tagList = Array.isArray(data.tagList)
    ? [...data.tagList]
    : Array.isArray(data.tags)
      ? [...data.tags]
      : []
  editorForm.value.cover = data.cover || ''
  editorForm.value.coverPreview = ''
  coverFile.value = null
}

function goEditArticle() {
  if (!article.value) return
  // 确保每次进入编辑模式都同步一份最新数据，避免重复编辑时预览数据为空
  syncEditorForm(article.value)
  isEditing.value = true
  editorTip.value = ''
}

function cancelEdit() {
  isEditing.value = false
  editorTip.value = ''
  syncEditorForm(article.value)
}
</script>

<style scoped>
.doc {
  --w: 920px;
  color: #111827;
  background: #fff;
}

/* 顶部 Hero */
.hero {
  position: relative;
  padding: 54px 16px 32px;
  background: #fff;
}
.hero__inner {
  max-width: var(--w);
  margin: 0 auto;
  text-align: center;
}
.hero__inner.editing-mode {
  text-align: left;
  max-width: 980px;
}
.hero__cover {
  max-width: 800px;
  margin: 0 auto 24px;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 10px 40px rgba(15, 23, 42, 0.12);
}
.hero__cover-img {
  width: 100%;
  height: auto;
  display: block;
  object-fit: cover;
}
.hero__title {
  font-size: clamp(26px, 4.4vw, 40px);
  line-height: 1.25;
  font-weight: 800;
  letter-spacing: 0.2px;
}

/* 标签 */
.hero__caps {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 10px;
  margin-bottom: 8px;
}
.cap {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  background: #f3f4f6;
  color: #374151;
  border: 1px solid #e5e7eb;
}
.cap.rec {
  color: #fff;
  border: 0;
  background: linear-gradient(
    90deg,
    #7c3aed,
    #6366f1,
    #60a5fa,
    #22d3ee,
    #7c3aed
  );
  background-size: 200% 200%;
  animation: capFlow 2.1s linear infinite;
}

/* 元信息 */
.hero__meta {
  margin-top: 6px;
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  color: #6b7280;
}
.m {
  font-size: 13px;
}
.fav-action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid #e5e7eb;
  border-radius: 999px;
  padding: 4px 12px;
  background: #fff;
  color: #6b7280;
  font-size: 13px;
  cursor: pointer;
  transition: all .2s ease;
}
.fav-action svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
}
.fav-action:hover:not(:disabled) {
  border-color: #111827;
  color: #111827;
}
.fav-action.active {
  color: #e11d48;
  border-color: #e11d48;
}
.fav-action:disabled {
  opacity: .5;
  cursor: not-allowed;
}
.edit-action {
  border: 1px solid #c7d2fe;
  background: #eef2ff;
  color: #4338ca;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
.edit-action:hover {
  border-color: #4338ca;
  background: #e0e7ff;
}
.m-desc {
  display: block;
  width: 100%;
  max-width: var(--w);
  margin-top: 2px;
  color: #4b5563;
}

/* 正文 */
.content {
  max-width: var(--w);
  margin: 20px auto 64px;
  padding: 0 16px 8px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.input,
.editor-input,
.editor-textarea {
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid #d1d5db;
  background: #fff;
  font-size: 14px;
}

.editor-textarea {
  resize: vertical;
  min-height: 80px;
}

.edit-hero-card {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 16px;
  padding: 16px;
  box-shadow: 0 14px 32px rgba(15, 23, 42, 0.08);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.edit-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 240px;
  gap: 14px;
  align-items: start;
}

.edit-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.edit-title {
  font-size: 20px;
  font-weight: 800;
}

.edit-desc {
  resize: vertical;
}

.tag-input-row {
  display: flex;
  gap: 8px;
}

.edit-cover {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.cover-thumb {
  width: 100%;
  height: 150px;
  border-radius: 12px;
  background-size: cover;
  background-position: center;
  border: 1px solid #e5e7eb;
}
.cover-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.edit-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 4px;
}

.tag-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 12px;
  background: #eef2ff;
  color: #4338ca;
  border: 1px solid #e0e7ff;
  font-size: 12px;
}
.chip button {
  border: none;
  background: transparent;
  cursor: pointer;
  color: inherit;
  font-weight: 700;
}

.editor-main {
  border: 1px solid #e5e7eb;
  border-radius: 14px;
  padding: 12px;
  background: #fff;
  box-shadow: 0 10px 26px rgba(15, 23, 42, 0.06);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.editor-main-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.editor-main-head h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}
.editor-main-head .muted {
  font-size: 13px;
  color: #6b7280;
}

.btn {
  padding: 8px 12px;
  border-radius: 12px;
  border: 1px solid #d1d5db;
  background: #fff;
  cursor: pointer;
  font-weight: 700;
  transition: all 0.18s ease;
}
.btn.primary {
  background: #111827;
  color: #fff;
  border-color: #111827;
}
.btn.ghost {
  background: #fff;
  color: #111827;
}
.content-body {
  font-size: 16px;
  line-height: 1.9;
  color: #111827;
}
.content-body :deep(h2) {
  margin: 1.6em 0 0.6em;
  font-size: 24px;
  font-weight: 800;
}
.content-body :deep(h3) {
  margin: 1.4em 0 0.4em;
  font-size: 20px;
  font-weight: 700;
}
.content-body :deep(p) {
  margin: 0.8em 0;
}
.content-body :deep(ul),
.content-body :deep(ol) {
  margin: 0.6em 0 0.8em 1.2em;
}
.content-body :deep(blockquote) {
  margin: 1em 0;
  padding: 0.6em 1em;
  border-left: 4px solid #e5e7eb;
  background: #f9fafb;
  color: #374151;
  border-radius: 8px;
}
.content-body :deep(code) {
  background: #f3f4f6;
  padding: 0.1em 0.35em;
  border-radius: 6px;
}
.content-body :deep(pre) {
  background: #0b1020;
  color: #e5e7eb;
  padding: 16px;
  border-radius: 12px;
  overflow: auto;
}
.content-body :deep(img) {
  max-width: 100%;
  height: auto;
  border-radius: 12px;
  margin: 14px 0;
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.14);
}

/* 状态文案 */
.state {
  max-width: var(--w);
  margin: 80px auto;
  padding: 16px;
  text-align: center;
  font-size: 14px;
  color: #6b7280;
}

.btn {
  padding: 8px 12px;
  border-radius: 12px;
  border: 1px solid #d1d5db;
  background: #fff;
  cursor: pointer;
  font-weight: 700;
  transition: all 0.18s ease;
}
.btn.primary {
  background: #111827;
  color: #fff;
  border-color: #111827;
}
.btn.ghost {
  background: #fff;
  color: #111827;
}

@media (max-width: 960px) {
  .edit-grid {
    grid-template-columns: 1fr;
  }
}

@keyframes capFlow {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}
</style>
