<script setup lang="ts">
import { ElMessage, ElMessageBox } from "element-plus";
import { computed, onMounted, reactive, ref } from "vue";

import { api, clearToken, getToken, saveToken } from "./services/api";
import type {
  AdminProfile,
  AdminUser,
  AgentDebugResult,
  AiLogRecord,
  ChatDetail,
  ChatRecord,
  DashboardStats,
  KnowledgeItem,
  ModelItem,
  SystemConfigItem,
  UserImportResult,
} from "./types/api";

const admin = ref<AdminProfile | null>(null);
const activeMenu = ref("dashboard");
const loading = ref(false);

const loginForm = reactive({ username: "admin", password: "admin123456" });
const stats = ref<DashboardStats | null>(null);
const dashboardDateRange = ref<[string, string] | null>(null);
const users = ref<AdminUser[]>([]);
const userKeyword = ref("");
const knowledge = ref<KnowledgeItem[]>([]);
const models = ref<ModelItem[]>([]);
const configs = ref<SystemConfigItem[]>([]);
const chats = ref<ChatRecord[]>([]);
const aiLogs = ref<AiLogRecord[]>([]);
const operationLogs = ref<Record<string, unknown>[]>([]);
const promptContent = ref("");
const chatDetail = ref<ChatDetail | null>(null);
const chatDetailOpen = ref(false);
const selectedAiLog = ref<AiLogRecord | null>(null);
const aiLogDetailOpen = ref(false);
const recordTab = ref("chats");
const editingModelId = ref<number | null>(null);
const editingKnowledgeId = ref<number | null>(null);
const batchNodeToken = ref("");
const batchImporting = ref(false);
const agentDebugging = ref(false);
const agentPreviewMessages = ref<{ role: "user" | "assistant" | "system"; content: string }[]>([
  { role: "assistant", content: "选择模型和知识库后，可以在这里调试 Agent 的真实问答效果。" },
]);

const studentForm = reactive({
  phone: "",
  name: "",
  nickname: "",
  status: 1,
  dailyChatLimit: 100,
  expiredAt: "",
});
const studentImportFile = ref<File | null>(null);
const studentImportInput = ref<HTMLInputElement | null>(null);
const studentImportResult = ref<UserImportResult | null>(null);

const knowledgeForm = reactive({
  name: "",
  feishuSpaceId: "",
  feishuNodeId: "",
  status: 1,
  remark: "",
});

const modelForm = reactive({
  provider: "openai-compatible",
  displayName: "",
  apiType: "openai_compatible",
  modelName: "",
  baseUrl: "https://api.openai.com/v1",
  apiUrl: "",
  apiKey: "",
  authType: "bearer",
  apiVersion: "",
  temperature: null as number | null,
  topP: null as number | null,
  topK: null as number | null,
  presencePenalty: null as number | null,
  frequencyPenalty: null as number | null,
  maxToken: null as number | null,
  contextWindow: null as number | null,
  streamEnabled: 1,
  responseFormat: "",
  extraParams: "",
  remark: "",
  timeoutSecond: 30,
});

const quickModelForm = reactive({
  provider: "deepseek",
  modelName: "deepseek-v4-pro",
  apiKey: "",
});

const quickModelProviders = [
  {
    label: "DeepSeek",
    provider: "deepseek",
    apiType: "openai_compatible",
    baseUrl: "https://api.deepseek.com",
    authType: "bearer",
    apiVersion: "",
    temperature: null,
    topP: null,
    maxToken: null,
    contextWindow: null,
    models: [
      { label: "deepseek-v4-pro", value: "deepseek-v4-pro" },
      { label: "deepseek-v4-flash", value: "deepseek-v4-flash" },
    ],
  },
  {
    label: "MiniMax",
    provider: "minimax",
    apiType: "openai_compatible",
    baseUrl: "https://api.minimaxi.com/v1",
    authType: "bearer",
    apiVersion: "",
    temperature: null,
    topP: null,
    maxToken: null,
    contextWindow: null,
    models: [
      { label: "MiniMax-Text-01", value: "MiniMax-Text-01" },
      { label: "MiniMax-M3", value: "MiniMax-M3" },
    ],
  },
] as const;

const protocolPresets = [
  {
    label: "OpenAI 兼容协议",
    provider: "openai-compatible",
    apiType: "openai_compatible",
    baseUrl: "https://api.openai.com/v1",
    apiUrl: "",
    authType: "bearer",
  },
  {
    label: "Anthropic Messages 协议",
    provider: "anthropic-compatible",
    apiType: "anthropic_messages",
    baseUrl: "https://api.anthropic.com",
    apiUrl: "",
    authType: "api_key",
    apiVersion: "2023-06-01",
    temperature: null,
    topP: null,
    topK: null,
  },
];

const nullableModelFields = [
  "temperature",
  "topP",
  "topK",
  "presencePenalty",
  "frequencyPenalty",
  "contextWindow",
  "responseFormat",
  "extraParams",
  "remark",
] as const;

type SystemSettingValue = string | number | boolean;
type SystemSettingType = "number" | "switch" | "text" | "password";

interface SystemSettingDefinition {
  key: string;
  label: string;
  type: SystemSettingType;
  defaultValue: SystemSettingValue;
  min?: number;
  max?: number;
  placeholder?: string;
  description: string;
}

interface SystemSettingSection {
  title: string;
  description: string;
  settings: SystemSettingDefinition[];
}

