<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';

import { $t } from '@easyflow/locales';
import { downloadFileFromBlob } from '@easyflow/utils';

import {
  Bottom,
  CirclePlus,
  Delete,
  Download,
  Edit,
  FolderAdd,
  MoreFilled,
  Plus,
  RefreshRight,
  Search,
  Top,
  Upload,
} from '@element-plus/icons-vue';
import {
  ElButton,
  ElDropdown,
  ElDropdownItem,
  ElDropdownMenu,
  ElInput,
  ElMessage,
  ElMessageBox,
  ElTable,
  ElTableColumn,
  ElTree,
} from 'element-plus';

import { api } from '#/api/request';
import PageData from '#/components/page/PageData.vue';
import { buildKnowledgePath } from '#/views/ai/documentCollection/share-path';

import FaqCategoryDialog from './FaqCategoryDialog.vue';
import FaqEditDialog from './FaqEditDialog.vue';
import FaqImportDialog from './FaqImportDialog.vue';

const props = defineProps({
  knowledgeId: {
    type: String,
    required: true,
  },
  manageable: {
    type: Boolean,
    default: true,
  },
  requestClient: {
    type: Object as any,
    default: () => api,
  },
  endpointPrefix: {
    type: String,
    default: '',
  },
});

const pageDataRef = ref();
const dialogVisible = ref(false);
const categoryDialogVisible = ref(false);
const editData = ref<any>({});
const categoryEditData = ref<any>({});
const categoryDialogTitle = ref('');
const categoryDialogDisableParent = ref(false);
const selectedCategoryId = ref<string>('all');
const searchKeyword = ref('');
const categoryTree = ref<any[]>([]);
const categoryParentOptions = ref<any[]>([]);
const categoryActionLoading = ref(false);
const importDialogVisible = ref(false);
const templateDownloadLoading = ref(false);
const exportLoading = ref(false);

const baseQueryParams = ref({
  collectionId: props.knowledgeId,
});

const treeData = computed(() => [
  {
    id: 'all',
    categoryName: $t('documentCollection.faq.allFaq'),
    isVirtual: true,
    levelNo: 0,
    children: categoryTree.value,
  },
]);

const refreshList = () => {
  const query: Record<string, any> = {};
  if (searchKeyword.value.trim()) {
    query.question = searchKeyword.value.trim();
  }
  if (selectedCategoryId.value !== 'all') {
    query.categoryId = selectedCategoryId.value;
  }
  pageDataRef.value?.setQuery(query);
};

const reloadCategoryTree = async () => {
  const res = await props.requestClient.get(
    buildKnowledgePath(props.endpointPrefix, '/api/v1/faqCategory/list'),
    {
      params: {
        collectionId: props.knowledgeId,
        asTree: true,
      },
    },
  );
  if (res.errorCode === 0) {
    categoryTree.value = normalizeCategoryTree(res.data || []);

    if (
      selectedCategoryId.value !== 'all' &&
      !hasCategoryId(categoryTree.value, selectedCategoryId.value)
    ) {
      selectedCategoryId.value = 'all';
    }
    refreshList();
  } else {
    ElMessage.error(res.message || $t('message.getDataError'));
  }
};

const normalizeCategoryTree = (nodes: any[]): any[] => {
  return (nodes || []).map((node) => ({
    ...node,
    id: String(node.id),
    parentId:
      node.parentId === undefined || node.parentId === null
        ? '0'
        : String(node.parentId),
    children: normalizeCategoryTree(node.children || []),
  }));
};

const hasCategoryId = (nodes: any[], id: string): boolean => {
  for (const node of nodes || []) {
    if (String(node.id) === id) {
      return true;
    }
    if (node.children?.length && hasCategoryId(node.children, id)) {
      return true;
    }
  }
  return false;
};

const handleSearch = () => {
  searchKeyword.value = searchKeyword.value.trim();
  refreshList();
};

