<template>
  <div class="document-page">
    <Breadcrumb :items="['知识管理', '文档管理']" />
    <a-card class="general-card" :bordered="false">
      <div class="page-header">
        <a-tabs v-model:active-key="scope" @change="handleScopeChange">
          <a-tab-pane key="my" title="我的文档" />
          <a-tab-pane key="all" title="全部文档" />
        </a-tabs>
        <a-space>
          <a-button v-if="canCreate" type="primary" @click="openCreate">
            <template #icon><icon-plus /></template>
            新建文档
          </a-button>
          <a-button :loading="loading" @click="loadDocuments">
            <template #icon><icon-refresh /></template>
            刷新
          </a-button>
        </a-space>
      </div>

      <a-form :model="filters" layout="inline" class="filters" @submit-success="search">
        <a-form-item label="关键词">
          <a-input v-model="filters.keyword" placeholder="搜索标题、描述、正文或关键词" allow-clear style="width: 260px" />
        </a-form-item>
        <a-form-item label="公共分类">
          <a-select v-model="filters.category_id" placeholder="全部分类" allow-clear allow-search style="width: 220px">
            <a-option v-for="category in categoryOptions" :key="category.id" :value="category.id">
              {{ category.label }}{{ category.status === 'inactive' ? '（停用）' : '' }}
            </a-option>
          </a-select>
        </a-form-item>
        <a-form-item v-if="scope === 'my'" label="状态">
          <a-select v-model="filters.status" placeholder="全部状态" allow-clear style="width: 130px">
            <a-option v-for="item in statusOptions" :key="item.value" :value="item.value">{{ item.label }}</a-option>
          </a-select>
        </a-form-item>
        <a-form-item>
          <a-space>
            <a-button type="primary" html-type="submit">查询</a-button>
            <a-button @click="resetFilters">重置</a-button>
          </a-space>
        </a-form-item>
      </a-form>

      <div class="content-layout">
        <section class="document-list">
          <a-spin :loading="loading" class="list-spin">
            <a-empty v-if="!documents.length" :description="scope === 'my' ? '暂无我的文档' : '暂无已审核文档'" />
            <button
              v-for="document in documents"
              :key="document.id"
              type="button"
              class="document-item"
              :class="{ active: selected?.id === document.id }"
              @click="selectDocument(document)"
            >
              <span class="document-title">{{ document.title }}</span>
              <span class="document-meta">
                <a-tag :color="statusColor(document.status)" size="small">{{ statusText(document.status) }}</a-tag>
                <span>{{ categoryName(document.category_id) }}</span>
                <span>{{ formatTime(document.updated_at) }}</span>
              </span>
            </button>
          </a-spin>
          <a-pagination
            v-model:current="pagination.current"
            :page-size="pagination.pageSize"
            :total="pagination.total"
            simple
            @change="changePage"
          />
        </section>

        <section class="document-detail">
          <a-spin :loading="detailLoading" class="detail-spin">
            <template v-if="selected">
              <div class="detail-header">
                <div>
                  <h2>{{ selected.title }}</h2>
                  <a-space wrap>
                    <a-tag :color="statusColor(selected.status)">{{ statusText(selected.status) }}</a-tag>
                    <a-tag>{{ typeText(selected.type) }}</a-tag>
                    <span>{{ selected.author_name || `用户 ${selected.author_id}` }}</span>
                  </a-space>
                </div>
                <a-space wrap>
                  <a-button :loading="favoriteLoading" @click="toggleFavorite">
                    <template #icon>
                      <icon-star-fill v-if="selected.is_favorited" />
                      <icon-star v-else />
                    </template>
                    {{ selected.is_favorited ? '取消收藏' : '收藏' }}
                  </a-button>
                  <a-button v-if="canEdit(selected)" type="primary" @click="openEdit">编辑</a-button>
                  <a-button v-if="canSubmit(selected)" @click="confirmPublish">提交审核</a-button>
                  <a-button v-if="canDelete(selected)" status="danger" @click="confirmDelete">删除</a-button>
                </a-space>
              </div>
              <a-descriptions :column="3" bordered size="small" class="metadata">
                <a-descriptions-item label="文档编号">{{ selected.doc_no }}</a-descriptions-item>
                <a-descriptions-item label="公共分类">{{ categoryName(selected.category_id) }}</a-descriptions-item>
                <a-descriptions-item label="更新时间">{{ formatTime(selected.updated_at) }}</a-descriptions-item>
                <a-descriptions-item label="描述" :span="3">{{ selected.description || '-' }}</a-descriptions-item>
                <a-descriptions-item label="关键词" :span="3">{{ selected.keywords || '-' }}</a-descriptions-item>
              </a-descriptions>
              <h3>正文</h3>
              <pre class="document-content">{{ selected.content }}</pre>
            </template>
            <a-empty v-else description="请从左侧选择文档" />
          </a-spin>
        </section>
      </div>
    </a-card>

    <a-drawer
      :visible="formVisible"
      :title="editing ? '编辑文档' : '新建文档'"
      :width="720"
      :closable="!saving"
      :mask-closable="!saving"
      :esc-to-close="!saving"
      :on-before-cancel="canCloseForm"
      unmount-on-close
      @cancel="closeForm"
      @update:visible="handleFormVisibleChange"
    >
      <a-form ref="formRef" :model="form" layout="vertical">
        <a-form-item label="标题" field="title" :rules="[{ required: true, message: '请输入标题' }]">
          <a-input v-model="form.title" :max-length="200" />
        </a-form-item>
        <a-row :gutter="16">
          <a-col :span="12">
            <a-form-item label="类型" field="type">
              <a-select v-model="form.type">
                <a-option v-for="item in documentTypeOptions" :key="item.value" :value="item.value">{{ item.label }}</a-option>
              </a-select>
            </a-form-item>
          </a-col>
          <a-col :span="12">
            <a-form-item label="公共分类" field="category_id" :rules="[{ required: true, message: '请选择启用分类' }]">
              <a-select v-model="form.category_id" placeholder="请选择启用分类" allow-clear allow-search>
                <a-option v-for="category in activeCategoryOptions" :key="category.id" :value="category.id">
                  {{ category.label }}
                </a-option>
              </a-select>
            </a-form-item>
          </a-col>
        </a-row>
        <a-alert v-if="originalCategoryUnavailable" type="warning" class="category-warning">
          原分类已停用或不可用，请重新选择启用分类
        </a-alert>
        <a-form-item label="描述" field="description">
          <a-textarea v-model="form.description" :max-length="1000" :auto-size="{ minRows: 2, maxRows: 4 }" />
        </a-form-item>
        <a-form-item label="正文" field="content" :rules="[{ required: true, message: '请输入正文' }]">
          <a-textarea v-model="form.content" :auto-size="{ minRows: 16, maxRows: 30 }" />
        </a-form-item>
        <a-form-item label="关键词" field="keywords">
          <a-input v-model="form.keywords" :max-length="500" placeholder="多个关键词可用逗号分隔" />
        </a-form-item>
        <a-form-item label="标签" field="tags">
          <a-input v-model="form.tags" placeholder='JSON 数组，例如 ["运维"]' />
        </a-form-item>
        <a-form-item label="检测点 ID" field="detection_point_ids">
          <a-input v-model="form.detection_point_ids" placeholder="JSON 数组，例如 [1,2]" />
        </a-form-item>
        <a-form-item label="备注" field="remarks">
          <a-textarea v-model="form.remarks" :auto-size="{ minRows: 2, maxRows: 4 }" />
        </a-form-item>
      </a-form>
      <template #footer>
        <a-space>
          <a-button :disabled="saving" @click="closeForm">取消</a-button>
          <a-button type="primary" :loading="saving" :disabled="saving" @click="saveDocument">保存</a-button>
        </a-space>
      </template>
    </a-drawer>
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, reactive, ref, watch } from 'vue'
import axios from 'axios'
import dayjs from 'dayjs'
import { Message, Modal } from '@arco-design/web-vue'
import type { FormInstance } from '@arco-design/web-vue'
import { IconPlus, IconRefresh, IconStar, IconStarFill } from '@arco-design/web-vue/es/icon'
import {
  createDocument,
  deleteDocument,
  documentTypeOptions,
  favoriteDocument,
  fetchDocumentDetail,
  fetchDocumentList,
  publishDocument,
  unfavoriteDocument,
  updateDocument,
  type Document,
  type DocumentScope,
  type DocumentStatus,
  type DocumentType,
} from '@/api/kb/document'
import { fetchCategoryTree, type CategoryStatus, type CategoryTreeNode } from '@/api/kb/category'
import usePermissionCodes from '@/hooks/usePermissionCodes'
import { useUserStore } from '@/store'
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'

