<template>
  <div class="faq-page">
    <Breadcrumb :items="['知识管理', 'FAQ 管理']" />

    <a-card class="general-card" :bordered="false">
      <div class="page-header">
        <a-tabs v-model:active-key="activeScope" @change="handleScopeChange">
          <a-tab-pane key="my" title="我的 FAQ" />
          <a-tab-pane key="all" title="全部 FAQ" />
        </a-tabs>
        <a-space>
          <a-button v-if="canCreate" type="primary" @click="openCreate">
            <template #icon><icon-plus /></template>
            新建 FAQ
          </a-button>
          <a-button :loading="loading" @click="fetchData">
            <template #icon><icon-refresh /></template>
            刷新
          </a-button>
        </a-space>
      </div>

      <a-form :model="filters" layout="inline" class="filter-form" @submit-success="handleSearch">
        <a-form-item label="关键词" field="keyword">
          <a-input v-model="filters.keyword" placeholder="搜索问题、答案或方案" allow-clear />
        </a-form-item>
        <a-form-item label="公共分类" field="category_id">
          <a-select
            v-model="filters.category_id"
            placeholder="全部分类"
            allow-clear
            allow-search
            :loading="categoryLoading"
            style="width: 220px"
          >
            <a-option v-for="category in categoryFilterOptions" :key="category.id" :value="category.id">
              {{ category.label }}{{ category.status === 'inactive' ? '（停用）' : '' }}
            </a-option>
          </a-select>
        </a-form-item>
        <a-form-item label="状态" field="status">
          <a-select v-model="filters.status" placeholder="全部状态" allow-clear style="width: 120px">
            <a-option v-for="option in visibleStatusOptions" :key="option.value" :value="option.value">
              {{ option.label }}
            </a-option>
          </a-select>
        </a-form-item>
        <a-form-item label="优先级" field="priority">
          <a-select v-model="filters.priority" placeholder="全部优先级" allow-clear style="width: 120px">
            <a-option v-for="option in faqPriorityOptions" :key="option.value" :value="option.value">
              {{ option.label }}
            </a-option>
          </a-select>
        </a-form-item>
        <a-form-item label="问题类型" field="problem_type">
          <a-select v-model="filters.problem_type" placeholder="请选择或输入" allow-clear allow-search allow-create style="width: 150px">
            <a-option v-for="option in faqProblemTypeOptions" :key="option.value" :value="option.value">
              {{ option.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="handleReset">重置</a-button>
          </a-space>
        </a-form-item>
      </a-form>

      <a-table
        row-key="id"
        :data="tableData"
        :columns="columns"
        :loading="loading"
        :pagination="pagination"
        :scroll="{ x: 1380 }"
        @page-change="handlePageChange"
        @page-size-change="handlePageSizeChange"
      >
        <template #question="{ record }">
          <a-button type="text" class="question-button" @click="openDetail(record)">
            {{ record.question }}
          </a-button>
        </template>
        <template #category="{ record }">
          {{ categoryName(record.category_id) }}
        </template>
        <template #priority="{ record }">
          <a-tag :color="getFaqPriorityColor(record.priority)">{{ getFaqPriorityText(record.priority) }}</a-tag>
        </template>
        <template #status="{ record }">
          <a-tag :color="getFaqStatusColor(record.status)">{{ getFaqStatusText(record.status) }}</a-tag>
        </template>
        <template #author="{ record }">
          {{ record.author_name || `用户 ${record.author_id}` }}
        </template>
        <template #updated_at="{ record }">
          {{ formatTime(record.updated_at) }}
        </template>
        <template #actions="{ record }">
          <a-space :size="4">
            <a-button type="text" size="small" @click="openDetail(record)">查看</a-button>
            <a-button type="text" size="small" :loading="isActionLoading(record.id, 'favorite')" @click="toggleFavorite(record)">
              <template #icon>
                <icon-star-fill v-if="record.is_favorited" />
                <icon-star v-else />
              </template>
              {{ record.is_favorited ? '取消收藏' : '收藏' }}
            </a-button>
            <a-button v-if="canEdit(record)" type="text" size="small" @click="openEdit(record)">编辑</a-button>
            <a-button
              v-if="canSubmit(record)"
              type="text"
              size="small"
              :loading="isActionLoading(record.id, 'publish')"
              @click="confirmPublish(record)"
            >
              提交审核
            </a-button>
            <a-button
              v-if="canDelete(record)"
              type="text"
              size="small"
              status="danger"
              :loading="isActionLoading(record.id, 'delete')"
              @click="confirmDelete(record)"
            >
              删除
            </a-button>
          </a-space>
        </template>
        <template #empty>
          <a-empty :description="activeScope === 'my' ? '暂无我的 FAQ' : '暂无已审核 FAQ'" />
        </template>
      </a-table>
    </a-card>

    <FaqFormDrawer
      v-model:visible="formVisible"
      :faq="editingFaq"
      :categories="categoryFormOptions"
      :submitting="formSubmitting"
      @submit="saveFaq"
    />

    <FaqDetailDrawer
      :visible="detailVisible"
      :faq="detailFaq"
      :category-name="detailFaq ? categoryName(detailFaq.category_id) : ''"
      :loading="detailLoading"
      :favorite-loading="Boolean(detailFaq && isActionLoading(detailFaq.id, 'favorite'))"
      :action-loading="Boolean(detailFaq && (isActionLoading(detailFaq.id, 'publish') || isActionLoading(detailFaq.id, 'delete')))"
      :can-edit="Boolean(detailFaq && canEdit(detailFaq))"
      :can-submit="Boolean(detailFaq && canSubmit(detailFaq))"
      :can-delete="Boolean(detailFaq && canDelete(detailFaq))"
      @update:visible="handleDetailVisible"
      @edit="openEditFromDetail"
      @submit="confirmPublish"
      @favorite="toggleFavorite"
      @delete="confirmDelete"
    />
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, reactive, ref } from 'vue'
import axios from 'axios'
import dayjs from 'dayjs'
import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus, IconRefresh, IconStar, IconStarFill } from '@arco-design/web-vue/es/icon'
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
import {
  createFaq,
  deleteFaq,
  faqPriorityOptions,
  faqProblemTypeOptions,
  faqStatusOptions,
  favoriteFaq,
  fetchFaqDetail,
  fetchFaqList,
  fetchGeneralCategoryTree,
  getFaqPriorityColor,
  getFaqPriorityText,
  getFaqStatusColor,
  getFaqStatusText,
  publishFaq,
  unfavoriteFaq,
  updateFaq,
  type CategoryTreeNode,
  type Faq,
  type FaqFormData,
  type FaqPriority,
  type FaqScope,
  type FaqStatus,
  type KbReply,
} from '@/api/kb/faq'
import usePermissionCodes from '@/hooks/usePermissionCodes'
import { useUserStore } from '@/store'
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
import FaqFormDrawer, { type FaqCategoryOption } from './components/FaqFormDrawer.vue'
import FaqDetailDrawer from './components/FaqDetailDrawer.vue'