const handleResetSearch = () => {
  searchKeyword.value = '';
  refreshList();
};

const openAddDialog = () => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  editData.value = {
    collectionId: props.knowledgeId,
    categoryId:
      selectedCategoryId.value === 'all' ? null : selectedCategoryId.value,
    answerHtml: '',
    question: '',
  };
  dialogVisible.value = true;
};

const downloadImportTemplate = async () => {
  if (templateDownloadLoading.value) {
    return;
  }
  templateDownloadLoading.value = true;
  try {
    const blob = await props.requestClient.download(
      buildKnowledgePath(
        props.endpointPrefix,
        `/api/v1/faqItem/downloadImportTemplate?collectionId=${props.knowledgeId}`,
      ),
    );
    downloadFileFromBlob({
      fileName: 'faq_import_template.xlsx',
      source: blob,
    });
  } finally {
    templateDownloadLoading.value = false;
  }
};

const exportFaqExcel = async () => {
  if (exportLoading.value) {
    return;
  }
  exportLoading.value = true;
  try {
    const blob = await props.requestClient.download(
      buildKnowledgePath(
        props.endpointPrefix,
        `/api/v1/faqItem/exportExcel?collectionId=${props.knowledgeId}`,
      ),
    );
    downloadFileFromBlob({
      fileName: 'faq_export.xlsx',
      source: blob,
    });
  } finally {
    exportLoading.value = false;
  }
};

const handleImportSuccess = async () => {
  await reloadCategoryTree();
  refreshList();
};

const handleMoreActionCommand = (command: string) => {
  if (command === 'import') {
    if (!props.manageable) {
      ElMessage.warning($t('documentCollection.managePermissionHint'));
      return;
    }
    importDialogVisible.value = true;
    return;
  }
  if (command === 'downloadTemplate') {
    downloadImportTemplate();
    return;
  }
  if (command === 'export') {
    exportFaqExcel();
  }
};

const openEditDialog = (row: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  editData.value = {
    id: row.id,
    collectionId: row.collectionId,
    categoryId:
      row.categoryId === undefined || row.categoryId === null
        ? ''
        : String(row.categoryId),
    question: row.question,
    answerHtml: row.answerHtml,
    orderNo: row.orderNo,
  };
  dialogVisible.value = true;
};

const saveFaq = async (payload: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  const url = payload.id ? '/api/v1/faqItem/update' : '/api/v1/faqItem/save';
  const res = await props.requestClient.post(
    buildKnowledgePath(props.endpointPrefix, url),
    payload,
  );
  if (res.errorCode === 0) {
    ElMessage.success(
      payload.id ? $t('message.updateOkMessage') : $t('message.saveOkMessage'),
    );
    dialogVisible.value = false;
    refreshList();
  } else {
    ElMessage.error(res.message);
  }
};

const removeFaq = (row: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
    confirmButtonText: $t('button.confirm'),
    cancelButtonText: $t('button.cancel'),
    type: 'warning',
  }).then(() => {
    props.requestClient
      .post(
        buildKnowledgePath(props.endpointPrefix, '/api/v1/faqItem/remove'),
        {
          id: row.id,
        },
      )
      .then((res: any) => {
        if (res.errorCode === 0) {
          ElMessage.success($t('message.deleteOkMessage'));
          refreshList();
        } else {
          ElMessage.error(res.message);
        }
      });
  });
};

const handleCategoryClick = (data: any) => {
  selectedCategoryId.value = String(data.id);
  refreshList();
};

const openAddRootCategory = () => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  categoryDialogTitle.value = $t('documentCollection.faq.addCategory');
  categoryDialogDisableParent.value = false;
  categoryEditData.value = {
    collectionId: props.knowledgeId,
    parentId: '0',
    categoryName: '',
  };
  categoryParentOptions.value = buildParentOptions();
  categoryDialogVisible.value = true;
};