interface CategoryOption {
  id: number
  label: string
  status: CategoryStatus
}

const statusOptions: Array<{ label: string; value: DocumentStatus }> = [
  { label: '草稿', value: 'draft' },
  { label: '待审核', value: 'published' },
  { label: '已审核', value: 'reviewed' },
  { label: '已拒绝', value: 'rejected' },
]

const userStore = useUserStore()
const { hasPermission } = usePermissionCodes()
const canCreate = computed(() => hasPermission('kb:content:create'))
const canManage = computed(() => hasPermission('kb:content:manage'))
const currentUserID = computed(() => {
  let source = userStore.$state.userInfo as Record<string, unknown> | null | undefined
  if (!source || typeof source !== 'object') source = SafeStorage.get<Record<string, unknown>>(AppStorageKey.USER_INFO)
  const value = Number(source?.user_id ?? source?.id)
  return Number.isFinite(value) ? value : 0
})

const scope = ref<DocumentScope>('my')
const filters = reactive<{ keyword: string; category_id?: number; status?: DocumentStatus }>({
  keyword: '',
  category_id: undefined,
  status: undefined,
})
const documents = ref<Document[]>([])
const loading = ref(false)
const pagination = reactive({ current: 1, pageSize: 20, total: 0 })
let listSequence = 0
let readyListSequence = 0

