<template>
  <div class="announcements-page">
    <div class="page-header">
      <div>
        <h2 class="page-title">📢 公告管理</h2>
        <p class="page-desc">发布和管理平台公告，支持草稿、置顶、封面和预览</p>
      </div>
      <a-space>
        <a-button type="primary" @click="handleCreate">
          <template #icon><PlusOutlined /></template>
          发布公告
        </a-button>
        <a-button :loading="loading" @click="loadAnnouncements">
          <template #icon><ReloadOutlined /></template>
          刷新
        </a-button>
      </a-space>
    </div>

    <a-row :gutter="[16, 16]" class="mb-6">
      <a-col :md="8" :xs="12">
        <div class="stat-card blue">
          <div class="stat-icon">📢</div>
          <div class="stat-info">
            <div class="stat-value">{{ totalCount }}</div>
            <div class="stat-label">全部公告</div>
          </div>
        </div>
      </a-col>
      <a-col :md="8" :xs="12">
        <div class="stat-card green">
          <div class="stat-icon">✅</div>
          <div class="stat-info">
            <div class="stat-value">{{ publishedCount }}</div>
            <div class="stat-label">已发布</div>
          </div>
        </div>
      </a-col>
      <a-col :md="8" :xs="12">
        <div class="stat-card orange">
          <div class="stat-icon">⭐</div>
          <div class="stat-info">
            <div class="stat-value">{{ recommendCount }}</div>
            <div class="stat-label">置顶公告</div>
          </div>
        </div>
      </a-col>
    </a-row>

    <div class="panel">
      <div class="panel-header">
        <span class="panel-title">📋 公告列表</span>
        <a-space wrap>
          <a-select v-model:value="filterStatus" style="width: 120px" @change="handleSearch">
            <a-select-option :value="undefined">全部状态</a-select-option>
            <a-select-option :value="0">已发布</a-select-option>
            <a-select-option :value="1">草稿</a-select-option>
          </a-select>
          <a-input-search
            v-model:value="searchKeyword"
            placeholder="搜索公告标题"
            style="width: 220px"
            @search="handleSearch"
          />
        </a-space>
      </div>

      <a-table
        :columns="columns"
        :data-source="announcements"
        :loading="loading"
        :pagination="pagination"
        row-key="articleId"
        size="middle"
        @change="handleTableChange"
      >
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'info'">
            <div class="ann-info-cell">
              <img v-if="record.image" :src="record.image" class="ann-thumb" />
              <div v-else class="ann-thumb-empty">📢</div>
              <div class="ann-info-text">
                <div class="ann-title">
                  <span v-if="record.recommend" class="pin-badge">📌 置顶</span>
                  {{ record.title }}
                </div>
                <div class="ann-overview">{{ record.overview || '暂无摘要' }}</div>
              </div>
            </div>
          </template>

          <template v-if="column.key === 'status'">
            <a-tag :color="record.status === 0 ? 'success' : 'default'">
              {{ record.status === 0 ? '已发布' : '草稿' }}
            </a-tag>
          </template>

          <template v-if="column.key === 'views'">
            <span class="text-sm text-gray">👁 {{ record.actualViews || 0 }}</span>
          </template>

          <template v-if="column.key === 'recommend'">
            <a-switch
              :checked="!!record.recommend"
              size="small"
              @change="(val: boolean) => handleTogglePin(record, val)"
            />
          </template>

          <template v-if="column.key === 'createTime'">
            <span class="text-sm text-gray">{{ record.createTime?.substring(0, 10) || '-' }}</span>
          </template>

          <template v-if="column.key === 'action'">
            <a-space>
              <a-button size="small" type="link" @click="handleView(record)">预览</a-button>
              <a-button size="small" type="link" @click="handleEdit(record)">编辑</a-button>
              <a-popconfirm title="确认删除此公告？" @confirm="handleDelete(record)">
                <a-button danger size="small" type="link">删除</a-button>
              </a-popconfirm>
            </a-space>
          </template>
        </template>
      </a-table>
    </div>

    <a-modal
      v-model:open="showFormModal"
      :confirm-loading="saving"
      :title="editing?.articleId ? '编辑公告' : '发布公告'"
      width="760px"
      @cancel="showFormModal = false"
      @ok="handleSave"
    >
      <a-form :model="formData" layout="vertical">
        <a-form-item label="公告标题" required>
          <a-input
            v-model:value="formData.title"
            :maxlength="200"
            placeholder="请输入公告标题"
            show-count
          />
        </a-form-item>

        <a-form-item label="封面图">
          <div class="cover-upload-wrap">
            <div v-if="formData.image" class="cover-preview-card">
              <img :src="formData.image" class="cover-preview-image" />
              <div class="cover-preview-actions">
                <a-button size="small" @click="handlePreviewImage(formData.image)">预览</a-button>
                <a-button danger size="small" @click="handleRemoveCover">移除</a-button>
              </div>
            </div>
            <a-upload
              :before-upload="beforeImageUpload"
              :custom-request="handleCoverUpload"
              :show-upload-list="false"
              accept="image/*"
            >
              <a-button :loading="imageUploading">上传封面</a-button>
            </a-upload>
            <div class="field-hint">支持 jpg/png/webp，适合公告 banner 场景，单张不超过 5MB</div>
          </div>
        </a-form-item>

        <a-form-item label="公告摘要">
          <a-textarea
            v-model:value="formData.overview"
            :maxlength="300"
            :rows="2"
            placeholder="简短描述公告内容"
            show-count
          />
        </a-form-item>

        <a-form-item label="公告内容" required>
          <a-textarea v-model:value="formData.content" :rows="10" placeholder="公告正文内容..." />
        </a-form-item>

        <a-row :gutter="16">
          <a-col :span="12">
            <a-form-item label="状态">
              <a-select v-model:value="formData.status">
                <a-select-option :value="0">立即发布</a-select-option>
                <a-select-option :value="1">保存为草稿</a-select-option>
              </a-select>
            </a-form-item>
          </a-col>
          <a-col :span="12">
            <a-form-item label="是否置顶">
              <a-switch v-model:checked="formPin" />
              <span class="switch-tip">置顶公告将优先展示在列表顶部</span>
            </a-form-item>
          </a-col>
        </a-row>
      </a-form>
    </a-modal>

    <a-modal
      v-model:open="showPreviewModal"
      :footer="null"
      :title="previewData?.title || '公告预览'"
      width="760px"
    >
      <template v-if="previewData">
        <div class="preview-meta">
          <span class="text-sm text-gray">发布时间：{{ previewData.createTime?.substring(0, 16) || '-' }}</span>
          <a-tag v-if="previewData.recommend" color="orange">置顶</a-tag>
          <a-tag :color="previewData.status === 0 ? 'success' : 'default'">
            {{ previewData.status === 0 ? '已发布' : '草稿' }}
          </a-tag>
        </div>
        <div v-if="previewData.image" class="preview-cover-wrap">
          <img :src="previewData.image" class="preview-cover" />
        </div>
        <div v-if="previewData.overview" class="preview-summary">{{ previewData.overview }}</div>
        <a-divider />
        <div class="preview-content" v-html="previewData.content || previewData.overview || '暂无内容'"></div>
      </template>
    </a-modal>

    <a-modal v-model:open="showImagePreview" :footer="null" title="封面预览" width="640px">
      <img v-if="previewImageUrl" :src="previewImageUrl" class="image-preview-modal" />
    </a-modal>
  </div>