const openAddSiblingCategory = (node: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  categoryDialogTitle.value = $t('documentCollection.faq.addSiblingCategory');
  categoryDialogDisableParent.value = false;
  categoryEditData.value = {
    collectionId: props.knowledgeId,
    parentId:
      node.parentId === undefined || node.parentId === null
        ? '0'
        : String(node.parentId),
    categoryName: '',
  };
  categoryParentOptions.value = buildParentOptions();
  categoryDialogVisible.value = true;
};

const openAddChildCategory = (node: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  if (node.isDefault) {
    ElMessage.warning(
      $t('documentCollection.faq.defaultCategoryChildForbidden'),
    );
    return;
  }
  if (Number(node.levelNo) >= 3) {
    ElMessage.warning($t('documentCollection.faq.maxLevelTip'));
    return;
  }
  categoryDialogTitle.value = $t('documentCollection.faq.addChildCategory');
  categoryDialogDisableParent.value = false;
  categoryEditData.value = {
    collectionId: props.knowledgeId,
    parentId: String(node.id),
    categoryName: '',
  };
  categoryParentOptions.value = buildParentOptions();
  categoryDialogVisible.value = true;
};

const openEditCategory = (node: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  categoryDialogTitle.value = $t('documentCollection.faq.editCategory');
  categoryDialogDisableParent.value = !!node.isDefault;
  categoryEditData.value = {
    id: node.id,
    collectionId: node.collectionId,
    parentId:
      node.parentId === undefined || node.parentId === null
        ? '0'
        : String(node.parentId),
    categoryName: node.categoryName,
  };
  categoryParentOptions.value = buildParentOptions(String(node.id));
  categoryDialogVisible.value = true;
};

const removeCategory = (node: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  if (node.isDefault) {
    ElMessage.warning(
      $t('documentCollection.faq.defaultCategoryDeleteForbidden'),
    );
    return;
  }
  ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
    confirmButtonText: $t('button.confirm'),
    cancelButtonText: $t('button.cancel'),
    type: 'warning',
  }).then(async () => {
    const res = await props.requestClient.post(
      buildKnowledgePath(props.endpointPrefix, '/api/v1/faqCategory/remove'),
      { id: node.id },
    );
    if (res.errorCode === 0) {
      ElMessage.success($t('message.deleteOkMessage'));
      if (selectedCategoryId.value === String(node.id)) {
        selectedCategoryId.value = 'all';
      }
      await reloadCategoryTree();
    } else {
      ElMessage.error(res.message);
    }
  });
};

const toLevel = (value: any, fallback = 1) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
};

const findNodeById = (
  targetId: string,
  nodes: any[] = categoryTree.value,
): any => {
  for (const node of nodes || []) {
    if (String(node.id) === targetId) {
      return node;
    }
    if (node.children?.length) {
      const found = findNodeById(targetId, node.children);
      if (found) {
        return found;
      }
    }
  }
  return null;
};

const findNodeContext = (
  targetId: string,
  nodes: any[] = categoryTree.value,
  parent: any = null,
): any => {
  for (let i = 0; i < (nodes || []).length; i += 1) {
    const node = nodes[i];
    if (String(node.id) === targetId) {
      return {
        node,
        parent,
        siblings: nodes,
        index: i,
      };
    }
    if (node.children?.length) {
      const found = findNodeContext(targetId, node.children, node);
      if (found) {
        return found;
      }
    }
  }
  return null;
};

const findSubtreeMaxLevel = (node: any): number => {
  const current = toLevel(node?.levelNo, 1);
  let maxLevel = current;
  for (const child of node?.children || []) {
    maxLevel = Math.max(maxLevel, findSubtreeMaxLevel(child));
  }
  return maxLevel;
};

const canMoveUnderParent = (node: any, newParentLevel: number): boolean => {
  const currentLevel = toLevel(node?.levelNo, 1);
  const subtreeMaxLevel = findSubtreeMaxLevel(node);
  const targetLevel = newParentLevel + 1;
  const delta = targetLevel - currentLevel;
  return subtreeMaxLevel + delta <= 3;
};