const categories = ref<CategoryTreeNode[]>([])
const categoryOptions = computed<CategoryOption[]>(() => flattenCategories(categories.value))
const activeCategoryOptions = computed(() => categoryOptions.value.filter((item) => item.status === 'active'))
const categoryLabels = computed(() => new Map(categoryOptions.value.map((item) => [item.id, item.label])))

const selected = ref<Document | null>(null)
const detailLoading = ref(false)
let detailSequence = 0
const favoriteLoading = ref(false)

const formVisible = ref(false)
const formRef = ref<FormInstance>()
const editing = ref<Document | null>(null)
const saving = ref(false)
const originalCategoryUnavailable = ref(false)
const form = reactive<{
  title: string
  description: string
  content: string
  type: DocumentType
  category_id?: number
  sub_category: string
  keywords: string
  tags: string
  detection_point_ids: string
  remarks: string
}>({
  title: '',
  description: '',
  content: '',
  type: 'common' as DocumentType,
  category_id: undefined,
  sub_category: '',
  keywords: '',
  tags: '',
  detection_point_ids: '',
  remarks: '',
})

function flattenCategories(nodes: CategoryTreeNode[], prefix = ''): CategoryOption[] {
  return nodes.flatMap((node) => {
    const label = prefix ? `${prefix} / ${node.name}` : node.name
    return [{ id: node.id, label, status: node.status }, ...flattenCategories(node.children || [], label)]
  })
}

function categoryName(id: number) {
  return categoryLabels.value.get(id) || (id ? `分类 ${id}` : '未分类')
}

/** 按服务端规则判断文档是否可修改。 */
function canMutate(document: Document) {
  if (document.status === 'published') return false
  if (document.status === 'reviewed') return canManage.value
  if (document.status !== 'draft' && document.status !== 'rejected') return false
  return canManage.value || (canCreate.value && currentUserID.value > 0 && document.author_id === currentUserID.value)
}

function canEdit(document: Document) {
  return canMutate(document)
}

function canSubmit(document: Document) {
  return (document.status === 'draft' || document.status === 'rejected') && canMutate(document)
}

function canDelete(document: Document) {
  return canMutate(document)
}

async function loadCategories() {
  try {
    const reply = await fetchCategoryTree()
    if (reply.code !== 0) throw new Error(reply.message || '获取分类失败')
    categories.value = reply.details || []
  } catch (error) {
    categories.value = []
    showRequestError(error, '获取分类失败')
  }
}

/** 加载当前范围文档，忽略过期请求。 */
async function loadDocuments() {
  const sequence = ++listSequence
  readyListSequence = 0
  ++detailSequence
  selected.value = null
  detailLoading.value = false
  loading.value = true
  try {
    const reply = await fetchDocumentList({
      page: pagination.current,
      page_size: pagination.pageSize,
      scope: scope.value,
      keyword: filters.keyword.trim() || undefined,
      category_id: filters.category_id,
      status: scope.value === 'my' ? filters.status : undefined,
    })
    if (sequence !== listSequence) return
    if (reply.code !== 0) throw new Error(reply.message || '获取文档列表失败')
    documents.value = reply.details?.data || []
    pagination.total = reply.details?.total || 0
    readyListSequence = sequence
  } catch (error) {
    if (sequence === listSequence) {
      documents.value = []
      pagination.total = 0
      showRequestError(error, '获取文档列表失败')
    }
  } finally {
    if (sequence === listSequence) loading.value = false
  }
}