type ActionType = 'favorite' | 'publish' | 'delete'

const columns: TableColumnData[] = [
  { title: '问题', dataIndex: 'question', slotName: 'question', width: 320, ellipsis: true },
  { title: '公共分类', dataIndex: 'category_id', slotName: 'category', width: 200, ellipsis: true },
  { title: '优先级', dataIndex: 'priority', slotName: 'priority', width: 90, align: 'center' },
  { title: '状态', dataIndex: 'status', slotName: 'status', width: 100, align: 'center' },
  { title: '作者', dataIndex: 'author_name', slotName: 'author', width: 130, ellipsis: true },
  { title: '更新时间', dataIndex: 'updated_at', slotName: 'updated_at', width: 170, align: 'center' },
  { title: '操作', slotName: 'actions', width: 370, fixed: 'right' },
]

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

const activeScope = ref<FaqScope>('my')
const filters = reactive<{
  keyword: string
  category_id?: number
  status?: FaqStatus
  priority?: FaqPriority
  problem_type: string
}>({
  keyword: '',
  category_id: undefined,
  status: undefined,
  priority: undefined,
  problem_type: '',
})
const visibleStatusOptions = computed(() =>
  activeScope.value === 'all' ? faqStatusOptions.filter((option) => option.value === 'reviewed') : faqStatusOptions
)