const getChildrenByParentId = (parentId: string): any[] => {
  if (parentId === '0') {
    return categoryTree.value || [];
  }
  const parent = findNodeById(parentId);
  return parent?.children || [];
};

const updateCategoryRequest = async (payload: Record<string, any>) => {
  const res = await props.requestClient.post(
    buildKnowledgePath(props.endpointPrefix, '/api/v1/faqCategory/update'),
    payload,
  );
  if (res.errorCode !== 0) {
    throw new Error(res.message || $t('message.getDataError'));
  }
};

const persistSiblingOrder = async (siblings: any[]) => {
  for (const [i, item] of siblings.entries()) {
    await updateCategoryRequest({
      id: item.id,
      collectionId: item.collectionId,
      parentId:
        item.parentId === undefined || item.parentId === null
          ? '0'
          : String(item.parentId),
      categoryName: item.categoryName,
      sortNo: i,
    });
  }
};

const canMoveUp = (node: any): boolean => {
  const ctx = findNodeContext(String(node.id));
  if (!ctx || ctx.index <= 0) {
    return false;
  }
  const previousSibling = ctx.siblings[ctx.index - 1];
  return !previousSibling?.isDefault;
};

const canMoveDown = (node: any): boolean => {
  const ctx = findNodeContext(String(node.id));
  return !!ctx && ctx.index < ctx.siblings.length - 1;
};

const canPromote = (node: any): boolean => {
  const ctx = findNodeContext(String(node.id));
  if (!ctx || !ctx.parent) {
    return false;
  }
  const parentParentId =
    ctx.parent.parentId === undefined || ctx.parent.parentId === null
      ? '0'
      : String(ctx.parent.parentId);
  if (parentParentId === String(ctx.parent.id)) {
    return false;
  }
  const newParentLevel =
    parentParentId === '0'
      ? 0
      : toLevel(findNodeById(parentParentId)?.levelNo, 1);
  return canMoveUnderParent(node, newParentLevel);
};

const canDemote = (node: any): boolean => {
  const ctx = findNodeContext(String(node.id));
  if (!ctx || ctx.index <= 0) {
    return false;
  }
  const previousSibling = ctx.siblings[ctx.index - 1];
  if (previousSibling?.isDefault) {
    return false;
  }
  return canMoveUnderParent(node, toLevel(previousSibling?.levelNo, 1));
};

const moveCategoryUp = async (node: any) => {
  if (categoryActionLoading.value || !canMoveUp(node)) {
    return;
  }
  const ctx = findNodeContext(String(node.id));
  if (!ctx) {
    return;
  }
  const reordered = [...ctx.siblings];
  [reordered[ctx.index - 1], reordered[ctx.index]] = [
    reordered[ctx.index],
    reordered[ctx.index - 1],
  ];
  categoryActionLoading.value = true;
  try {
    await persistSiblingOrder(reordered);
    ElMessage.success($t('message.updateOkMessage'));
    await reloadCategoryTree();
  } catch (error: any) {
    ElMessage.error(error?.message || $t('message.getDataError'));
  } finally {
    categoryActionLoading.value = false;
  }
};

const moveCategoryDown = async (node: any) => {
  if (categoryActionLoading.value || !canMoveDown(node)) {
    return;
  }
  const ctx = findNodeContext(String(node.id));
  if (!ctx) {
    return;
  }
  const reordered = [...ctx.siblings];
  [reordered[ctx.index], reordered[ctx.index + 1]] = [
    reordered[ctx.index + 1],
    reordered[ctx.index],
  ];
  categoryActionLoading.value = true;
  try {
    await persistSiblingOrder(reordered);
    ElMessage.success($t('message.updateOkMessage'));
    await reloadCategoryTree();
  } catch (error: any) {
    ElMessage.error(error?.message || $t('message.getDataError'));
  } finally {
    categoryActionLoading.value = false;
  }
};