async function selectDocument(document: Document) {
  const listContext = listSequence
  if (loading.value || readyListSequence !== listContext || !documents.value.some((item) => item.id === document.id)) return
  const sequence = ++detailSequence
  detailLoading.value = true
  try {
    const reply = await fetchDocumentDetail(document.id)
    if (
      sequence !== detailSequence ||
      listContext !== listSequence ||
      readyListSequence !== listContext ||
      !documents.value.some((item) => item.id === document.id)
    )
      return
    if (reply.code !== 0) throw new Error(reply.message || '获取文档详情失败')
    selected.value = reply.details
  } catch (error) {
    if (sequence === detailSequence) showRequestError(error, '获取文档详情失败')
  } finally {
    if (sequence === detailSequence) detailLoading.value = false
  }
}

function handleScopeChange() {
  pagination.current = 1
  selected.value = null
  if (scope.value === 'all') filters.status = undefined
  loadDocuments()
}

function search() {
  pagination.current = 1
  loadDocuments()
}

function resetFilters() {
  Object.assign(filters, { keyword: '', category_id: undefined, status: undefined })
  pagination.current = 1
  loadDocuments()
}

function changePage(page: number) {
  pagination.current = page
  loadDocuments()
}

function resetForm() {
  Object.assign(form, {
    title: '',
    description: '',
    content: '',
    type: 'common',
    category_id: undefined,
    sub_category: '',
    keywords: '',
    tags: '',
    detection_point_ids: '',
    remarks: '',
  })
}

function openCreate() {
  if (saving.value) return
  editing.value = null
  originalCategoryUnavailable.value = false
  resetForm()
  formVisible.value = true
}

function openEdit() {
  if (!selected.value || !canEdit(selected.value) || saving.value) return
  editing.value = selected.value
  const categoryAvailable = activeCategoryOptions.value.some((category) => category.id === selected.value?.category_id)
  originalCategoryUnavailable.value = !categoryAvailable
  Object.assign(form, {
    title: selected.value.title,
    description: selected.value.description,
    content: selected.value.content,
    type: selected.value.type,
    category_id: categoryAvailable ? selected.value.category_id : undefined,
    sub_category: selected.value.sub_category,
    keywords: selected.value.keywords,
    tags: selected.value.tags,
    detection_point_ids: selected.value.detection_point_ids,
    remarks: selected.value.remarks,
  })
  formVisible.value = true
}

function closeForm() {
  if (saving.value) return
  formVisible.value = false
  editing.value = null
  originalCategoryUnavailable.value = false
  formRef.value?.clearValidate()
}

async function saveDocument() {
  if (saving.value) return
  saving.value = true
  try {
    if (await formRef.value?.validate()) return
    const categoryID = form.category_id
    if (!categoryID || !activeCategoryOptions.value.some((category) => category.id === categoryID)) {
      originalCategoryUnavailable.value = Boolean(editing.value)
      Message.warning('请选择启用分类')
      return
    }
    const wasCreate = !editing.value
    const payload = { ...form, category_id: categoryID, title: form.title.trim(), content: form.content.trim() }
    const reply = editing.value ? await updateDocument({ ...payload, id: editing.value.id }) : await createDocument(payload)
    if (reply.code !== 0) throw new Error(reply.message || '保存文档失败')
    Message.success(editing.value ? '文档已更新' : '文档已创建')
    const savedID = reply.details.id
    formVisible.value = false
    editing.value = null
    originalCategoryUnavailable.value = false
    formRef.value?.clearValidate()
    if (wasCreate && scope.value !== 'my') {
      scope.value = 'my'
      filters.status = undefined
      pagination.current = 1
    }
    await loadDocuments()
    const current = documents.value.find((item) => item.id === savedID)
    if (current) await selectDocument(current)
  } catch (error) {
    showRequestError(error, '保存文档失败')
  } finally {
    saving.value = false
  }
}

function canCloseForm() {
  return !saving.value
}

function handleFormVisibleChange(visible: boolean) {
  if (!visible) closeForm()
}

function confirmPublish() {
  if (!selected.value) return
  const document = selected.value
  Modal.confirm({
    title: '提交审核',
    content: `确认提交「${document.title}」审核吗？提交后将变为只读。`,
    onOk: async () => {
      try {
        const reply = await publishDocument(document.id)
        if (reply.code !== 0) throw new Error(reply.message || '提交审核失败')
        Message.success(reply.message || '已提交审核')
        await loadDocuments()
        const current = documents.value.find((item) => item.id === document.id)
        if (current) await selectDocument(current)
      } catch (error) {
        showRequestError(error, '提交审核失败')
      }
    },
  })
}

