<template>
  <section>
    <header class="page-head">
      <div>
        <h2>操作日志</h2>
        <p>记录后台关键动作，支持筛选、导出和查看完整详情。</p>
      </div>
      <div class="head-actions">
        <el-button @click="loadLogs">刷新</el-button>
        <el-button @click="cleanupLogs">清理过期日志</el-button>
        <el-button type="primary" @click="exportLogs">导出日志</el-button>
      </div>
    </header>

    <el-card class="panel" shadow="never">
      <p class="retention-note">日志保留策略：普通后台日志、公开查询和公开下载日志保留 180 天；高风险后台操作保留 365 天。</p>
      <div class="filters">
        <el-input v-model.trim="filters.keyword" clearable placeholder="搜索摘要、对象ID、详情" @keyup.enter="loadLogs" />
        <el-select v-model="filters.action" clearable placeholder="操作类型">
          <el-option v-for="item in actionOptions" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
        <el-select v-model="filters.object_type" clearable placeholder="对象类型">
          <el-option label="项目" value="project_course" />
          <el-option label="学员" value="learner" />
          <el-option label="证书" value="certificate" />
          <el-option label="导入批次" value="import_batch" />
          <el-option label="数据库备份" value="database_backup" />
          <el-option label="系统设置" value="system_setting" />
          <el-option label="公开访问" value="public_access" />
        </el-select>
        <el-select v-model="filters.risk" clearable placeholder="风险等级">
          <el-option label="高风险" value="high" />
          <el-option label="中风险" value="medium" />
          <el-option label="低风险" value="low" />
        </el-select>
        <el-button type="primary" @click="loadLogs">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </div>

      <div v-if="totalPages > 1" class="pager">
        <el-button :disabled="page <= 1" @click="loadLogs(page - 1)">上一页</el-button>
        <template v-for="item in pageItems" :key="item">
          <span v-if="item === '...'" class="ellipsis">...</span>
          <el-button v-else :type="item === page ? 'primary' : 'default'" @click="loadLogs(Number(item))">{{ item }}</el-button>
        </template>
        <el-button :disabled="page >= totalPages" @click="loadLogs(page + 1)">下一页</el-button>
        <span class="total">共 {{ total }} 条</span>
      </div>

      <el-table :data="logs" border>
        <el-table-column prop="created_at" label="时间" width="190" />
        <el-table-column prop="admin_user_id" label="操作人ID" width="100" />
        <el-table-column prop="action_label" label="操作" width="150" />
        <el-table-column prop="object_label" label="对象" width="100" />
        <el-table-column prop="object_id" label="对象ID" width="110" />
        <el-table-column label="风险" width="100">
          <template #default="{ row }">
            <el-tag :type="riskTag(row.risk)">{{ row.risk_label }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="summary" label="摘要" min-width="260" show-overflow-tooltip />
        <el-table-column label="操作" width="90">
          <template #default="{ row }">
            <el-button size="small" @click="openDetail(row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>

      <div v-if="totalPages > 1" class="pager bottom">
        <el-button :disabled="page <= 1" @click="loadLogs(page - 1)">上一页</el-button>
        <template v-for="item in pageItems" :key="`bottom-${item}`">
          <span v-if="item === '...'" class="ellipsis">...</span>
          <el-button v-else :type="item === page ? 'primary' : 'default'" @click="loadLogs(Number(item))">{{ item }}</el-button>
        </template>
        <el-button :disabled="page >= totalPages" @click="loadLogs(page + 1)">下一页</el-button>
        <span class="total">共 {{ total }} 条</span>
      </div>
    </el-card>

    <el-dialog v-model="detailVisible" title="操作日志详情" width="720px">
      <el-descriptions v-if="current" :column="1" border>
        <el-descriptions-item label="时间">{{ current.created_at }}</el-descriptions-item>
        <el-descriptions-item label="操作人ID">{{ current.admin_user_id || "-" }}</el-descriptions-item>
        <el-descriptions-item label="操作">{{ current.action_label }}</el-descriptions-item>
        <el-descriptions-item label="对象">{{ current.object_label }} #{{ current.object_id || "-" }}</el-descriptions-item>
        <el-descriptions-item label="风险等级">
          <el-tag :type="riskTag(current.risk)">{{ current.risk_label }}</el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="摘要">{{ current.summary }}</el-descriptions-item>
      </el-descriptions>
      <pre class="detail-json">{{ prettyDetail }}</pre>
    </el-dialog>
  </section>
</template>

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

import { http, type OperationLog } from "../api";
import { downloadFile } from "../download";

const logs = ref<OperationLog[]>([]);
const current = ref<OperationLog | null>(null);
const detailVisible = ref(false);
const filters = reactive({ keyword: "", action: "", object_type: "", risk: "" });
const page = ref(1);
const pageSize = 20;
const total = ref(0);
const totalPages = ref(1);

const actionOptions = [
  { value: "create_project", label: "新增项目" },
  { value: "update_project", label: "修改项目" },
  { value: "create_learner", label: "新增学员" },
  { value: "update_learner", label: "修改学员" },
  { value: "delete_learner", label: "删除学员" },
  { value: "create_certificate", label: "新增证书" },
  { value: "void_certificate", label: "作废证书" },
  { value: "regenerate_public_token", label: "重置证书链接" },
  { value: "upload_import_file", label: "上传导入文件" },
  { value: "confirm_import_batch", label: "确认导入" },
  { value: "delete_import_batch", label: "删除导入批次" },
  { value: "export_certificates", label: "导出证书" },
  { value: "create_database_backup", label: "创建数据库备份" },
  { value: "restore_database_backup", label: "回滚数据库备份" },
  { value: "update_pdf_settings", label: "修改PDF生成设置" },
  { value: "public_search_certificate", label: "公开查询证书" },
  { value: "public_download_certificate", label: "公开下载证书" },
];

const prettyDetail = computed(() => {
  if (!current.value?.detail_json) return "{}";
  try {
    return JSON.stringify(JSON.parse(current.value.detail_json), null, 2);
  } catch {
    return current.value.detail_json;
  }
});

const pageItems = computed<Array<number | string>>(() => compactPages(page.value, totalPages.value));

function queryParams() {
  const params = new URLSearchParams();
  Object.entries(filters).forEach(([key, value]) => {
    if (value) params.set(key, value);
  });
  params.set("page", String(page.value));
  params.set("page_size", String(pageSize));
  return params.toString();
}

function compactPages(current: number, totalPageCount: number) {
  const pages = new Set<number>();
  pages.add(1);
  for (let item = current - 2; item <= current + 2; item += 1) {
    if (item > 1 && item < totalPageCount) pages.add(item);
  }
  if (totalPageCount > 1) pages.add(totalPageCount);
  const sorted = Array.from(pages).sort((a, b) => a - b);
  const result: Array<number | string> = [];
  sorted.forEach((item, index) => {
    if (index > 0 && item - sorted[index - 1] > 1) result.push("...");
    result.push(item);
  });
  return result;
}

function riskTag(risk?: string | null) {
  if (risk === "high") return "danger";
  if (risk === "medium") return "warning";
  return "success";
}

function openDetail(row: OperationLog) {
  current.value = row;
  detailVisible.value = true;
}

function resetFilters() {
  filters.keyword = "";
  filters.action = "";
  filters.object_type = "";
  filters.risk = "";
  loadLogs(1);
}

async function loadLogs(nextPage = 1) {
  page.value = nextPage;
  const query = queryParams();
  const { data } = await http.get<{ items: OperationLog[]; total: number; page: number; page_size: number; total_pages: number }>(`/admin/logs${query ? `?${query}` : ""}`);
  logs.value = data.items;
  total.value = data.total;
  page.value = data.page;
  totalPages.value = data.total_pages;
}

async function exportLogs() {
  const query = queryParams();
  await downloadFile(`/admin/logs/export${query ? `?${query}` : ""}`, "operation-logs.xlsx");
}

async function cleanupLogs() {
  await http.post("/admin/logs/cleanup");
  await loadLogs();
}

onMounted(loadLogs);
</script>

<style scoped>
.page-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
}

.page-head p {
  margin: 6px 0 0;
  color: #64748b;
}

.head-actions {
  display: flex;
  gap: 8px;
}

.panel {
  border-radius: 8px;
}

.retention-note {
  margin: 0 0 12px;
  color: #64748b;
  font-size: 13px;
}

.filters {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) repeat(3, 160px) auto auto;
  gap: 10px;
  margin-bottom: 14px;
}

.filters :deep(.el-select__wrapper) {
  padding-left: 14px;
  padding-right: 14px;
}

.filters :deep(.el-select__suffix) {
  margin-left: 14px;
}

.pager {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin: 12px 0;
}

.pager.bottom {
  margin-top: 14px;
  margin-bottom: 0;
}

.ellipsis,
.total {
  color: #64748b;
  font-size: 13px;
}

.ellipsis {
  padding: 0 4px;
}

.detail-json {
  margin: 14px 0 0;
  max-height: 360px;
  overflow: auto;
  white-space: pre-wrap;
  border: 1px solid #dfe9ec;
  border-radius: 8px;
  background: #f8fafc;
  padding: 14px;
}

@media (max-width: 1100px) {
  .filters {
    grid-template-columns: 1fr 1fr;
  }
}
</style>
