<template>
  <div class="admin-page home-featured-page">
    <div class="page-grid card">
      <!-- 左侧：候选文章列表（GET /api/articles） -->
      <section class="column">
        <div class="section-header">
          <div>
            <div class="section-title">候选文章</div>
            <p class="section-sub">搜索 / 分页选择文章加入首页推送</p>
          </div>
          <div class="section-actions">
            <input
              v-model="searchKeyword"
              class="input"
              type="text"
              placeholder="搜索标题 / 描述 / 标签"
              @keyup.enter="handleSearch"
            />
            <button class="btn" type="button" @click="handleSearch" :disabled="loadingCandidates">
              {{ loadingCandidates ? '查询中...' : '搜索' }}
            </button>
            <button class="btn ghost" type="button" @click="refreshCandidates" :disabled="loadingCandidates">
              刷新
            </button>
          </div>
        </div>

        <div class="candidate-list">
          <div v-if="loadingCandidates" class="empty">文章列表加载中...</div>
          <div v-else-if="candidates.length === 0" class="empty">暂无匹配的文章</div>
          <div v-else class="candidate-cards">
            <article v-for="article in candidates" :key="article.slug" class="candidate-card">
              <div class="card-main">
                <div class="card-title">{{ article.title }}</div>
                <p class="card-desc">
                  {{ article.description || '暂无描述' }}
                </p>
                <div class="card-tags" v-if="article.tagList?.length">
                  <span v-for="tag in article.tagList" :key="`${article.slug}-${tag}`" class="tag-chip">
                    {{ tag }}
                  </span>
                </div>
                <div class="card-meta">
                  <span>浏览：{{ article.views ?? 0 }}</span>
                  <span class="dot">·</span>
                  <span>创建：{{ formatDate(article.createdAt) }}</span>
                </div>
              </div>
              <div class="card-actions">
                <button
                  class="btn primary"
                  type="button"
                  :disabled="isInSlots(article.slug) || slotsFull"
                  @click="addToSlots(article)"
                >
                  {{ isInSlots(article.slug) ? '已加入' : '加入槽位' }}
                </button>
              </div>
            </article>
          </div>
        </div>

        <div class="pagination" v-if="totalPages > 1">
          <button class="btn ghost" type="button" :disabled="page === 1" @click="prevPage">
            上一页
          </button>
          <span class="page-info">第 {{ page }} / {{ totalPages }} 页</span>
          <button class="btn ghost" type="button" :disabled="page >= totalPages" @click="nextPage">
            下一页
          </button>
        </div>
      </section>

      <!-- 右侧：首页推送槽位（GET/PUT /admin/home-featured-articles） -->
      <section class="column">
        <div class="section-header">
          <div>
            <div class="section-title">首页推送槽位</div>
            <p class="section-sub">最多 {{ maxSlots }} 条，保存后前台 /api/home-featured-articles 可见</p>
          </div>
          <div class="section-actions">
            <button class="btn ghost" type="button" @click="refreshSlots" :disabled="loadingSlots">
              {{ loadingSlots ? '读取中...' : '刷新配置' }}
            </button>
            <button class="btn primary" type="button" @click="saveSlots" :disabled="saving || !slots.length">
              {{ saving ? '保存中...' : '保存配置' }}
            </button>
          </div>
        </div>

        <div class="slots-list">
          <div v-if="loadingSlots" class="empty">正在读取首页推送配置...</div>
          <div v-else-if="slots.length === 0" class="empty">尚未添加首页推送文章</div>
          <div v-else class="slot-items">
            <div v-for="(article, index) in slots" :key="article.slug" class="slot-item">
              <div class="slot-index">#{{ index + 1 }}</div>
              <div class="slot-body">
                <div class="card-title">{{ article.title }}</div>
                <p class="card-desc">
                  {{ article.description || '暂无描述' }}
                </p>
                <div class="card-tags" v-if="article.tagList?.length">
                  <span v-for="tag in article.tagList" :key="`${article.slug}-${tag}`" class="tag-chip">
                    {{ tag }}
                  </span>
                </div>
              </div>
              <div class="slot-actions">
                <button class="btn ghost small" type="button" :disabled="index === 0" @click="moveSlot(index, -1)">
                  上移
                </button>
                <button
                  class="btn ghost small"
                  type="button"
                  :disabled="index === slots.length - 1"
                  @click="moveSlot(index, 1)"
                >
                  下移
                </button>
                <button class="btn danger small" type="button" @click="removeSlot(index)">移除</button>
              </div>
            </div>
          </div>
        </div>

        <div class="slots-footer">
          <span>已选择 {{ slots.length }} / {{ maxSlots }} 条</span>
          <span class="muted">保存后前台会实时展示，建议保持 10 条满配</span>
        </div>
      </section>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { navigateTo, useAsyncData, useRuntimeConfig } from '#app'