function confirmDelete() {
  if (!selected.value) return
  const document = selected.value
  Modal.confirm({
    title: '确认删除',
    content: `确认删除「${document.title}」吗？删除后可在回收站恢复。`,
    onOk: async () => {
      try {
        const reply = await deleteDocument(document.id)
        if (reply.code !== 0) throw new Error(reply.message || '删除文档失败')
        Message.success('文档已移入回收站')
        selected.value = null
        await loadDocuments()
      } catch (error) {
        showRequestError(error, '删除文档失败')
      }
    },
  })
}

async function toggleFavorite() {
  if (!selected.value || favoriteLoading.value) return
  const documentID = selected.value.id
  const wasFavorited = selected.value.is_favorited
  const listContext = listSequence
  const readyListContext = readyListSequence
  const detailContext = detailSequence
  const isCurrentContext = () =>
    selected.value?.id === documentID &&
    listSequence === listContext &&
    readyListSequence === readyListContext &&
    detailSequence === detailContext
  favoriteLoading.value = true
  try {
    const reply = wasFavorited ? await unfavoriteDocument(documentID) : await favoriteDocument(documentID)
    if (reply.code !== 0) throw new Error(reply.message || '收藏操作失败')
    if (!isCurrentContext() || !selected.value) return
    selected.value = { ...selected.value, is_favorited: !wasFavorited }
    Message.success(wasFavorited ? '已取消收藏' : '已收藏')
  } catch (error) {
    if (isCurrentContext()) showRequestError(error, '收藏操作失败')
  } finally {
    favoriteLoading.value = false
  }
}

function showRequestError(error: unknown, fallback: string) {
  if (axios.isAxiosError(error)) {
    const status = error.response?.status
    const message = (error.response?.data as { message?: string } | undefined)?.message
    if (status === 403) return Message.error('无操作权限')
    if (status === 400 || status === 409) return Message.error(message || fallback)
  }
  Message.error(error instanceof Error && error.message ? error.message : fallback)
}

function statusText(status: DocumentStatus) {
  return { draft: '草稿', published: '待审核', reviewed: '已审核', rejected: '已拒绝' }[status]
}

function statusColor(status: DocumentStatus) {
  return { draft: 'gray', published: 'orange', reviewed: 'green', rejected: 'red' }[status]
}

function typeText(type: DocumentType) {
  return documentTypeOptions.find((item) => item.value === type)?.label || type
}

function formatTime(value: string | null) {
  return value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-'
}

watch(
  activeCategoryOptions,
  (options) => {
    const document = editing.value
    if (!formVisible.value || !document || !originalCategoryUnavailable.value) return
    if (options.some((category) => category.id === document.category_id)) {
      form.category_id = document.category_id
      originalCategoryUnavailable.value = false
    }
  },
  { deep: true }
)

watch(
  () => form.category_id,
  (categoryID) => {
    if (categoryID && activeCategoryOptions.value.some((category) => category.id === categoryID)) {
      originalCategoryUnavailable.value = false
    }
  }
)

onMounted(async () => {
  await Promise.all([loadCategories(), loadDocuments()])
})
</script>

<style scoped lang="less">
.document-page {
  padding: 0 20px 20px;
}
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.filters {
  margin-bottom: 16px;
}
.content-layout {
  display: grid;
  grid-template-columns: 360px minmax(0, 1fr);
  gap: 16px;
  min-height: 560px;
}
.document-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  border-right: 1px solid var(--color-border);
  padding-right: 16px;
}
.list-spin,
.detail-spin {
  width: 100%;
}
.document-item {
  width: 100%;
  border: 1px solid transparent;
  background: none;
  border-radius: 6px;
  padding: 12px;
  text-align: left;
  cursor: pointer;
  color: inherit;
}
.document-item:hover {
  background: var(--color-fill-2);
}
.document-item.active {
  background: rgb(var(--primary-1));
  border-color: rgb(var(--primary-3));
}
.document-title {
  display: block;
  font-weight: 600;
  margin-bottom: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.document-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  color: var(--color-text-3);
  font-size: 12px;
}
.detail-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.detail-header h2 {
  margin: 0 0 8px;
}
.metadata {
  margin-bottom: 20px;
}
.category-warning {
  margin-bottom: 16px;
}
.document-content {
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.8;
  font-family: inherit;
  background: var(--color-fill-1);
  padding: 16px;
  border-radius: 6px;
}
@media (max-width: 1000px) {
  .content-layout {
    grid-template-columns: 1fr;
  }
  .document-list {
    border-right: 0;
    border-bottom: 1px solid var(--color-border);
    padding: 0 0 16px;
  }
}
</style>