const tableData = ref<Faq[]>([])
const loading = ref(false)
const pagination = reactive({
  current: 1,
  pageSize: 20,
  total: 0,
  showTotal: true,
  showPageSize: true,
})
let listRequestSequence = 0

const categoryLoading = ref(false)
const categoryFilterOptions = ref<FaqCategoryOption[]>([])
const categoryFormOptions = computed(() => categoryFilterOptions.value.filter((category) => category.status === 'active'))
const categoryLabels = ref(new Map<number, string>())

const formVisible = ref(false)
const formSubmitting = ref(false)
const editingFaq = ref<Faq | null>(null)

const detailVisible = ref(false)
const detailLoading = ref(false)
const detailFaq = ref<Faq | null>(null)
let detailRequestSequence = 0

const actionState = reactive<{ id: number; type: ActionType | '' }>({ id: 0, type: '' })

/** 将登录信息里的 ID 收敛为可比较的数值。 */
function numericId(value: unknown) {
  if (value === null || value === undefined || value === '') return 0
  const id = Number(value)
  return Number.isFinite(id) ? id : 0
}

/** 按服务端规则判断当前用户能否修改 FAQ。 */
function canMutate(faq: Faq) {
  if (faq.status === 'published') return false
  if (faq.status === 'reviewed') return canManage.value
  if (faq.status !== 'draft' && faq.status !== 'rejected') return false
  return canManage.value || (canCreate.value && currentUserId.value > 0 && faq.author_id === currentUserId.value)
}

function canEdit(faq: Faq) {
  return canMutate(faq)
}

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

function canDelete(faq: Faq) {
  return canMutate(faq)
}

/** 加载当前范围的 FAQ，过期响应不会覆盖新查询。 */
async function fetchData() {
  const sequence = ++listRequestSequence
  loading.value = true
  try {
    const reply = await fetchFaqList({
      page: pagination.current,
      page_size: pagination.pageSize,
      scope: activeScope.value,
      keyword: filters.keyword.trim() || undefined,
      category_id: filters.category_id,
      status: filters.status,
      priority: filters.priority,
      problem_type: filters.problem_type || undefined,
    })
    if (sequence !== listRequestSequence) return
    if (reply.code !== 0) {
      tableData.value = []
      pagination.total = 0
      Message.error(reply.message || '获取 FAQ 列表失败')
      return
    }
    tableData.value = reply.details?.data || []
    pagination.total = reply.details?.total || 0
  } catch (error) {
    if (sequence !== listRequestSequence) return
    tableData.value = []
    pagination.total = 0
    showRequestError(error, '获取 FAQ 列表失败')
  } finally {
    if (sequence === listRequestSequence) loading.value = false
  }
}

/** 加载全部公共分类，同时为表单保留 active 子集。 */
async function loadCategories() {
  categoryLoading.value = true
  try {
    const reply = await fetchGeneralCategoryTree()
    if (reply.code !== 0) {
      Message.error(reply.message || '获取公共分类失败')
      return
    }
    const labels = new Map<number, string>()
    const categories: FaqCategoryOption[] = []
    flattenCategories(reply.details || [], [], labels, categories)
    categoryLabels.value = labels
    categoryFilterOptions.value = categories
  } catch (error) {
    showRequestError(error, '获取公共分类失败')
  } finally {
    categoryLoading.value = false
  }
}

/** 展开分类树，并保留每个节点的父级、层级、路径和状态。 */
function flattenCategories(nodes: CategoryTreeNode[], parentNames: string[], labels: Map<number, string>, categories: FaqCategoryOption[]) {
  nodes.forEach((node) => {
    const names = [...parentNames, node.name]
    const label = names.join(' / ')
    labels.set(node.id, node.status === 'inactive' ? `${label}（停用）` : label)
    categories.push({
      id: node.id,
      name: node.name,
      label,
      parentId: node.parent_id,
      level: node.level,
      path: node.path,
      status: node.status,
    })
    flattenCategories(node.children || [], names, labels, categories)
  })
}