const systemSettingSections: SystemSettingSection[] = [
  {
    title: "用户与登录",
    description: "控制学员准入、默认资料、验证码和登录态有效时间。",
    settings: [
      {
        key: "daily_chat_limit",
        label: "默认每日聊天额度",
        type: "number",
        defaultValue: 100,
        min: 0,
        max: 100000,
        description: "新建或导入学员未单独填写额度时使用。",
      },
      {
        key: "access_token_expire_minutes",
        label: "登录 Token 有效期（分钟）",
        type: "number",
        defaultValue: 43200,
        min: 5,
        max: 43200,
        description: "用户端和管理端登录后的 Token 有效期。",
      },
      {
        key: "sms_code_expire_minutes",
        label: "验证码有效期（分钟）",
        type: "number",
        defaultValue: 5,
        min: 1,
        max: 60,
        description: "短信验证码生成后的可使用时间。",
      },
      {
        key: "mock_sms_enabled",
        label: "使用 mock 短信",
        type: "switch",
        defaultValue: true,
        description: "开发和演示环境可开启，生产环境应关闭并接入真实短信。",
      },
      {
        key: "mock_sms_code",
        label: "mock 短信验证码",
        type: "text",
        defaultValue: "123456",
        placeholder: "例如 123456",
        description: "mock 短信开启时使用的固定验证码。",
      },
      {
        key: "aliyun_sms_access_key_id",
        label: "阿里云 AccessKey ID",
        type: "password",
        defaultValue: "",
        placeholder: "请输入 RAM 用户 AccessKey ID",
        description: "关闭 mock 短信后用于调用阿里云短信服务。",
      },
      {
        key: "aliyun_sms_access_key_secret",
        label: "阿里云 AccessKey Secret",
        type: "password",
        defaultValue: "",
        placeholder: "请输入 RAM 用户 AccessKey Secret",
        description: "敏感配置，建议使用最小权限 RAM 用户。",
      },
      {
        key: "aliyun_sms_sign_name",
        label: "阿里云短信签名",
        type: "text",
        defaultValue: "",
        placeholder: "例如 某某课堂",
        description: "需与阿里云短信控制台审核通过的签名一致。",
      },
      {
        key: "aliyun_sms_template_code",
        label: "阿里云模板 Code",
        type: "text",
        defaultValue: "",
        placeholder: "例如 SMS_123456789",
        description: "验证码短信模板 Code。",
      },
      {
        key: "aliyun_sms_template_param_key",
        label: "验证码变量名",
        type: "text",
        defaultValue: "code",
        placeholder: "默认 code",
        description: "模板中验证码变量名，例如模板变量为 ${code} 时填写 code。",
      },
      {
        key: "aliyun_sms_endpoint",
        label: "阿里云短信 Endpoint",
        type: "text",
        defaultValue: "dysmsapi.aliyuncs.com",
        placeholder: "dysmsapi.aliyuncs.com",
        description: "一般保持默认值即可。",
      },
      {
        key: "default_user_name_prefix",
        label: "默认用户名前缀",
        type: "text",
        defaultValue: "用户",
        placeholder: "例如 用户",
        description: "系统需要生成默认名称时使用的前缀。",
      },
    ],
  },
  {
    title: "AI 问答",
    description: "控制 Agent 调用、上下文和用户端回答展示策略。",
    settings: [
      {
        key: "ai_timeout_seconds",
        label: "AI 请求超时（秒）",
        type: "number",
        defaultValue: 60,
        min: 5,
        max: 300,
        description: "真实模型请求超过该时间应判定为超时。",
      },
      {
        key: "mock_model_enabled",
        label: "使用 mock 大模型",
        type: "switch",
        defaultValue: true,
        description: "开发和演示环境可开启，真实模型验收前应关闭。",
      },
      {
        key: "chat_context_message_count",
        label: "上下文消息数",
        type: "number",
        defaultValue: 10,
        min: 0,
        max: 100,
        description: "组装 Prompt 时带入的历史消息数量。",
      },
      {
        key: "show_reference_sources",
        label: "用户端展示引用来源",
        type: "switch",
        defaultValue: true,
        description: "后台始终记录引用；此项控制用户端是否展示。",
      },
      {
        key: "chat_max_active_requests",
        label: "问答最大并发数",
        type: "number",
        defaultValue: 2,
        min: 1,
        max: 1000,
        description: "同一后端进程内同时进入飞书和模型生成链路的最大请求数。",
      },
      {
        key: "chat_max_queue_size",
        label: "问答最大排队数",
        type: "number",
        defaultValue: 20,
        min: 0,
        max: 10000,
        description: "超过最大并发后允许等待的请求数量，超过后直接提示稍后再试。",
      },
      {
        key: "chat_queue_timeout_seconds",
        label: "问答排队超时（秒）",
        type: "number",
        defaultValue: 60,
        min: 1,
        max: 3600,
        description: "请求在排队中超过该时间后自动结束并提示用户稍后再试。",
      },
      {
        key: "chat_active_lease_seconds",
        label: "问答执行租约（秒）",
        type: "number",
        defaultValue: 900,
        min: 60,
        max: 86400,
        description: "Redis 全局队列中执行名额的自动过期时间，防止 worker 异常退出后长期占用并发名额。",
      },
    ],
  },
  {
    title: "外部服务",
    description: "控制飞书搜索接口、飞书直读超时和重试策略。",
    settings: [
      {
        key: "feishu_search_url",
        label: "飞书检索服务地址",
        type: "text",
        defaultValue: "",
        placeholder: "例如 https://example.com/search",
        description: "如使用独立飞书检索适配服务，在这里填写调用地址。",
      },
      {
        key: "feishu_app_id",
        label: "飞书应用 AppID",
        type: "text",
        defaultValue: "",
        placeholder: "填写飞书开放平台应用 AppID",
        description: "用于后端直读飞书知识库；系统设置优先于环境变量。",
      },
      {
        key: "feishu_app_secret",
        label: "飞书应用 AppSecret",
        type: "password",
        defaultValue: "",
        placeholder: "填写飞书开放平台应用 AppSecret",
        description: "用于后端获取飞书 tenant_access_token，保存后立即生效。",
      },
      {
        key: "feishu_timeout_seconds",
        label: "飞书请求超时（秒）",
        type: "number",
        defaultValue: 20,
        min: 1,
        max: 120,
        description: "飞书相关接口调用超过该时间应判定为超时。",
      },
      {
        key: "feishu_retry_count",
        label: "飞书检索重试次数",
        type: "number",
        defaultValue: 2,
        min: 0,
        max: 10,
        description: "飞书检索失败时的最大重试次数。",
      },
    ],
  },
  {
    title: "系统开关",
    description: "控制系统级展示和运行策略，不包含密钥类配置。",
    settings: [
      {
        key: "show_operation_log_detail",
        label: "展示操作日志详情",
        type: "switch",
        defaultValue: true,
        description: "开启后后台审计页可展示更完整的操作上下文。",
      },
      {
        key: "chat_export_enabled",
        label: "允许导出聊天记录",
        type: "switch",
        defaultValue: true,
        description: "关闭后后台不应提供聊天记录导出能力。",
      },
      {
        key: "maintenance_mode",
        label: "维护模式",
        type: "switch",
        defaultValue: false,
        description: "预留给后续停机维护提示和访问控制使用。",
      },
    ],
  },
];

const systemSettingDefinitions = systemSettingSections.flatMap((section) => section.settings);
const systemSettingValues = reactive<Record<string, SystemSettingValue>>({});
systemSettingDefinitions.forEach((setting) => {
  systemSettingValues[setting.key] = setting.defaultValue;
});

const chatFilters = reactive({
  keyword: "",
  userId: undefined as number | undefined,
  status: "",
  dateFrom: "",
  dateTo: "",
});

const agentForm = reactive({
  modelId: undefined as number | undefined,
  knowledgeIds: [] as number[],
  temperature: 0.2 as number | null,
  topP: null as number | null,
  topK: null as number | null,
  presencePenalty: null as number | null,
  frequencyPenalty: null as number | null,
  maxToken: 1024 as number | null,
  question: "",
});

onMounted(async () => {
  if (!getToken()) return;
  try {
    admin.value = await api.profile();
    await loadCurrentMenu();
  } catch {
    clearToken();
  }
});

async function login() {
  loading.value = true;
  try {
    const result = await api.login(loginForm.username, loginForm.password);
    saveToken(result.token);
    admin.value = result.admin;
    await loadCurrentMenu();
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : "登录失败");
  } finally {
    loading.value = false;
  }
}

function logout() {
  clearToken();
  admin.value = null;
}

async function switchMenu(menu: string) {
  activeMenu.value = menu;
  await loadCurrentMenu();
}

async function loadDashboard() {
  const [start, end] = dashboardDateRange.value ?? ["", ""];
  stats.value = await api.dashboard(start, end);
}

async function loadCurrentMenu() {
  loading.value = true;
  try {
    if (activeMenu.value === "dashboard") await loadDashboard();
    if (activeMenu.value === "users") users.value = await api.users(userKeyword.value);
    if (activeMenu.value === "knowledge") knowledge.value = await api.knowledge();
    if (activeMenu.value === "prompt") {
      const [prompt, modelRows, knowledgeRows] = await Promise.all([api.prompt(), api.models(), api.knowledge()]);
      promptContent.value = prompt.promptContent;
      models.value = modelRows;
      knowledge.value = knowledgeRows;
      if (!agentForm.modelId && modelRows.length) {
        agentForm.modelId = modelRows.find((model) => model.enabled === 1)?.id ?? modelRows[0].id;
      }
    }
    if (activeMenu.value === "models") models.value = await api.models();
    if (activeMenu.value === "configs") {
      configs.value = await api.configs();
      hydrateSystemSettings();
    }
    if (activeMenu.value === "records") {
      chats.value = await api.chats(buildChatQuery());
      aiLogs.value = await api.aiLogs();
      operationLogs.value = await api.operationLogs();
    }
  } finally {
    loading.value = false;
  }
}