const promoteCategory = async (node: any) => {
  if (categoryActionLoading.value || !canPromote(node)) {
    return;
  }
  const ctx = findNodeContext(String(node.id));
  if (!ctx || !ctx.parent) {
    return;
  }
  const newParentId =
    ctx.parent.parentId === undefined || ctx.parent.parentId === null
      ? '0'
      : String(ctx.parent.parentId);
  const newSiblings = getChildrenByParentId(newParentId);
  const oldSiblingsWithoutNode = ctx.siblings.filter(
    (_: any, index: number) => index !== ctx.index,
  );
  categoryActionLoading.value = true;
  try {
    await updateCategoryRequest({
      id: node.id,
      collectionId: node.collectionId,
      parentId: newParentId,
      categoryName: node.categoryName,
      sortNo: newSiblings.length,
    });
    await persistSiblingOrder(oldSiblingsWithoutNode);
    ElMessage.success($t('message.updateOkMessage'));
    await reloadCategoryTree();
  } catch (error: any) {
    ElMessage.error(error?.message || $t('message.getDataError'));
  } finally {
    categoryActionLoading.value = false;
  }
};

const demoteCategory = async (node: any) => {
  if (categoryActionLoading.value || !canDemote(node)) {
    return;
  }
  const ctx = findNodeContext(String(node.id));
  if (!ctx || ctx.index <= 0) {
    return;
  }
  const previousSibling = ctx.siblings[ctx.index - 1];
  if (previousSibling?.isDefault) {
    ElMessage.warning(
      $t('documentCollection.faq.defaultCategoryChildForbidden'),
    );
    return;
  }
  const childCategories = previousSibling.children || [];
  const oldSiblingsWithoutNode = ctx.siblings.filter(
    (_: any, index: number) => index !== ctx.index,
  );
  categoryActionLoading.value = true;
  try {
    await updateCategoryRequest({
      id: node.id,
      collectionId: node.collectionId,
      parentId: String(previousSibling.id),
      categoryName: node.categoryName,
      sortNo: childCategories.length,
    });
    await persistSiblingOrder(oldSiblingsWithoutNode);
    ElMessage.success($t('message.updateOkMessage'));
    await reloadCategoryTree();
  } catch (error: any) {
    ElMessage.error(error?.message || $t('message.getDataError'));
  } finally {
    categoryActionLoading.value = false;
  }
};

const saveCategory = async (payload: any) => {
  if (!props.manageable) {
    ElMessage.warning($t('documentCollection.managePermissionHint'));
    return;
  }
  const url = payload.id
    ? '/api/v1/faqCategory/update'
    : '/api/v1/faqCategory/save';
  const res = await props.requestClient.post(
    buildKnowledgePath(props.endpointPrefix, url),
    payload,
  );
  if (res.errorCode === 0) {
    ElMessage.success(
      payload.id ? $t('message.updateOkMessage') : $t('message.saveOkMessage'),
    );
    categoryDialogVisible.value = false;
    await reloadCategoryTree();
  } else {
    ElMessage.error(res.message);
  }
};

const buildParentOptions = (excludeRootId?: string) => {
  const excludedIds = new Set<string>();
  if (excludeRootId) {
    collectDescendantIds(categoryTree.value, excludeRootId, excludedIds);
  }
  return [
    {
      id: '0',
      categoryName: $t('documentCollection.faq.rootCategory'),
      children: filterTree(categoryTree.value, excludedIds),
    },
  ];
};

const collectDescendantIds = (
  nodes: any[],
  rootId: string,
  output: Set<string>,
) => {
  for (const node of nodes || []) {
    const currentId = String(node.id);
    if (currentId === rootId) {
      collectNodeIds(node, output);
      return true;
    }
    if (node.children?.length) {
      const found = collectDescendantIds(node.children, rootId, output);
      if (found) {
        return true;
      }
    }
  }
  return false;
};