function categoryName(categoryId: number) {
  return categoryLabels.value.get(categoryId) || '-'
}

/** 切换可见范围时清除范围不兼容的状态筛选。 */
function changeScope(scope: FaqScope, load = true) {
  activeScope.value = scope
  pagination.current = 1
  filters.status = undefined
  handleDetailVisible(false)
  if (load) fetchData()
}

function handleScopeChange(scope: string | number) {
  changeScope(scope as FaqScope)
}

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

function handleReset() {
  filters.keyword = ''
  filters.category_id = undefined
  filters.status = undefined
  filters.priority = undefined
  filters.problem_type = ''
  pagination.current = 1
  fetchData()
}

function handlePageChange(page: number) {
  pagination.current = page
  fetchData()
}

function handlePageSizeChange(pageSize: number) {
  pagination.current = 1
  pagination.pageSize = pageSize
  fetchData()
}

function openCreate() {
  if (!canCreate.value) {
    Message.error('无操作权限')
    return
  }
  editingFaq.value = null
  formVisible.value = true
}

function openEdit(faq: Faq) {
  if (!canEdit(faq)) {
    Message.error('无操作权限')
    return
  }
  editingFaq.value = { ...faq }
  formVisible.value = true
}

function openEditFromDetail(faq: Faq) {
  handleDetailVisible(false)
  openEdit(faq)
}

/** 保存表单；仅接口成功后关闭抽屉。 */
async function saveFaq(values: FaqFormData, done: () => void) {
  if (formSubmitting.value) {
    done()
    return
  }
  formSubmitting.value = true
  const target = editingFaq.value
  try {
    if ((!target && !canCreate.value) || (target && !canEdit(target))) {
      Message.error('无操作权限')
      return
    }
    const reply = target ? await updateFaq({ id: target.id, ...values }) : await createFaq(values)
    if (!showReplyResult(reply, target ? '保存 FAQ 失败' : '创建 FAQ 失败')) return
    Message.success(target ? 'FAQ 已保存' : 'FAQ 已创建')
    formVisible.value = false
    editingFaq.value = null
    if (!target) changeScope('my', false)
    else pagination.current = 1
    await fetchData()
  } catch (error) {
    showRequestError(error, target ? '保存 FAQ 失败' : '创建 FAQ 失败')
  } finally {
    formSubmitting.value = false
    done()
  }
}

/** 获取最新详情，关闭抽屉后自动废弃仍在途的响应。 */
async function openDetail(faq: Faq) {
  const sequence = ++detailRequestSequence
  detailVisible.value = true
  detailLoading.value = true
  detailFaq.value = null
  try {
    const reply = await fetchFaqDetail(faq.id)
    if (sequence !== detailRequestSequence) return
    if (!showReplyResult(reply, '获取 FAQ 详情失败')) return
    detailFaq.value = reply.details
    updateVisibleFaq(reply.details)
  } catch (error) {
    if (sequence !== detailRequestSequence) return
    showRequestError(error, '获取 FAQ 详情失败')
  } finally {
    if (sequence === detailRequestSequence) detailLoading.value = false
  }
}

function handleDetailVisible(visible: boolean) {
  detailVisible.value = visible
  if (!visible) {
    detailRequestSequence += 1
    detailLoading.value = false
    detailFaq.value = null
  }
}

function confirmPublish(faq: Faq) {
  if (!canSubmit(faq)) {
    Message.error('无操作权限')
    return
  }
  Modal.confirm({
    title: '提交审核',
    content: `确认提交 FAQ「${faq.question}」进行审核吗？提交后将暂时不能修改。`,
    okText: '提交审核',
    onOk: () => submitForReview(faq),
  })
}

async function submitForReview(faq: Faq) {
  if (!beginAction(faq.id, 'publish')) return false
  try {
    const reply = await publishFaq(faq.id)
    if (!showReplyResult(reply, '提交审核失败')) return false
    Message.success(reply.details || '已提交审核')
    handleDetailVisible(false)
    await fetchData()
    return true
  } catch (error) {
    showRequestError(error, '提交审核失败')
    return false
  } finally {
    endAction(faq.id, 'publish')
  }
}