async function saveUser(row: AdminUser) {
  await api.updateUser(row.id, { status: row.status, dailyChatLimit: row.dailyChatLimit });
  ElMessage.success("用户已更新");
  await loadCurrentMenu();
}

async function createStudent() {
  if (!studentForm.phone || !studentForm.name) {
    ElMessage.error("请填写手机号和姓名");
    return;
  }
  await api.createUser({
    phone: studentForm.phone,
    name: studentForm.name,
    nickname: studentForm.nickname || null,
    status: studentForm.status,
    dailyChatLimit: studentForm.dailyChatLimit,
    expiredAt: studentForm.expiredAt || null,
  });
  ElMessage.success("学员已添加");
  resetStudentForm();
  await loadCurrentMenu();
}

function handleStudentImportFile(event: Event) {
  const input = event.target as HTMLInputElement;
  studentImportFile.value = input.files?.[0] ?? null;
}

async function downloadStudentTemplate() {
  await api.downloadStudentTemplate();
}

async function importStudents() {
  if (!studentImportFile.value) {
    ElMessage.error("请先选择 Excel 文件");
    return;
  }
  studentImportResult.value = await api.importUsersExcel(studentImportFile.value);
  ElMessage.success(
    `导入完成：新增 ${studentImportResult.value.created}，更新 ${studentImportResult.value.updated}，失败 ${studentImportResult.value.failed}`,
  );
  studentImportFile.value = null;
  if (studentImportInput.value) studentImportInput.value.value = "";
  await loadCurrentMenu();
}

function resetStudentForm() {
  Object.assign(studentForm, { phone: "", name: "", nickname: "", status: 1, dailyChatLimit: 100, expiredAt: "" });
}

async function deleteUser(row: AdminUser) {
  if (!window.confirm(`确认删除用户 ${row.phone} 吗？删除后前台将无法继续使用该账号。`)) return;
  await api.deleteUser(row.id);
  ElMessage.success("用户已删除");
  await loadCurrentMenu();
}

async function saveKnowledge() {
  if (!knowledgeForm.name || !knowledgeForm.feishuSpaceId || !knowledgeForm.feishuNodeId) {
    ElMessage.error("请填写知识库名称、SpaceID 和 NodeID");
    return;
  }
  if (editingKnowledgeId.value) {
    await api.updateKnowledge(editingKnowledgeId.value, { ...knowledgeForm });
    ElMessage.success("知识库已更新");
  } else {
    await api.createKnowledge({ ...knowledgeForm });
    ElMessage.success("知识库已新增");
  }
  resetKnowledgeForm();
  await loadCurrentMenu();
}

async function saveKnowledgeRow(row: KnowledgeItem) {
  await api.updateKnowledge(row.id, {
    name: row.name,
    feishuSpaceId: row.feishuSpaceId,
    feishuNodeId: row.feishuNodeId,
    status: row.status,
    remark: row.remark ?? "",
  });
  ElMessage.success("知识库状态已更新");
}

function editKnowledge(row: KnowledgeItem) {
  editingKnowledgeId.value = row.id;
  Object.assign(knowledgeForm, {
    name: row.name,
    feishuSpaceId: row.feishuSpaceId,
    feishuNodeId: row.feishuNodeId,
    status: row.status,
    remark: row.remark ?? "",
  });
}

async function deleteKnowledge(row: KnowledgeItem) {
  if (!window.confirm(`确认删除知识库 ${row.name} 吗？删除后所有用户都无法再检索该知识库。`)) return;
  await api.deleteKnowledge(row.id);
  ElMessage.success("知识库已删除");
  if (editingKnowledgeId.value === row.id) resetKnowledgeForm();
  await loadCurrentMenu();
}

function resetKnowledgeForm() {
  editingKnowledgeId.value = null;
  Object.assign(knowledgeForm, { name: "", feishuSpaceId: "", feishuNodeId: "", status: 1, remark: "" });
}

async function batchImportKnowledge() {
  const token = batchNodeToken.value.trim();
  if (!token) {
    ElMessage.warning("请输入 NodeToken");
    return;
  }
  batchImporting.value = true;
  try {
    const result = await api.importKnowledgeFromSpace({ node_token: token });
    if (result.imported > 0) {
      ElMessage.success(`导入成功：${result.name}（id=${result.id}）`);
    } else {
      ElMessage.info(`该节点已存在：${result.name}（id=${result.id}）`);
    }
    batchNodeToken.value = "";
    await loadCurrentMenu();
  } catch (e: any) {
    ElMessage.error(e.message || "导入失败");
  } finally {
    batchImporting.value = false;
  }
}

async function savePrompt() {
  const result = await api.savePrompt(promptContent.value);
  promptContent.value = result.promptContent;
  ElMessage.success("Prompt 已保存");
}

async function resetPrompt() {
  const result = await api.resetPrompt();
  promptContent.value = result.promptContent;
  ElMessage.success("Prompt 已恢复默认");
}

async function saveModel() {
  if (!validateModelForm()) return;
  const payload = buildModelPayload();
  if (editingModelId.value) {
    await api.updateModel(editingModelId.value, payload);
    ElMessage.success("模型已更新");
  } else {
    await api.createModel(payload);
    ElMessage.success("模型已新增");
  }
  resetModelForm();
  await loadCurrentMenu();
}

function buildModelPayload() {
  const payload = { ...modelForm } as Record<string, unknown>;
  nullableModelFields.forEach((field) => {
    if (payload[field] === undefined || payload[field] === "") {
      payload[field] = null;
    }
  });
  Object.assign(payload, {
    temperature: null,
    topP: null,
    topK: null,
    presencePenalty: null,
    frequencyPenalty: null,
    maxToken: null,
    contextWindow: null,
    streamEnabled: 1,
    responseFormat: null,
    extraParams: null,
  });
  return payload;
}

const selectedQuickProvider = computed(() => {
  return quickModelProviders.find((item) => item.provider === quickModelForm.provider) ?? quickModelProviders[0];
});

async function quickAddModel() {
  const provider = selectedQuickProvider.value;
  if (!provider) return;
  if (!quickModelForm.apiKey) {
    ElMessage.error("请填写 API Key");
    return;
  }

  await api.createModel({
    provider: provider.provider,
    displayName: `${provider.label} ${quickModelForm.modelName}`,
    apiType: provider.apiType,
    modelName: quickModelForm.modelName,
    baseUrl: provider.baseUrl,
    apiUrl: "",
    apiKey: quickModelForm.apiKey,
    authType: provider.authType,
    apiVersion: provider.apiVersion,
    temperature: null,
    topP: null,
    topK: null,
    presencePenalty: null,
    frequencyPenalty: null,
    maxToken: null,
    contextWindow: null,
    streamEnabled: 1,
    responseFormat: null,
    extraParams: null,
    remark: "快速添加",
    timeoutSecond: 30,
  });
  ElMessage.success(`${provider.label} 模型已新增`);
  quickModelForm.apiKey = "";
  await loadCurrentMenu();
}

function applyQuickProvider(provider: string) {
  const target = quickModelProviders.find((item) => item.provider === provider);
  if (!target) return;
  quickModelForm.modelName = target.models[0]?.value ?? "";
}

async function enableModel(id: number) {
  await api.enableModel(id);
  ElMessage.success("模型已启用");
  await loadCurrentMenu();
}

async function deleteModel(id: number) {
  try {
    await ElMessageBox.confirm("确认删除该模型？删除后不可恢复。", "删除模型", {
      confirmButtonText: "确认删除",
      cancelButtonText: "取消",
      type: "warning",
    });
  } catch {
    return;
  }
  await api.deleteModel(id);
  ElMessage.success("模型已删除");
  await loadCurrentMenu();
}