const collectNodeIds = (node: any, output: Set<string>) => {
  output.add(String(node.id));
  for (const child of node.children || []) {
    collectNodeIds(child, output);
  }
};

const filterTree = (nodes: any[], excludedIds: Set<string>): any[] => {
  return (nodes || [])
    .filter((node) => !excludedIds.has(String(node.id)))
    .map((node) => ({
      ...node,
      children: filterTree(node.children || [], excludedIds),
    }));
};

const categoryTreeOptions = computed(() => categoryTree.value || []);

onMounted(() => {
  reloadCategoryTree();
});
</script>

<template>
  <div class="faq-table-wrapper">
    <div class="faq-layout">
      <div class="faq-category-pane">
        <div class="faq-category-header">
          <span>{{ $t('documentCollection.faq.categoryTree') }}</span>
          <ElButton
            v-if="props.manageable"
            link
            type="primary"
            :icon="Plus"
            @click="openAddRootCategory"
          >
            {{ $t('button.add') }}
          </ElButton>
        </div>
        <ElTree
          class="faq-category-tree"
          :data="treeData"
          node-key="id"
          default-expand-all
          :expand-on-click-node="false"
          :current-node-key="selectedCategoryId"
          :props="{ label: 'categoryName', children: 'children' }"
          @node-click="handleCategoryClick"
        >
          <template #default="{ data }">
            <div
              class="faq-category-node"
              :class="{ 'is-all-node': data.isVirtual }"
            >
              <span class="faq-category-node-label">{{
                data.categoryName
              }}</span>
              <div
                v-if="props.manageable && !data.isVirtual && !data.isDefault"
                class="faq-category-node-actions"
                @click.stop
              >
                <ElDropdown trigger="click">
                  <ElButton link :icon="MoreFilled" />
                  <template #dropdown>
                    <ElDropdownMenu>
                      <ElDropdownItem
                        :icon="Top"
                        :disabled="!canMoveUp(data)"
                        @click="moveCategoryUp(data)"
                      >
                        {{ $t('documentCollection.faq.moveUp') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="Bottom"
                        :disabled="!canMoveDown(data)"
                        @click="moveCategoryDown(data)"
                      >
                        {{ $t('documentCollection.faq.moveDown') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="Upload"
                        :disabled="!canPromote(data)"
                        @click="promoteCategory(data)"
                      >
                        {{ $t('documentCollection.faq.promote') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="Download"
                        :disabled="!canDemote(data)"
                        @click="demoteCategory(data)"
                      >
                        {{ $t('documentCollection.faq.demote') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="CirclePlus"
                        @click="openAddSiblingCategory(data)"
                      >
                        {{ $t('documentCollection.faq.addSiblingCategory') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="FolderAdd"
                        :disabled="
                          Number(data.levelNo) >= 3 || !!data.isDefault
                        "
                        @click="openAddChildCategory(data)"
                      >
                        {{ $t('documentCollection.faq.addChildCategory') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="Edit"
                        @click="openEditCategory(data)"
                      >
                        {{ $t('button.edit') }}
                      </ElDropdownItem>
                      <ElDropdownItem
                        :icon="Delete"
                        :disabled="!!data.isDefault"
                        @click="removeCategory(data)"
                      >
                        {{ $t('button.delete') }}
                      </ElDropdownItem>
                    </ElDropdownMenu>
                  </template>
                </ElDropdown>
              </div>
            </div>
          </template>
        </ElTree>
      </div>

      <div class="faq-content-pane">
        <div class="faq-header">
          <div v-if="!props.manageable" class="faq-readonly-tip">
            {{ $t('documentCollection.managePermissionHint') }}
          </div>
          <div class="faq-toolbar">
            <div class="faq-search-actions">
              <ElInput
                v-model="searchKeyword"
                clearable
                class="faq-search-input"
                :placeholder="$t('common.searchPlaceholder')"
                @keyup.enter="handleSearch"
              />
              <ElButton type="primary" :icon="Search" @click="handleSearch">
                {{ $t('button.query') }}
              </ElButton>
              <ElButton :icon="RefreshRight" @click="handleResetSearch">
                {{ $t('button.reset') }}
              </ElButton>
            </div>

            <div class="faq-primary-actions">
              <ElButton
                v-if="props.manageable"
                type="primary"
                :icon="Plus"
                @click="openAddDialog"
              >
                {{ $t('button.add') }}
              </ElButton>
              <ElDropdown trigger="click" @command="handleMoreActionCommand">
                <ElButton :icon="MoreFilled">
                  {{ $t('documentCollection.faq.import.moreActions') }}
                </ElButton>
                <template #dropdown>
                  <ElDropdownMenu>
                    <ElDropdownItem
                      v-if="props.manageable"
                      command="import"
                      :icon="Upload"
                    >
                      {{ $t('button.import') }}
                    </ElDropdownItem>
                    <ElDropdownItem
                      command="downloadTemplate"
                      :icon="Download"
                      :disabled="templateDownloadLoading"
                    >
                      {{ $t('documentCollection.faq.import.downloadTemplate') }}
                    </ElDropdownItem>
                    <ElDropdownItem
                      command="export"
                      :icon="Download"
                      :disabled="exportLoading"
                    >
                      {{ $t('button.export') }}
                    </ElDropdownItem>
                  </ElDropdownMenu>
                </template>
              </ElDropdown>
            </div>
          </div>
        </div>

        <PageData
          ref="pageDataRef"
          :page-url="
            buildKnowledgePath(props.endpointPrefix, '/api/v1/faqItem/page')
          "
          :page-size="10"
          :request-client="props.requestClient"
          :extra-query-params="baseQueryParams"
        >
          <template #default="{ pageList }">
            <ElTable :data="pageList" size="large">
              <ElTableColumn
                prop="categoryPath"
                :label="$t('documentCollection.faq.categoryPath')"
                min-width="220"
                show-overflow-tooltip
              />
              <ElTableColumn
                prop="question"
                :label="$t('documentCollection.faq.question')"
                min-width="220"
              />
              <ElTableColumn
                prop="answerText"
                :label="$t('documentCollection.faq.answer')"
                min-width="260"
                show-overflow-tooltip
              />
              <ElTableColumn
                v-if="props.manageable"
                :label="$t('common.handle')"
                width="170"
                align="right"
              >
                <template #default="{ row }">
                  <ElButton
                    link
                    type="primary"
                    :icon="Edit"
                    @click="openEditDialog(row)"
                  >
                    {{ $t('button.edit') }}
                  </ElButton>
                  <ElButton
                    link
                    type="danger"
                    :icon="Delete"
                    @click="removeFaq(row)"
                  >
                    {{ $t('button.delete') }}
                  </ElButton>
                </template>
              </ElTableColumn>
            </ElTable>
          </template>
        </PageData>
      </div>
    </div>

    <FaqEditDialog
      v-model="dialogVisible"
      :data="editData"
      :category-options="categoryTreeOptions"
      :request-client="props.requestClient"
      :endpoint-prefix="props.endpointPrefix"
      @submit="saveFaq"
    />

    <FaqCategoryDialog
      v-model="categoryDialogVisible"
      :title="categoryDialogTitle"
      :data="categoryEditData"
      :disable-parent="categoryDialogDisableParent"
      :parent-options="categoryParentOptions"
      @submit="saveCategory"
    />

    <FaqImportDialog
      v-model="importDialogVisible"
      :knowledge-id="knowledgeId"
      :request-client="props.requestClient"
      :endpoint-prefix="props.endpointPrefix"
      @success="handleImportSuccess"
    />
  </div>
</template>

<style scoped>
.faq-table-wrapper {
  width: 100%;
  height: calc(100vh - 220px);
}

.faq-layout {
  display: flex;
  gap: 12px;
  height: 100%;
}

.faq-category-pane {
  display: flex;
  flex-direction: column;
  width: 236px;
  min-width: 236px;
  overflow: hidden;
  background: var(--el-fill-color-blank);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 12px;
}

.faq-category-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
  font-weight: 600;
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.faq-category-tree {
  flex: 1;
  padding: 6px;
  overflow: auto;
}

.faq-category-node {
  display: flex;
  gap: 6px;
  align-items: center;
  width: 100%;
  min-width: 0;
}

.faq-category-node.is-all-node .faq-category-node-label {
  padding-left: 6px;
  font-weight: 600;
}

.faq-category-node-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.faq-category-node-actions {
  margin-left: auto;
  opacity: 0;
  transition: opacity 0.18s ease;
}

.faq-content-pane {
  flex: 1;
  padding: 14px 16px 8px;
  overflow: auto;
  background: var(--el-fill-color-blank);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 12px;
}

.faq-header {
  margin-bottom: 12px;
}

.faq-readonly-tip {
  margin-bottom: 10px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--el-text-color-secondary);
}

.faq-toolbar {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  padding: 2px 0;
}

.faq-search-actions {
  display: flex;
  flex: 1;
  gap: 10px;
  align-items: center;
  min-width: 260px;
}

.faq-search-input {
  width: min(460px, 100%);
}

.faq-primary-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: flex-end;
}

:deep(.faq-toolbar .el-button) {
  height: 38px;
  padding: 0 16px;
  font-weight: 500;
  border-radius: 10px;
  transition:
    border-color 0.2s ease,
    background-color 0.2s ease,
    color 0.2s ease;
}

:deep(.faq-toolbar .el-button:not(.el-button--primary):hover) {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 7%);
  border-color: hsl(var(--primary) / 45%);
}

:deep(.faq-search-input .el-input__wrapper) {
  padding-right: 12px;
  padding-left: 12px;
  border-radius: 10px;
  box-shadow: 0 0 0 1px var(--el-border-color) inset;
}

:deep(.faq-search-input .el-input__wrapper:hover) {
  box-shadow: 0 0 0 1px hsl(var(--primary) / 30%) inset;
}

:deep(.faq-search-input .el-input__wrapper.is-focus) {
  box-shadow: 0 0 0 1px hsl(var(--primary) / 60%) inset;
}

:deep(
  .faq-category-tree
    > .el-tree-node
    > .el-tree-node__content
    > .el-tree-node__expand-icon
) {
  display: none;
}

:deep(.faq-category-tree .el-tree-node__content) {
  height: 34px;
  padding-right: 4px;
  border-radius: 8px;
}

:deep(.faq-category-tree .el-tree-node__content:hover) {
  background-color: var(--el-fill-color-light);
}

:deep(
  .faq-category-tree .el-tree-node__content:hover .faq-category-node-actions
) {
  opacity: 1;
}

:deep(.el-tree-node.is-current > .el-tree-node__content) {
  color: hsl(var(--primary));
  background-color: hsl(var(--primary) / 15%);
}

:deep(
  .el-tree-node.is-current > .el-tree-node__content .faq-category-node-actions
) {
  opacity: 1;
}

:deep(.el-table) {
  overflow: hidden;
  border-radius: 10px;
}

:deep(.el-table th.el-table__cell) {
  background: var(--el-fill-color-light);
}

:deep(.el-table td.el-table__cell) {
  padding-top: 14px;
  padding-bottom: 14px;
}

@media (max-width: 1360px) {
  .faq-toolbar {
    flex-direction: column;
    align-items: flex-start;
  }

  .faq-search-actions,
  .faq-primary-actions {
    width: 100%;
  }

  .faq-primary-actions {
    justify-content: flex-start;
  }
}
</style>