function confirmDelete(faq: Faq) {
  if (!canDelete(faq)) {
    Message.error('无操作权限')
    return
  }
  Modal.confirm({
    title: '删除 FAQ',
    content: `确认删除 FAQ「${faq.question}」吗？删除后将移入回收站。`,
    okText: '删除',
    okButtonProps: { status: 'danger' },
    onOk: () => removeFaq(faq),
  })
}

async function removeFaq(faq: Faq) {
  if (!beginAction(faq.id, 'delete')) return false
  try {
    const reply = await deleteFaq(faq.id)
    if (!showReplyResult(reply, '删除 FAQ 失败')) return false
    Message.success('FAQ 已移入回收站')
    if (detailFaq.value?.id === faq.id) handleDetailVisible(false)
    if (tableData.value.length === 1 && pagination.current > 1) pagination.current -= 1
    await fetchData()
    return true
  } catch (error) {
    showRequestError(error, '删除 FAQ 失败')
    return false
  } finally {
    endAction(faq.id, 'delete')
  }
}

async function toggleFavorite(faq: Faq) {
  if (!beginAction(faq.id, 'favorite')) return
  const nextFavorited = !faq.is_favorited
  try {
    const reply = nextFavorited ? await favoriteFaq(faq.id) : await unfavoriteFaq(faq.id)
    if (!showReplyResult(reply, nextFavorited ? '收藏失败' : '取消收藏失败')) return
    updateFavoriteState(faq.id, nextFavorited)
    Message.success(nextFavorited ? '已收藏' : '已取消收藏')
  } catch (error) {
    showRequestError(error, nextFavorited ? '收藏失败' : '取消收藏失败')
  } finally {
    endAction(faq.id, 'favorite')
  }
}

function updateFavoriteState(id: number, isFavorited: boolean) {
  const row = tableData.value.find((item) => item.id === id)
  if (row) row.is_favorited = isFavorited
  if (detailFaq.value?.id === id) detailFaq.value.is_favorited = isFavorited
}

function updateVisibleFaq(faq: Faq) {
  const index = tableData.value.findIndex((item) => item.id === faq.id)
  if (index >= 0) tableData.value[index] = { ...tableData.value[index], ...faq }
}

function beginAction(id: number, type: ActionType) {
  if (actionState.type) return false
  actionState.id = id
  actionState.type = type
  return true
}

function endAction(id: number, type: ActionType) {
  if (actionState.id === id && actionState.type === type) {
    actionState.id = 0
    actionState.type = ''
  }
}

function isActionLoading(id: number, type: ActionType) {
  return actionState.id === id && actionState.type === type
}

/** 处理服务端返回的业务错误。 */
function showReplyResult(reply: KbReply<unknown>, fallback: string) {
  if (reply.code === 0) return true
  Message.error(reply.message || fallback)
  return false
}

/** 按 HTTP 状态展示鉴权与冲突错误。 */
function showRequestError(error: unknown, fallback: string) {
  if (!axios.isAxiosError(error)) {
    Message.error(fallback)
    return
  }
  const status = error.response?.status
  const data = error.response?.data
  const message = isReplyLike(data) ? data.message : ''
  if (status === 403) {
    Message.error('无操作权限')
    return
  }
  if (status === 409) {
    Message.error(message || fallback)
    return
  }
  Message.error(message || fallback)
}

function isReplyLike(value: unknown): value is Partial<KbReply<unknown>> {
  return Boolean(value && typeof value === 'object' && 'message' in value)
}

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

onMounted(() => {
  loadCategories()
  fetchData()
})
</script>

<script lang="ts">
export default {
  name: 'KbFaqManage',
}
</script>

<style scoped lang="less">
.faq-page {
  padding: 0 20px 20px;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;

  :deep(.arco-tabs) {
    flex: 1;
  }
}

.filter-form {
  margin: 8px 0 20px;
  padding: 16px 16px 0;
  background: var(--color-fill-1);
  border-radius: 4px;
}

.question-button {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