async function testModel(id: number) {
  const result = await api.testModel(id);
  if (result.ok) {
    ElMessage.success(result.message);
  } else {
    ElMessage.error(result.message);
  }
}

function applyModelPreset(label: string) {
  const preset = protocolPresets.find((item) => item.label === label);
  if (!preset) return;
  Object.assign(
    modelForm,
    {
      apiVersion: "",
      authType: "bearer",
      temperature: null,
      topP: null,
      topK: null,
      presencePenalty: null,
      frequencyPenalty: null,
      responseFormat: "",
      streamEnabled: 1,
    },
    preset,
  );
}

function editModel(row: ModelItem) {
  editingModelId.value = row.id;
  Object.assign(modelForm, {
    provider: row.provider,
    displayName: row.displayName ?? "",
    apiType: row.apiType,
    modelName: row.modelName,
    baseUrl: row.baseUrl ?? "",
    apiUrl: row.apiUrl,
    apiKey: "",
    authType: row.authType,
    apiVersion: row.apiVersion ?? "",
    temperature: null,
    topP: null,
    topK: null,
    presencePenalty: null,
    frequencyPenalty: null,
    maxToken: null,
    contextWindow: null,
    streamEnabled: row.streamEnabled,
    responseFormat: row.responseFormat ?? "",
    extraParams: row.extraParams ?? "",
    remark: row.remark ?? "",
    timeoutSecond: row.timeoutSecond,
  });
}

function resetModelForm() {
  editingModelId.value = null;
  Object.assign(modelForm, {
    provider: "openai-compatible",
    displayName: "",
    apiType: "openai_compatible",
    modelName: "",
    baseUrl: "https://api.openai.com/v1",
    apiUrl: "",
    apiKey: "",
    authType: "bearer",
    apiVersion: "",
    temperature: null,
    topP: null,
    topK: null,
    presencePenalty: null,
    frequencyPenalty: null,
    maxToken: null,
    contextWindow: null,
    streamEnabled: 1,
    responseFormat: "",
    extraParams: "",
    remark: "",
    timeoutSecond: 30,
  });
}

function validateModelForm() {
  if (!modelForm.apiUrl && !modelForm.baseUrl) {
    ElMessage.error("请填写 Base URL 或 API URL");
    return false;
  }
  if (!editingModelId.value && !modelForm.apiKey) {
    ElMessage.error("新增模型必须填写 API Key");
    return false;
  }
  if (modelForm.extraParams.trim()) {
    try {
      const extraParams = JSON.parse(modelForm.extraParams);
      if (!extraParams || Array.isArray(extraParams) || typeof extraParams !== "object") {
        throw new Error("高级 JSON 参数必须是对象");
      }
    } catch (error) {
      ElMessage.error(error instanceof Error ? error.message : "高级 JSON 参数不是合法 JSON");
      return false;
    }
  }
  return true;
}

async function saveSystemSettings() {
  await Promise.all(
    systemSettingDefinitions.map((setting) =>
      api.saveConfig({
        configKey: setting.key,
        configValue: serializeSystemSettingValue(systemSettingValues[setting.key]),
        description: setting.description,
      }),
    ),
  );
  ElMessage.success("系统配置已保存");
  configs.value = await api.configs();
  hydrateSystemSettings();
}

function hydrateSystemSettings() {
  const configMap = new Map(configs.value.map((item) => [item.configKey, item.configValue]));
  systemSettingDefinitions.forEach((setting) => {
    systemSettingValues[setting.key] = normalizeSystemSettingValue(setting, configMap.get(setting.key));
  });
}

function normalizeSystemSettingValue(setting: (typeof systemSettingDefinitions)[number], rawValue?: string) {
  if (rawValue === undefined || rawValue === "") {
    return setting.defaultValue;
  }
  if (setting.type === "switch") {
    return ["1", "true", "yes", "on", "启用"].includes(rawValue.toLowerCase());
  }
  if (setting.type === "text" || setting.type === "password") {
    return rawValue;
  }
  const parsed = Number(rawValue);
  if (Number.isFinite(parsed)) {
    return parsed;
  }
  return setting.defaultValue;
}

function serializeSystemSettingValue(value: SystemSettingValue) {
  if (typeof value === "boolean") return value ? "true" : "false";
  return String(value);
}

function agentMessageParts(content: string) {
  const matched = content.match(/<think>([\s\S]*?)<\/think>/i);
  if (!matched) return { reasoning: "", answer: content };
  const answer = content.replace(matched[0], "").trim();
  return {
    reasoning: matched[1].trim(),
    answer: answer || "模型未返回正式回答。",
  };
}

async function debugAgent() {
  if (!agentForm.modelId) {
    ElMessage.error("请选择调试模型");
    return;
  }
  if (!agentForm.question.trim()) {
    ElMessage.error("请输入调试问题");
    return;
  }
  const question = agentForm.question.trim();
  agentPreviewMessages.value.push({ role: "user", content: question });
  agentForm.question = "";
  agentDebugging.value = true;
  try {
    const result: AgentDebugResult = await api.debugAgent({
      promptContent: promptContent.value,
      modelId: agentForm.modelId,
      knowledgeIds: agentForm.knowledgeIds,
      question,
      temperature: agentForm.temperature,
      topP: agentForm.topP,
      topK: agentForm.topK,
      presencePenalty: agentForm.presencePenalty,
      frequencyPenalty: agentForm.frequencyPenalty,
      maxToken: agentForm.maxToken,
    });
    if (!result.ok) {
      ElMessage.error(result.message);
      agentPreviewMessages.value.push({ role: "assistant", content: result.message });
      return;
    }
    agentPreviewMessages.value.push({ role: "assistant", content: result.answer });
    ElMessage.success(result.message);
  } catch (error) {
    const message = error instanceof Error ? error.message : "Agent 调试失败";
    ElMessage.error(message);
    agentPreviewMessages.value.push({ role: "assistant", content: message });
  } finally {
    agentDebugging.value = false;
  }
}

function clearAgentPreview() {
  agentPreviewMessages.value = [
    { role: "assistant", content: "预览已清空，可以继续发起新的 Agent 调试。" },
  ];
}

async function clearFeishuCache() {
  try {
    const result = await api.clearFeishuCache();
    ElMessage.success(result.message || `已清空 ${result.cleared} 条缓存`);
  } catch (e: any) {
    ElMessage.error(e.message || "刷新缓存失败");
  }
}

async function searchChats() {
  chats.value = await api.chats(buildChatQuery());
}

async function resetChatFilters() {
  Object.assign(chatFilters, { keyword: "", userId: undefined, status: "", dateFrom: "", dateTo: "" });
  await searchChats();
}

async function openChatDetail(row: ChatRecord) {
  chatDetail.value = await api.chatDetail(row.id);
  chatDetailOpen.value = true;
}

async function openAiLogDetail(row: AiLogRecord) {
  selectedAiLog.value = await api.aiLogDetail(row.id);
  aiLogDetailOpen.value = true;
}

async function exportChats() {
  await api.exportChats(buildChatQuery());
}

function buildChatQuery() {
  return {
    keyword: chatFilters.keyword,
    userId: chatFilters.userId,
    status: chatFilters.status,
    dateFrom: formatRecordDateTime(chatFilters.dateFrom, "start"),
    dateTo: formatRecordDateTime(chatFilters.dateTo, "end"),
  };
}