</template>

<script lang="ts" setup>
import { PlusOutlined, ReloadOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import {
  pageAppArticle as pageCmsArticle,
  addAppArticle as addCmsArticle,
  updateAppArticle as updateCmsArticle,
  removeAppArticle as removeCmsArticle,
} from '@/api/app/article'
import { uploadFile } from '@/api/system/file'
import type { AppArticle as CmsArticle } from '@/api/app/article/model'

definePageMeta({ layout: 'admin' })
useHead({ title: '公告管理 - 平台管理' })

type UploadRequestOption = {
  file?: File
  onSuccess?: (body: unknown, file: File) => void
  onError?: (err: unknown) => void
}

const loading = ref(false)
const imageUploading = ref(false)
const announcements = ref<CmsArticle[]>([])
const filterStatus = ref<number | undefined>(undefined)
const searchKeyword = ref('')

const totalCount = ref(0)
const publishedCount = ref(0)
const recommendCount = ref(0)

const pagination = reactive({ current: 1, pageSize: 20, total: 0, showSizeChanger: true, showQuickJumper: true })

const columns = [
  { title: '公告信息', key: 'info', width: 420 },
  { title: '状态', key: 'status', width: 100 },
  { title: '阅读量', key: 'views', width: 100 },
  { title: '置顶', key: 'recommend', width: 80 },
  { title: '发布时间', key: 'createTime', width: 120 },
  { title: '操作', key: 'action', width: 170 },
]

const showFormModal = ref(false)
const saving = ref(false)
const editing = ref<CmsArticle | null>(null)
const formData = reactive<CmsArticle>({ title: '', overview: '', content: '', status: 0, image: '' })
const formPin = ref(false)

const showPreviewModal = ref(false)
const previewData = ref<CmsArticle | null>(null)
const showImagePreview = ref(false)
const previewImageUrl = ref('')

const ANNOUNCE_MODEL = 'announcement'

async function loadAnnouncements() {
  loading.value = true
  try {
    const res = await pageCmsArticle({
      page: pagination.current,
      limit: pagination.pageSize,
      model: ANNOUNCE_MODEL,
      status: filterStatus.value,
      keywords: searchKeyword.value || undefined,
    })
    announcements.value = res?.list || []
    pagination.total = res?.count || 0
    loadStats()
  } catch {
    message.error('加载公告列表失败')
  } finally {
    loading.value = false
  }
}

async function loadStats() {
  try {
    const [allRes, pubRes, pinRes] = await Promise.allSettled([
      pageCmsArticle({ page: 1, limit: 1, model: ANNOUNCE_MODEL }),
      pageCmsArticle({ page: 1, limit: 1, model: ANNOUNCE_MODEL, status: 0 }),
      pageCmsArticle({ page: 1, limit: 1, model: ANNOUNCE_MODEL, recommend: 1 }),
    ])
    totalCount.value = allRes.status === 'fulfilled' ? allRes.value?.count || 0 : 0
    publishedCount.value = pubRes.status === 'fulfilled' ? pubRes.value?.count || 0 : 0
    recommendCount.value = pinRes.status === 'fulfilled' ? pinRes.value?.count || 0 : 0
  } catch {
    // ignore
  }
}

function handleSearch() {
  pagination.current = 1
  loadAnnouncements()
}

function handleTableChange(pag: { current: number; pageSize: number }) {
  pagination.current = pag.current
  pagination.pageSize = pag.pageSize
  loadAnnouncements()
}

function resetForm() {
  Object.assign(formData, {
    articleId: undefined,
    title: '',
    overview: '',
    content: '',
    status: 0,
    image: '',
  })
  formPin.value = false
}

function handleCreate() {
  editing.value = null
  resetForm()
  showFormModal.value = true
}

function handleEdit(record: CmsArticle) {
  editing.value = record
  Object.assign(formData, {
    articleId: record.articleId,
    title: record.title || '',
    overview: record.overview || '',
    content: record.content || '',
    status: record.status ?? 0,
    image: record.image || '',
  })
  formPin.value = !!record.recommend
  showFormModal.value = true
}

function handleView(record: CmsArticle) {
  previewData.value = record
  showPreviewModal.value = true
}

async function handleSave() {
  if (!formData.title?.trim()) {
    message.warning('请输入公告标题')
    return
  }
  if (!formData.content?.trim()) {
    message.warning('请输入公告内容')
    return
  }
  saving.value = true
  try {
    const data: CmsArticle = {
      ...formData,
      model: ANNOUNCE_MODEL,
      recommend: formPin.value ? 1 : 0,
    }
    if (editing.value?.articleId) {
      await updateCmsArticle(data)
      message.success('公告已更新')
    } else {
      await addCmsArticle(data)
      message.success('公告已发布')
    }
    showFormModal.value = false
    loadAnnouncements()
  } catch (e: any) {
    message.error(e?.message || '保存失败')
  } finally {
    saving.value = false
  }
}

async function handleDelete(record: CmsArticle) {
  try {
    await removeCmsArticle(record.articleId)
    message.success('公告已删除')
    loadAnnouncements()
  } catch (e: any) {
    message.error(e?.message || '删除失败')
  }
}

async function handleTogglePin(record: CmsArticle, val: boolean) {
  try {
    await updateCmsArticle({ articleId: record.articleId, recommend: val ? 1 : 0 })
    message.success(val ? '已置顶' : '已取消置顶')
    loadAnnouncements()
  } catch (e: any) {
    message.error(e?.message || '操作失败')
  }
}

function beforeImageUpload(file: File) {
  if (!file.type.startsWith('image/')) {
    message.error('只能上传图片文件')
    return false
  }
  if (file.size > 5 * 1024 * 1024) {
    message.error('图片大小不能超过 5MB')
    return false
  }
  return true
}

async function handleCoverUpload(option: UploadRequestOption) {
  const rawFile = option.file
  if (!rawFile) return

  imageUploading.value = true
  try {
    const record = await uploadFile(rawFile)
    const url = (record?.url || record?.downloadUrl || '').trim()
    if (!url) throw new Error('上传成功但未返回图片地址')
    formData.image = url
    option.onSuccess?.(record, rawFile)
    message.success('封面上传成功')
  } catch (e) {
    option.onError?.(e)
    message.error(e instanceof Error ? e.message : '封面上传失败')
  } finally {
    imageUploading.value = false
  }
}

function handleRemoveCover() {
  formData.image = ''
}

function handlePreviewImage(url?: string) {
  if (!url) return
  previewImageUrl.value = url
  showImagePreview.value = true
}

onMounted(() => loadAnnouncements())
</script>

<style scoped>
.announcements-page { min-height: 100%; }

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}
.page-title { font-size: 18px; font-weight: 700; color: #1f2937; margin: 0; }
.page-desc { font-size: 13px; color: #9ca3af; margin: 2px 0 0; }

.stat-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px;
  border-radius: 12px;
  border: 2px solid transparent;
  transition: all 0.2s;
}
.stat-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); }
.stat-card.blue { background: #eff6ff; border-color: #dbeafe; }
.stat-card.green { background: #f0fdf4; border-color: #bbf7d0; }
.stat-card.orange { background: #fff7ed; border-color: #fed7aa; }
.stat-icon { font-size: 28px; flex-shrink: 0; }
.stat-value { font-size: 22px; font-weight: 700; color: rgba(0, 0, 0, 0.85); line-height: 1.2; }
.stat-label { font-size: 12px; color: rgba(0, 0, 0, 0.45); margin-top: 2px; }

.panel { background: #fff; border: 1px solid #f0f0f0; border-radius: 12px; overflow: hidden; }
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid #f5f5f5;
  flex-wrap: wrap;
  gap: 10px;
}
.panel-title { font-size: 14px; font-weight: 600; color: rgba(0, 0, 0, 0.85); }

.ann-info-cell { display: flex; align-items: flex-start; gap: 12px; }
.ann-thumb {
  width: 72px;
  height: 48px;
  border-radius: 8px;
  object-fit: cover;
  flex-shrink: 0;
  border: 1px solid #f0f0f0;
}
.ann-thumb-empty {
  width: 72px;
  height: 48px;
  border-radius: 8px;
  background: #f3f4f6;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex-shrink: 0;
}
.ann-info-text { flex: 1; min-width: 0; }
.ann-title {
  font-size: 14px;
  font-weight: 500;
  color: rgba(0, 0, 0, 0.85);
  margin-bottom: 4px;
  line-height: 1.6;
}
.ann-overview {
  font-size: 12px;
  color: rgba(0, 0, 0, 0.45);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 320px;
}
.pin-badge {
  font-size: 11px;
  color: #f97316;
  background: #fff7ed;
  padding: 1px 6px;
  border-radius: 4px;
  margin-right: 6px;
  border: 1px solid #fed7aa;
}

.cover-upload-wrap { display: flex; flex-direction: column; gap: 10px; }
.cover-preview-card {
  width: 240px;
  padding: 8px;
  border: 1px dashed #d9d9d9;
  border-radius: 10px;
  background: #fafafa;
}
.cover-preview-image {
  width: 100%;
  height: 132px;
  object-fit: cover;
  border-radius: 8px;
  display: block;
}
.cover-preview-actions { display: flex; gap: 8px; margin-top: 8px; }
.field-hint { font-size: 12px; color: rgba(0, 0, 0, 0.45); }
.switch-tip { margin-left: 8px; font-size: 12px; color: rgba(0, 0, 0, 0.45); }

.preview-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.preview-cover-wrap { margin: 16px 0 12px; }
.preview-cover {
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  border-radius: 12px;
  border: 1px solid #f0f0f0;
}
.preview-summary {
  margin-top: 12px;
  padding: 12px 14px;
  background: #fafafa;
  border-radius: 10px;
  color: rgba(0, 0, 0, 0.65);
  line-height: 1.7;
}
.preview-content {
  font-size: 15px;
  line-height: 1.8;
  color: rgba(0, 0, 0, 0.85);
  white-space: pre-wrap;
  word-break: break-word;
}
.image-preview-modal {
  width: 100%;
  max-height: 70vh;
  object-fit: contain;
  border-radius: 8px;
}

.text-sm { font-size: 12px; }
.text-gray { color: rgba(0, 0, 0, 0.45); }
.mb-6 { margin-bottom: 24px; }
</style>