import { useAuth } from '@/composables/useAuth'
import { useAuthToken } from '@/composables/useApi'

definePageMeta({
  layout: 'admin',
  adminTitle: '首页推送',
  adminSub: '配置首页推送文章，控制前台首页推荐顺序',
})

interface ArticleItem {
  slug: string
  title: string
  description?: string | null
  tagList?: string[]
  views?: number
  createdAt?: string
}

interface ArticleListResponse {
  articles?: ArticleItem[]
  articles_count?: number
  articlesCount?: number
}

interface FeaturedResponse {
  articles?: ArticleItem[]
}

const pageSize = 8 // 候选列表分页大小，可根据后台能力调整
const maxSlots = 10 // 首页推送最大槽位数，保存时会截断到该数量

const searchKeyword = ref('')
const page = ref(1)
const total = ref(0)
const candidates = ref<ArticleItem[]>([])
const slots = ref<ArticleItem[]>([])
const saving = ref(false)
const loadingCandidates = ref(false)
const loadingSlots = ref(false)
const hasAccess = ref(false)

const { token } = useAuthToken()
const { user: authUser, fetchMe } = useAuth()
const { public: config } = useRuntimeConfig()

const authHeaders = computed(() => {
  return token.value ? { Authorization: `Token ${token.value}` } : {}
})

const isAdmin = computed(() => {
  const roles = authUser.value?.roles
  return Array.isArray(roles) && roles.includes('admin')
})

function formatDate(value?: string): string {
  if (!value) return '--'
  try {
    return new Date(value).toLocaleDateString()
  } catch {
    return '--'
  }
}

async function ensureAccess(redirect = true): Promise<boolean> {
  if (!token.value) {
    hasAccess.value = false
    if (redirect) await navigateTo('/login')
    return false
  }
  if (!authUser.value) {
    try {
      await fetchMe()
    } catch (err) {
      console.error('[Admin] fetch user failed', err)
    }
  }
  const ok = isAdmin.value
  hasAccess.value = ok
  if (!ok && redirect) await navigateTo('/')
  return ok
}

// GET /api/articles（搜索 + 分页）
const { data: candidateData, refresh: refreshCandidatesData } = useAsyncData(
  'admin-home-candidates',
  () =>
    $fetch<ArticleListResponse>(`${config.apiBase}/articles`, {
      headers: authHeaders.value,
      params: {
        limit: pageSize,
        offset: (page.value - 1) * pageSize,
        search: searchKeyword.value || undefined,
      },
    }),
  { server: false, immediate: false },
)

watch(candidateData, (res) => {
  const list = Array.isArray(res?.articles) ? res?.articles : []
  candidates.value = list
  const totalCount = Number(res?.articles_count ?? res?.articlesCount ?? list.length)
  total.value = Number.isNaN(totalCount) ? list.length : totalCount
})

async function handleSearch(): Promise<void> {
  page.value = 1
  await refreshCandidates()
}

async function refreshCandidates(): Promise<void> {
  if (!hasAccess.value) return
  loadingCandidates.value = true
  try {
    await refreshCandidatesData()
  } finally {
    loadingCandidates.value = false
  }
}

const totalPages = computed(() =>
  Math.max(1, Math.ceil(total.value / pageSize)),
)

function prevPage(): void {
  if (page.value <= 1) return
  page.value -= 1
  refreshCandidates()
}

function nextPage(): void {
  if (page.value >= totalPages.value) return
  page.value += 1
  refreshCandidates()
}

// GET /admin/home-featured-articles（加载已有配置）
const { data: slotData, refresh: refreshSlotData } = useAsyncData(
  'admin-home-featured',
  () =>
    $fetch<FeaturedResponse>(`${config.apiBase}/admin/home-featured-articles`, {
      headers: authHeaders.value,
    }),
  { server: false, immediate: false },
)

watch(slotData, (res) => {
  const list = Array.isArray(res?.articles) ? res.articles : []
  slots.value = list.slice(0, maxSlots)
})

async function refreshSlots(): Promise<void> {
  if (!hasAccess.value) return
  loadingSlots.value = true
  try {
    await refreshSlotData()
  } finally {
    loadingSlots.value = false
  }
}

const slotsFull = computed(() => slots.value.length >= maxSlots)