function formatRecordDateTime(value: string, boundary: "start" | "end") {
  if (!value) return "";
  const [date, time = ""] = value.trim().replace("T", " ").split(" ");
  if (!date) return "";
  const [hour = "00", minute = "00", second] = time.split(":");
  const normalizedSecond = second ?? (boundary === "end" ? "59" : "00");
  return `${date} ${hour.padStart(2, "0")}:${minute.padStart(2, "0")}:${normalizedSecond.padStart(2, "0")}`;
}
</script>

<template>
  <main v-if="!admin" class="login-page">
    <section class="login-hero">
      <div class="login-hero-main">
        <div class="login-brand">
          <span>答</span>
          <strong>大本营千问千答</strong>
        </div>
        <h1>把大本营答疑、知识库和学员运营放在同一个控制台里。</h1>
        <p>统一维护学员名单、开放知识库、模型接入、Agent 调试、问答记录和系统运行参数，方便排查与交接。</p>
        <div class="login-highlights">
          <span>大本营知识库</span>
          <span>答疑 Agent 调试</span>
          <span>问答记录审计</span>
        </div>
      </div>
      <section class="login-card">
        <div class="login-card-head">
          <h2>登录答疑后台</h2>
          <p>请输入大本营答疑助手后台管理员账号和密码。</p>
        </div>
        <el-form label-position="top" @submit.prevent="login">
          <el-form-item label="管理员账号">
            <el-input v-model="loginForm.username" size="large" placeholder="请输入管理员账号" />
          </el-form-item>
          <el-form-item label="密码">
            <el-input
              v-model="loginForm.password"
              size="large"
              type="password"
              placeholder="请输入密码"
              show-password
            />
          </el-form-item>
          <el-button type="primary" :loading="loading" class="full-btn login-submit" @click="login">进入答疑后台</el-button>
        </el-form>
      </section>
    </section>
  </main>

  <main v-else class="admin-shell">
    <aside class="sidebar">
      <div class="sidebar-title">大本营千问千答</div>
      <button :class="{ active: activeMenu === 'dashboard' }" @click="switchMenu('dashboard')">数据看板</button>
      <button :class="{ active: activeMenu === 'users' }" @click="switchMenu('users')">用户管理</button>
      <button :class="{ active: activeMenu === 'knowledge' }" @click="switchMenu('knowledge')">知识库管理</button>
      <button :class="{ active: activeMenu === 'prompt' }" @click="switchMenu('prompt')">Agent 管理</button>
      <button :class="{ active: activeMenu === 'models' }" @click="switchMenu('models')">模型管理</button>
      <button :class="{ active: activeMenu === 'configs' }" @click="switchMenu('configs')">系统配置</button>
      <button :class="{ active: activeMenu === 'records' }" @click="switchMenu('records')">记录审计</button>
    </aside>

    <section class="workspace">
      <header class="topbar">
        <div>
          <strong>{{ admin.name }}</strong>
          <span>管理员</span>
        </div>
        <el-button @click="logout">退出</el-button>
      </header>

      <section v-loading="loading" class="content">
        <template v-if="activeMenu === 'dashboard'">
          <div class="page-head inline">
            <div><h2>数据看板</h2><p>核心业务数据概览。</p></div>
            <el-date-picker
              v-model="dashboardDateRange"
              type="daterange"
              range-separator="至"
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              value-format="YYYY-MM-DD"
              @change="loadDashboard"
            />
          </div>
          <div class="stats-grid">
            <div class="stat"><span>用户数</span><strong>{{ stats?.userCount ?? 0 }}</strong></div>
            <div class="stat"><span>会话数</span><strong>{{ stats?.sessionCount ?? 0 }}</strong></div>
            <div class="stat"><span>消息数</span><strong>{{ stats?.messageCount ?? 0 }}</strong></div>
            <div class="stat"><span>AI 请求</span><strong>{{ stats?.aiRequestCount ?? 0 }}</strong></div>
            <div class="stat"><span>知识库</span><strong>{{ stats?.knowledgeCount ?? 0 }}</strong></div>
            <div class="stat"><span>输入 Token</span><strong>{{ stats?.inputToken ?? 0 }}</strong></div>
            <div class="stat"><span>输出 Token</span><strong>{{ stats?.outputToken ?? 0 }}</strong></div>
            <div class="stat"><span>总 Token</span><strong>{{ stats?.totalToken ?? 0 }}</strong></div>
          </div>
        </template>

        <template v-if="activeMenu === 'users'">
          <div class="page-head inline">
            <div><h2>用户管理</h2><p>维护学员名单；只有名单内启用学员可以登录用户端。</p></div>
            <el-input v-model="userKeyword" placeholder="手机号或姓名" clearable @change="loadCurrentMenu" />
          </div>
          <section class="student-tools">
            <div class="student-tool-panel">
              <h3>手动添加学员</h3>
              <div class="student-form-grid">
                <el-input v-model="studentForm.phone" placeholder="手机号" />
                <el-input v-model="studentForm.name" placeholder="姓名" />
                <el-input v-model="studentForm.nickname" placeholder="昵称（可选）" />
                <el-input-number v-model="studentForm.dailyChatLimit" :min="0" />
                <el-date-picker
                  v-model="studentForm.expiredAt"
                  type="datetime"
                  value-format="YYYY-MM-DDTHH:mm:ss"
                  placeholder="有效期（可选）"
                />
                <div class="form-switch-row">
                  <span>启用</span>
                  <el-switch v-model="studentForm.status" :active-value="1" :inactive-value="0" />
                </div>
              </div>
              <div class="actions compact">
                <el-button type="primary" @click="createStudent">添加学员</el-button>
                <el-button @click="resetStudentForm">清空</el-button>
              </div>
            </div>
            <div class="student-tool-panel">
              <h3>批量导入学员</h3>
              <p class="tool-help">下载模板后填写学员信息，再上传 .xlsx 文件导入。</p>
              <div class="student-file-import">
                <input
                  ref="studentImportInput"
                  class="file-input"
                  type="file"
                  accept=".xlsx"
                  @change="handleStudentImportFile"
                />
                <span class="file-name">{{ studentImportFile?.name || "未选择文件" }}</span>
              </div>
              <div class="actions compact">
                <el-button @click="downloadStudentTemplate">下载模板</el-button>
                <el-button type="primary" @click="importStudents">导入 Excel</el-button>
                <span v-if="studentImportResult" class="import-result">
                  新增 {{ studentImportResult.created }}，更新 {{ studentImportResult.updated }}，失败 {{ studentImportResult.failed }}
                </span>
              </div>
              <div v-if="studentImportResult?.failures.length" class="import-failures">
                <span v-for="failure in studentImportResult.failures.slice(0, 5)" :key="`${failure.row}-${failure.phone}`">
                  第 {{ failure.row }} 行 {{ failure.phone || '-' }}：{{ failure.reason }}
                </span>
              </div>
            </div>
          </section>
          <el-table :data="users" stripe>
            <el-table-column prop="id" label="ID" width="80" />
            <el-table-column prop="phone" label="手机号" width="150" />
            <el-table-column prop="name" label="姓名" width="140" />
            <el-table-column prop="nickname" label="昵称" width="140" />
            <el-table-column label="状态" width="130">
              <template #default="{ row }"><el-switch v-model="row.status" :active-value="1" :inactive-value="0" /></template>
            </el-table-column>
            <el-table-column label="每日额度" width="150">
              <template #default="{ row }"><el-input-number v-model="row.dailyChatLimit" :min="0" size="small" /></template>
            </el-table-column>
            <el-table-column prop="dailyChatUsed" label="已用" width="90" />
            <el-table-column prop="lastLoginAt" label="最近登录" width="180" />
            <el-table-column label="操作" width="160" fixed="right">
              <template #default="{ row }">
                <el-button size="small" @click="saveUser(row)">保存</el-button>
                <el-button size="small" type="danger" @click="deleteUser(row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </template>

        <template v-if="activeMenu === 'knowledge'">
          <div class="page-head inline">
            <div><h2>知识库管理</h2><p>维护飞书知识库 SpaceID 和 NodeID。</p></div>
            <el-button type="warning" @click="clearFeishuCache">刷新飞书缓存</el-button>
          </div>
          <el-form class="knowledge-form" :model="knowledgeForm">
            <el-input v-model="knowledgeForm.name" placeholder="知识库名称" />
            <el-input v-model="knowledgeForm.feishuSpaceId" placeholder="SpaceID" />
            <el-input v-model="knowledgeForm.feishuNodeId" placeholder="NodeID" />
            <el-input v-model="knowledgeForm.remark" placeholder="备注" />
            <div class="form-switch-row">
              <span>启用</span>
              <el-switch v-model="knowledgeForm.status" :active-value="1" :inactive-value="0" />
            </div>
            <div class="form-actions-inline">
              <el-button type="primary" @click="saveKnowledge">{{ editingKnowledgeId ? "保存" : "新增" }}</el-button>
              <el-button @click="resetKnowledgeForm">清空</el-button>
            </div>
          </el-form>

          <el-divider />
          <div class="batch-import-row">
            <el-input v-model="batchNodeToken" placeholder="输入 NodeToken（如 wikcnXXX），自动获取标题与 SpaceID" style="max-width: 400px" />
            <el-button type="primary" @click="batchImportKnowledge" :loading="batchImporting">导入</el-button>
          </div>
          <el-table :data="knowledge" stripe>
            <el-table-column prop="id" label="ID" width="80" />
            <el-table-column prop="name" label="名称" min-width="160" />
            <el-table-column prop="feishuSpaceId" label="SpaceID" min-width="180" show-overflow-tooltip />
            <el-table-column prop="feishuNodeId" label="NodeID" min-width="180" show-overflow-tooltip />
            <el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
            <el-table-column label="状态" width="120">
              <template #default="{ row }">
                <el-switch
                  v-model="row.status"
                  :active-value="1"
                  :inactive-value="0"
                  @change="saveKnowledgeRow(row)"
                />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="160" fixed="right">
              <template #default="{ row }">
                <el-button size="small" @click="editKnowledge(row)">编辑</el-button>
                <el-button size="small" type="danger" @click="deleteKnowledge(row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </template>

        <template v-if="activeMenu === 'prompt'">
          <div class="page-head inline">
            <div><h2>Agent 管理</h2><p>配置提示词、模型、知识库和调试参数，并在右侧实时预览对话效果。</p></div>
            <el-button @click="resetPrompt">恢复默认提示词</el-button>
          </div>
          <section class="agent-workbench">
            <div class="agent-config-panel">
              <el-form label-position="top">
                <el-form-item label="Agent 提示词">
                  <el-input v-model="promptContent" type="textarea" :rows="10" />
                </el-form-item>
                <div class="agent-config-grid">
                  <el-form-item label="调试模型">
                    <el-select v-model="agentForm.modelId" placeholder="选择已接入模型">
                      <el-option
                        v-for="model in models"
                        :key="model.id"
                        :label="model.displayName || model.modelName"
                        :value="model.id"
                      />
                    </el-select>
                  </el-form-item>
                  <el-form-item label="调试知识库">
                    <el-select v-model="agentForm.knowledgeIds" multiple collapse-tags placeholder="默认使用全部启用知识库">
                      <el-option v-for="item in knowledge" :key="item.id" :label="item.name" :value="item.id" />
                    </el-select>
                  </el-form-item>
                  <el-form-item label="Temperature">
                    <el-input-number v-model="agentForm.temperature" :min="0" :max="2" :step="0.1" />
                  </el-form-item>
                  <el-form-item label="Top P">
                    <el-input-number v-model="agentForm.topP" :min="0" :max="1" :step="0.05" />
                  </el-form-item>
                  <el-form-item label="Top K">
                    <el-input-number v-model="agentForm.topK" :min="1" :max="1000" />
                  </el-form-item>
                  <el-form-item label="Max Tokens">
                    <el-input-number v-model="agentForm.maxToken" :min="1" :max="100000" />
                  </el-form-item>
                  <el-form-item label="Presence Penalty">
                    <el-input-number v-model="agentForm.presencePenalty" :min="-2" :max="2" :step="0.1" />
                  </el-form-item>
                  <el-form-item label="Frequency Penalty">
                    <el-input-number v-model="agentForm.frequencyPenalty" :min="-2" :max="2" :step="0.1" />
                  </el-form-item>
                </div>
                <div class="actions">
                  <el-button type="primary" @click="savePrompt">保存 Agent 提示词</el-button>
                  <el-button @click="clearAgentPreview">清空预览</el-button>
                </div>
              </el-form>
            </div>
            <aside class="agent-preview-panel">
              <div class="agent-preview-head">
                <h3>调试预览</h3>
                <span>{{ models.find((model) => model.id === agentForm.modelId)?.modelName || "未选择模型" }}</span>
              </div>
              <div class="agent-preview-chat">
                <article
                  v-for="(message, index) in agentPreviewMessages"
                  :key="`${message.role}-${index}`"
                  class="agent-preview-message-row"
                  :class="message.role"
                >
                  <div class="agent-preview-avatar">{{ message.role === "user" ? "你" : "AI" }}</div>
                  <div class="agent-preview-bubble">
                    <strong>{{ message.role === "user" ? "你" : "Agent" }}</strong>
                    <template v-if="message.role === 'assistant'">
                      <details v-if="agentMessageParts(message.content).reasoning" class="agent-preview-thinking">
                        <summary>思考过程</summary>
                        <pre>{{ agentMessageParts(message.content).reasoning }}</pre>
                      </details>
                      <pre>{{ agentMessageParts(message.content).answer }}</pre>
                    </template>
                    <pre v-else>{{ message.content }}</pre>
                  </div>
                </article>
                <article v-if="agentDebugging" class="agent-preview-message-row assistant thinking">
                  <div class="agent-preview-avatar">AI</div>
                  <div class="agent-preview-bubble">
                    <strong>Agent</strong>
                    <pre>正在思考中...</pre>
                  </div>
                </article>
              </div>
              <div class="agent-preview-composer">
                <el-input
                  v-model="agentForm.question"
                  type="textarea"
                  :rows="2"
                  resize="none"
                  placeholder="向 Agent 发送测试问题"
                  @keydown.enter.exact.prevent="debugAgent"
                />
                <el-button type="primary" :loading="agentDebugging" @click="debugAgent">发送</el-button>
              </div>
            </aside>
          </section>
        </template>

        <template v-if="activeMenu === 'models'">
          <div class="page-head inline">
            <div><h2>模型管理</h2><p>DeepSeek、MiniMax 可快速添加；其他供应商按 OpenAI 兼容或 Anthropic Messages 协议添加。</p></div>
            <el-button @click="resetModelForm">清空表单</el-button>
          </div>

          <section class="quick-model-panel">
            <div class="quick-model-head">
              <div>
                <h3>快速添加</h3>
                <p>适用于 DeepSeek 和 MiniMax，只需要填写 API Key 并选择模型。</p>
              </div>
            </div>
            <div class="quick-model-grid">
              <el-form-item label="厂商">
                <el-select v-model="quickModelForm.provider" @change="applyQuickProvider">
                  <el-option
                    v-for="provider in quickModelProviders"
                    :key="provider.provider"
                    :label="provider.label"
                    :value="provider.provider"
                  />
                </el-select>
              </el-form-item>
              <el-form-item label="模型">
                <el-select v-model="quickModelForm.modelName" :key="quickModelForm.provider">
                  <el-option
                    v-for="model in selectedQuickProvider.models"
                    :key="model.value"
                    :label="model.label"
                    :value="model.value"
                  />
                </el-select>
              </el-form-item>
              <el-form-item label="API Key">
                <el-input v-model="quickModelForm.apiKey" placeholder="填写后保存，列表中只展示掩码" show-password />
              </el-form-item>
              <el-form-item label=" ">
                <el-button type="primary" class="full-btn" @click="quickAddModel">快速新增</el-button>
              </el-form-item>
            </div>
          </section>

          <el-form class="model-config-panel" label-position="top" :model="modelForm">
            <div class="model-config-grid">
              <el-form-item label="协议模板">
                <el-select placeholder="选择协议模板" clearable @change="applyModelPreset">
                  <el-option v-for="preset in protocolPresets" :key="preset.label" :label="preset.label" :value="preset.label" />
                </el-select>
              </el-form-item>
              <el-form-item label="供应商标识">
                <el-input v-model="modelForm.provider" placeholder="custom-openai / custom-anthropic" />
              </el-form-item>
              <el-form-item label="显示名称">
                <el-input v-model="modelForm.displayName" placeholder="例如：自建网关生产模型" />
              </el-form-item>
              <el-form-item label="协议类型">
                <el-select v-model="modelForm.apiType">
                  <el-option label="OpenAI 兼容" value="openai_compatible" />
                  <el-option label="Anthropic Messages" value="anthropic_messages" />
                  <el-option label="MiniMax 官方" value="minimax" />
                </el-select>
              </el-form-item>
              <el-form-item label="模型名">
                <el-input v-model="modelForm.modelName" placeholder="gpt-4.1 / deepseek-chat / claude..." />
              </el-form-item>
              <el-form-item label="Base URL">
                <el-input v-model="modelForm.baseUrl" placeholder="https://api.example.com/v1" />
              </el-form-item>
              <el-form-item label="API URL（可覆盖）">
                <el-input v-model="modelForm.apiUrl" placeholder="留空则按协议自动拼接" />
              </el-form-item>
              <el-form-item label="鉴权方式">
                <el-select v-model="modelForm.authType">
                  <el-option label="Bearer Token" value="bearer" />
                  <el-option label="x-api-key" value="api_key" />
                </el-select>
              </el-form-item>
              <el-form-item label="API Key">
                <el-input v-model="modelForm.apiKey" placeholder="保存时会覆盖旧 Key" show-password />
              </el-form-item>
              <el-form-item label="API Version">
                <el-input v-model="modelForm.apiVersion" placeholder="Anthropic 默认 2023-06-01" />
              </el-form-item>
              <el-form-item label="超时秒数">
                <el-input-number v-model="modelForm.timeoutSecond" :min="1" :max="300" />
              </el-form-item>
            </div>
            <el-form-item label="备注">
              <el-input v-model="modelForm.remark" placeholder="用途、额度、注意事项等" />
            </el-form-item>
            <div class="actions">
              <el-button type="primary" @click="saveModel">{{ editingModelId ? "保存模型" : "新增模型" }}</el-button>
              <el-button @click="resetModelForm">取消编辑</el-button>
            </div>
          </el-form>
          <el-table :data="models" stripe>
            <el-table-column prop="displayName" label="显示名称" width="170" />
            <el-table-column prop="provider" label="Provider" />
            <el-table-column prop="apiType" label="协议" width="170" />
            <el-table-column prop="modelName" label="模型" />
            <el-table-column prop="baseUrl" label="Base URL" min-width="220" show-overflow-tooltip />
            <el-table-column prop="authType" label="鉴权" width="110" />
            <el-table-column prop="enabled" label="启用" width="90" />
            <el-table-column label="操作" width="210" fixed="right">
              <template #default="{ row }">
                <el-button size="small" @click="editModel(row)">编辑</el-button>
                <el-button size="small" @click="testModel(row.id)">测试</el-button>
                <el-button size="small" type="primary" @click="enableModel(row.id)">启用</el-button>
                <el-button size="small" type="danger" @click="deleteModel(row.id)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </template>

        <template v-if="activeMenu === 'configs'">
          <div class="page-head inline">
            <div><h2>系统配置</h2><p>按业务分组维护运行时配置，默认每日额度已接入学员新增和导入。</p></div>
            <el-button type="primary" @click="saveSystemSettings">保存系统配置</el-button>
          </div>
          <section class="settings-grid">
            <div v-for="section in systemSettingSections" :key="section.title" class="settings-section">
              <div class="settings-section-head">
                <h3>{{ section.title }}</h3>
                <p>{{ section.description }}</p>
              </div>
              <div class="settings-fields">
                <label v-for="setting in section.settings" :key="setting.key" class="setting-field">
                  <span>{{ setting.label }}</span>
                  <el-input-number
                    v-if="setting.type === 'number'"
                    v-model="systemSettingValues[setting.key]"
                    :min="setting.min"
                    :max="setting.max"
                  />
                  <el-input
                    v-else-if="setting.type === 'text'"
                    v-model="systemSettingValues[setting.key]"
                    :placeholder="setting.placeholder"
                  />
                  <el-input
                    v-else-if="setting.type === 'password'"
                    v-model="systemSettingValues[setting.key]"
                    :placeholder="setting.placeholder"
                    type="password"
                    show-password
                  />
                  <el-switch v-else v-model="systemSettingValues[setting.key]" />
                  <small>{{ setting.description }}</small>
                </label>
              </div>
            </div>
          </section>
        </template>

        <template v-if="activeMenu === 'records'">
          <div class="page-head"><h2>记录审计</h2><p>聊天、AI 请求和后台操作记录。</p></div>
          <el-tabs v-model="recordTab">
            <el-tab-pane label="聊天记录" name="chats">
              <div class="record-filter">
                <el-input v-model="chatFilters.keyword" placeholder="搜索标题、手机号、姓名、聊天内容" clearable />
                <el-input-number v-model="chatFilters.userId" :min="1" placeholder="用户ID" />
                <el-select v-model="chatFilters.status" placeholder="消息状态" clearable>
                  <el-option label="全部状态" value="" />
                  <el-option label="已完成" value="FINISHED" />
                  <el-option label="生成中" value="GENERATING" />
                  <el-option label="已停止" value="STOPPED" />
                  <el-option label="失败" value="FAILED" />
                </el-select>
                <div class="record-date-range" aria-label="时间范围">
                  <label class="record-date-field">
                    <span>开始时间</span>
                    <input v-model="chatFilters.dateFrom" type="datetime-local" aria-label="开始时间" />
                  </label>
                  <span class="record-date-sep">至</span>
                  <label class="record-date-field">
                    <span>结束时间</span>
                    <input v-model="chatFilters.dateTo" type="datetime-local" aria-label="结束时间" />
                  </label>
                </div>
                <div class="record-filter-actions">
                  <el-button type="primary" @click="searchChats">搜索</el-button>
                  <el-button @click="resetChatFilters">重置</el-button>
                  <el-button @click="exportChats">导出</el-button>
                </div>
              </div>
              <el-table :data="chats" stripe>
                <el-table-column prop="id" label="会话ID" width="90" />
                <el-table-column prop="userPhone" label="手机号" width="140" />
                <el-table-column prop="userName" label="用户" width="120" />
                <el-table-column prop="title" label="会话标题" min-width="220" show-overflow-tooltip />
                <el-table-column prop="messageCount" label="消息数" width="90" />
                <el-table-column prop="lastMessageAt" label="最后消息" width="180" />
                <el-table-column label="操作" width="100" fixed="right">
                  <template #default="{ row }">
                    <el-button size="small" @click="openChatDetail(row)">详情</el-button>
                  </template>
                </el-table-column>
              </el-table>
            </el-tab-pane>
            <el-tab-pane label="AI 请求" name="aiLogs">
              <el-table :data="aiLogs" stripe>
                <el-table-column prop="sessionId" label="会话ID" width="90" />
                <el-table-column prop="modelName" label="模型" width="160" />
                <el-table-column prop="knowledgeIds" label="知识库" width="120" />
                <el-table-column prop="retrieveCount" label="命中" width="80" />
                <el-table-column label="片段" width="80">
                  <template #default="{ row }">
                    {{ row.retrievedChunks?.length || 0 }}
                  </template>
                </el-table-column>
                <el-table-column prop="totalToken" label="Token" width="90" />
                <el-table-column prop="costMs" label="耗时(ms)" width="100" />
                <el-table-column prop="status" label="状态" width="100" />
                <el-table-column prop="errorMessage" label="错误" min-width="220" show-overflow-tooltip />
                <el-table-column label="操作" width="100" fixed="right">
                  <template #default="{ row }">
                    <el-button size="small" @click="openAiLogDetail(row)">详情</el-button>
                  </template>
                </el-table-column>
              </el-table>
            </el-tab-pane>
            <el-tab-pane label="操作日志" name="operationLogs">
              <el-table :data="operationLogs" stripe>
                <el-table-column prop="module" label="模块" />
                <el-table-column prop="action" label="动作" />
                <el-table-column prop="result" label="结果" />
                <el-table-column prop="createdAt" label="时间" />
              </el-table>
            </el-tab-pane>
          </el-tabs>
        </template>
      </section>
    </section>

    <el-drawer v-model="chatDetailOpen" size="760px" title="聊天详情">
      <template v-if="chatDetail">
        <section class="chat-summary">
          <div><span>会话ID</span><strong>{{ chatDetail.session.id }}</strong></div>
          <div><span>用户</span><strong>{{ chatDetail.session.userName || '-' }}</strong></div>
          <div><span>手机号</span><strong>{{ chatDetail.session.userPhone || '-' }}</strong></div>
          <div><span>消息数</span><strong>{{ chatDetail.session.messageCount }}</strong></div>
        </section>

        <h3 class="detail-title">完整对话</h3>
        <section class="conversation-list">
          <article
            v-for="message in chatDetail.messages"
            :key="message.id"
            class="conversation-message"
            :class="message.role"
          >
            <div class="message-meta">
              <strong>{{ message.role === 'user' ? '用户' : '大本营答疑助手' }}</strong>
              <span>{{ message.createdAt }}</span>
            </div>
            <pre>{{ message.content }}</pre>
            <div class="message-extra">
              <span>状态：{{ message.messageStatus }}</span>
              <span v-if="message.tokenInput">输入 Token：{{ message.tokenInput }}</span>
              <span v-if="message.tokenOutput">输出 Token：{{ message.tokenOutput }}</span>
              <span v-if="message.responseTimeMs">耗时：{{ message.responseTimeMs }}ms</span>
            </div>
          </article>
        </section>

        <h3 class="detail-title">关联 AI 请求</h3>
        <el-collapse>
          <el-collapse-item v-for="log in chatDetail.aiLogs" :key="log.id" :title="`请求 #${log.id} / ${log.status}`">
            <div class="ai-log-grid">
              <span>模型：{{ log.modelName || '-' }}</span>
              <span>知识库：{{ log.knowledgeIds || '-' }}</span>
              <span>命中数：{{ log.retrieveCount }}</span>
              <span>Token：{{ log.totalToken || '-' }}</span>
              <span>耗时：{{ log.costMs || '-' }}ms</span>
              <span>时间：{{ log.createdAt }}</span>
            </div>
            <section v-if="log.retrievedChunks?.length" class="retrieval-chunks">
              <article v-for="chunk in log.retrievedChunks" :key="`${log.id}-${chunk.index}`" class="retrieval-chunk">
                <div class="chunk-head">
                  <strong>#{{ chunk.index }} {{ chunk.knowledgeName || '未知知识库' }}</strong>
                  <span>{{ chunk.title || '未命名片段' }}</span>
                </div>
                <pre>{{ chunk.content }}</pre>
                <a v-if="chunk.sourceUrl" :href="chunk.sourceUrl" target="_blank" rel="noreferrer">查看来源</a>
              </article>
            </section>
            <el-empty v-else description="本条请求未保存召回片段" :image-size="64" />
            <pre class="prompt-preview">{{ log.prompt || '无 Prompt 记录' }}</pre>
            <p v-if="log.errorMessage" class="error-text">{{ log.errorMessage }}</p>
          </el-collapse-item>
        </el-collapse>
      </template>
    </el-drawer>

    <el-drawer v-model="aiLogDetailOpen" size="820px" title="AI 请求详情">
      <template v-if="selectedAiLog">
        <section class="chat-summary">
          <div><span>请求ID</span><strong>{{ selectedAiLog.id }}</strong></div>
          <div><span>会话ID</span><strong>{{ selectedAiLog.sessionId || '-' }}</strong></div>
          <div><span>消息ID</span><strong>{{ selectedAiLog.messageId || '-' }}</strong></div>
          <div><span>用户ID</span><strong>{{ selectedAiLog.userId || '-' }}</strong></div>
          <div><span>模型</span><strong>{{ selectedAiLog.modelName || '-' }}</strong></div>
          <div><span>状态</span><strong>{{ selectedAiLog.status }}</strong></div>
        </section>

        <section class="audit-detail-grid">
          <span>知识库：{{ selectedAiLog.knowledgeIds || '-' }}</span>
          <span>命中数：{{ selectedAiLog.retrieveCount }}</span>
          <span>片段数：{{ selectedAiLog.retrievedChunks?.length || 0 }}</span>
          <span>输入 Token：{{ selectedAiLog.inputToken || '-' }}</span>
          <span>输出 Token：{{ selectedAiLog.outputToken || '-' }}</span>
          <span>总 Token：{{ selectedAiLog.totalToken || '-' }}</span>
          <span>耗时：{{ selectedAiLog.costMs || '-' }}ms</span>
          <span>时间：{{ selectedAiLog.createdAt }}</span>
        </section>

        <p v-if="selectedAiLog.errorMessage" class="error-text">{{ selectedAiLog.errorMessage }}</p>

        <h3 class="detail-title">召回知识片段</h3>
        <section v-if="selectedAiLog.retrievedChunks?.length" class="retrieval-chunks">
          <article
            v-for="chunk in selectedAiLog.retrievedChunks"
            :key="`${selectedAiLog.id}-${chunk.index}`"
            class="retrieval-chunk"
          >
            <div class="chunk-head">
              <strong>片段 {{ chunk.index }}/{{ selectedAiLog.retrievedChunks.length }}</strong>
              <span>{{ chunk.knowledgeName || '未知知识库' }}</span>
              <span v-if="chunk.knowledgeId">知识库ID：{{ chunk.knowledgeId }}</span>
            </div>
            <div class="chunk-title">{{ chunk.title || '未命名片段' }}</div>
            <pre>{{ chunk.content }}</pre>
            <a v-if="chunk.sourceUrl" :href="chunk.sourceUrl" target="_blank" rel="noreferrer">查看来源</a>
          </article>
        </section>
        <el-empty v-else description="本条请求未保存召回片段" :image-size="72" />

        <h3 class="detail-title">完整 Prompt</h3>
        <pre class="prompt-preview">{{ selectedAiLog.prompt || '无 Prompt 记录' }}</pre>
      </template>
    </el-drawer>
  </main>
</template>