function isInSlots(slug?: string): boolean {
  if (!slug) return false
  return slots.value.some((item) => item.slug === slug)
}

function addToSlots(article: ArticleItem): void {
  if (slotsFull.value) {
    alert(`最多只能选择 ${maxSlots} 条首页推送`)
    return
  }
  if (isInSlots(article.slug)) return
  slots.value = [...slots.value, article].slice(0, maxSlots)
}

function removeSlot(index: number): void {
  slots.value.splice(index, 1)
}

function moveSlot(index: number, delta: number): void {
  const newIndex = index + delta
  if (newIndex < 0 || newIndex >= slots.value.length) return
  const newList = [...slots.value]
  const [moved] = newList.splice(index, 1)
  newList.splice(newIndex, 0, moved)
  slots.value = newList
}

// PUT /admin/home-featured-articles（保存配置）
async function saveSlots(): Promise<void> {
  if (!hasAccess.value || saving.value || !slots.value.length) return
  saving.value = true
  try {
    await $fetch(`${config.apiBase}/admin/home-featured-articles`, {
      method: 'PUT',
      headers: authHeaders.value,
      body: {
        articles: slots.value.map((item) => ({ slug: item.slug })),
      },
    })
    await refreshSlots()
    alert('首页推送配置已保存')
  } catch (err: any) {
    console.error('[Admin] save home featured failed', err)
    alert(err?.statusMessage || '保存失败，请稍后重试')
  } finally {
    saving.value = false
  }
}

async function initPage(): Promise<void> {
  if (await ensureAccess(true)) {
    await Promise.all([refreshCandidates(), refreshSlots()])
  }
}

onMounted(initPage)

watch(
  () => token.value,
  async () => {
    if (await ensureAccess(false)) {
      await Promise.all([refreshCandidates(), refreshSlots()])
    } else {
      candidates.value = []
      slots.value = []
    }
  },
)
</script>

<style scoped>
.admin-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.card {
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid #e5e7eb;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.06);
  padding: 16px;
}

.page-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 16px;
}

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

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}

.section-title {
  font-weight: 800;
  font-size: 18px;
}

.section-sub {
  margin-top: 4px;
  color: #6b7280;
  font-size: 13px;
}

.section-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.input {
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid #d1d5db;
  min-width: 220px;
  font-size: 14px;
  background: #fff;
}

.btn {
  padding: 8px 14px;
  border-radius: 10px;
  border: 1px solid #d1d5db;
  background: #fff;
  cursor: pointer;
  transition: all 0.18s ease;
  font-weight: 600;
}

.btn.primary {
  background: linear-gradient(120deg, #22d3ee, #6366f1);
  color: #fff;
  border-color: transparent;
}

.btn.ghost {
  border-color: #e5e7eb;
  background: #fff;
}

.btn.danger {
  color: #b91c1c;
  border-color: #fecdd3;
  background: #fff5f5;
}

.btn.small {
  padding: 6px 10px;
  font-size: 13px;
}

.candidate-list,
.slots-list {
  background: #f8fafc;
  border-radius: 12px;
  border: 1px solid #e5e7eb;
  padding: 12px;
  min-height: 200px;
}

.candidate-cards,
.slot-items {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.candidate-card,
.slot-item {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  background: #ffffff;
  border-radius: 12px;
  padding: 12px;
  border: 1px solid #e5e7eb;
}

.slot-item {
  grid-template-columns: 60px 1fr auto;
  align-items: center;
}

.card-main,
.slot-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.card-title {
  font-weight: 700;
  color: #0f172a;
}

.card-desc {
  margin: 0;
  color: #4b5563;
  font-size: 13px;
  line-height: 1.5;
}

.card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tag-chip {
  background: #eef2ff;
  color: #4f46e5;
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 12px;
}

.card-meta {
  display: flex;
  gap: 6px;
  color: #94a3b8;
  font-size: 12px;
}

.dot {
  opacity: 0.4;
}

.card-actions,
.slot-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.slot-index {
  font-weight: 800;
  color: #6366f1;
  font-size: 16px;
}

.slots-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #6b7280;
  font-size: 13px;
  padding: 0 4px;
}

.muted {
  color: #9ca3af;
}

.pagination {
  display: flex;
  align-items: center;
  gap: 10px;
}

.page-info {
  color: #4b5563;
}

.empty {
  text-align: center;
  padding: 18px 10px;
  color: #9ca3af;
}

@media (max-width: 1100px) {
  .page-grid {
    grid-template-columns: 1fr;
  }
}
</style>
