<template>
  <div class="page page--flush">
    <div class="main-content-flat unified-shell">
      <!-- 统计概览 -->
      <div class="stats-row">
        <div class="stat-card stat-card--total">
          <div class="stat-icon">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
          </div>
          <div class="stat-body">
            <span class="stat-value">{{ total }}</span>
            <span class="stat-label">日志总数</span>
          </div>
        </div>
        <div class="stat-card stat-card--success">
          <div class="stat-icon">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
          </div>
          <div class="stat-body">
            <span class="stat-value">{{ successCount }}</span>
            <span class="stat-label">{{ TEXT.audit.resultSuccess }}</span>
          </div>
        </div>
        <div class="stat-card stat-card--fail">
          <div class="stat-icon">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
          </div>
          <div class="stat-body">
            <span class="stat-value">{{ failCount }}</span>
            <span class="stat-label">{{ TEXT.audit.resultFail }}</span>
          </div>
        </div>
        <div class="stat-card stat-card--operators">
          <div class="stat-icon">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
          </div>
          <div class="stat-body">
            <span class="stat-value">{{ operatorCount }}</span>
            <span class="stat-label">操作人数</span>
          </div>
        </div>
      </div>

      <!-- 筛选栏 -->
      <div class="audit-toolbar unified-action-bar">
        <el-form :inline="true" :model="filters" class="filter-form">
          <div class="filter-item-form">
            <el-select v-model="filters.eventType" clearable :placeholder="TEXT.modules.adminAuditLogs.filterEvent" @change="onServerFilterChange" class="filter-select-comp">
              <el-option v-for="opt in eventTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
            </el-select>
          </div>
          <div class="filter-item-form">
            <el-select v-model="filters.operatorId" clearable :placeholder="TEXT.modules.adminAuditLogs.filterOperator" filterable @change="onServerFilterChange" class="filter-select-comp">
              <el-option v-for="u in userOptions" :key="u.value" :label="u.label" :value="u.value" />
            </el-select>
          </div>
          <div class="filter-item-form">
            <el-select v-model="filters.result" clearable :placeholder="TEXT.modules.adminAuditLogs.filterResult" @change="onLocalFilterChange" class="filter-select-result">
              <el-option :label="TEXT.audit.resultSuccess" value="SUCCESS" />
              <el-option :label="TEXT.audit.resultFail" value="FAIL" />
            </el-select>
          </div>
          <div class="filter-item-form">
            <el-date-picker
              v-model="filters.range"
              type="daterange"
              unlink-panels
              :start-placeholder="TEXT.modules.adminAuditLogs.rangeStart"
              :end-placeholder="TEXT.modules.adminAuditLogs.rangeEnd"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
              class="date-range-picker-comp"
              @change="onLocalFilterChange"
            />
          </div>
          <div class="filter-spacer"></div>
          <el-dropdown trigger="click" @command="handleExportCommand" class="audit-export-dropdown">
            <el-button plain class="audit-export-btn" :loading="exportLoading">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14" style="margin-right:4px"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
              导出 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
            </el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item v-if="isAdmin" command="system">{{ TEXT.modules.adminAuditLogs.exportSystem }}</el-dropdown-item>
                <el-dropdown-item v-if="canProjectExport" command="project">{{ TEXT.modules.adminAuditLogs.exportProject }}</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </el-form>
      </div>

      <!-- 日志表格 -->
      <div class="unified-section table-section">
        <el-table :data="logs" v-loading="loading" class="audit-table" style="width: 100%" table-layout="fixed">
          <el-table-column prop="timestamp" :label="TEXT.modules.adminAuditLogs.columns.time" width="155">
            <template #default="scope">
              <span class="time-text">{{ displayDateTime(scope.row.timestamp) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="actorName" :label="TEXT.modules.adminAuditLogs.columns.actor" width="130">
            <template #default="scope">
              <div class="actor-cell">
                <div class="actor-avatar">{{ getInitials(scope.row.actorName) }}</div>
                <span class="actor-name">{{ scope.row.actorName }}</span>
              </div>
            </template>
          </el-table-column>
          <el-table-column prop="eventLabel" :label="TEXT.modules.adminAuditLogs.columns.event" width="140">
            <template #default="scope">
              <span class="event-tag">{{ scope.row.eventLabel }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="actionText" :label="TEXT.modules.adminAuditLogs.columns.action" show-overflow-tooltip />
          <el-table-column :label="TEXT.modules.adminAuditLogs.columns.target">
            <template #default="scope">
              <div class="target-cell">
                <span v-if="scope.row.targetTypeLabel" class="target-text">
                  <strong>{{ scope.row.targetTypeLabel }}</strong><span v-if="scope.row.targetName" class="text-muted">：{{ scope.row.targetName }}</span>
                </span>
                <span v-else class="text-muted">{{ TEXT.common.fallback }}</span>
              </div>
            </template>
          </el-table-column>
          <el-table-column :label="TEXT.modules.adminAuditLogs.columns.diff">
            <template #default="scope">
              <div v-if="scope.row.diffText?.length" class="diff-container">
                <div v-for="(line, idx) in getDiffPreview(scope.row)" :key="idx" class="diff-line">{{ formatDiffLine(line) }}</div>
                <div class="diff-more-row" v-if="scope.row.diffText.length > DIFF_PREVIEW_LIMIT">
                  <span class="diff-more-text">其余 {{ scope.row.diffText.length - DIFF_PREVIEW_LIMIT }} 项</span>
                  <el-button link type="primary" size="small" @click="openDetail(scope.row)">{{ TEXT.common.actions.view }}</el-button>
                </div>
              </div>
              <span v-else class="text-muted">{{ TEXT.audit.emptyValue }}</span>
            </template>
          </el-table-column>
          <el-table-column :label="TEXT.modules.adminAuditLogs.columns.result" width="80" align="center">
            <template #default="scope">
              <span class="result-badge" :class="scope.row.result === 'SUCCESS' ? 'result--success' : 'result--fail'">
                {{ scope.row.resultLabel }}
              </span>
            </template>
          </el-table-column>
        </el-table>
        <div class="pagination-wrap" v-if="total > 0">
          <el-pagination
            v-model:current-page="page"
            v-model:page-size="pageSize"
            :page-sizes="[5, 10, 20]"
            :total="total"
            layout="prev, pager, next, sizes, total"
            small
            @current-change="onPageChange"
            @size-change="onPageSizeChange"
          />
        </div>
      </div>
    </div>

    <!-- 详情抽屉 -->
    <el-drawer
      v-if="detailVisible"
      v-model="detailVisible"
      direction="rtl"
      size="580px"
      :close-on-click-modal="false"
      :show-close="false"
      class="audit-detail-drawer"
    >
      <template #header>
        <div class="drawer-header">
          <div class="drawer-title-row">
            <div class="drawer-icon">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" width="18" height="18"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
            </div>
            <span class="drawer-title">审计详情</span>
          </div>
          <el-button @click="detailVisible = false" size="small">{{ TEXT.common.actions.close }}</el-button>
        </div>
      </template>
      <template v-if="selectedLog">
        <!-- 基本信息 -->
        <div class="detail-meta-grid">
          <div class="meta-card">
            <span class="meta-label">时间</span>
            <span class="meta-value">{{ displayDateTime(selectedLog.timestamp) }}</span>
          </div>
          <div class="meta-card">
            <span class="meta-label">操作人</span>
            <span class="meta-value">{{ selectedLog.actorName || TEXT.common.fallback }}</span>
          </div>
          <div class="meta-card">
            <span class="meta-label">事件</span>
            <span class="meta-value">{{ selectedLog.eventLabel || TEXT.common.fallback }}</span>
          </div>
          <div class="meta-card">
            <span class="meta-label">结果</span>
            <span class="meta-value">
              <span class="result-badge" :class="selectedLog.result === 'SUCCESS' ? 'result--success' : 'result--fail'">{{ selectedLog.resultLabel }}</span>
            </span>
          </div>
        </div>
        <div class="detail-meta-grid detail-meta-full">
          <div class="meta-card meta-card-full">
            <span class="meta-label">对象</span>
            <span class="meta-value">{{ selectedLog.targetTypeLabel || TEXT.common.fallback }}<span v-if="selectedLog.targetName">：{{ selectedLog.targetName }}</span></span>
          </div>
          <div class="meta-card meta-card-full">
            <span class="meta-label">动作</span>
            <span class="meta-value">{{ selectedLog.actionText || TEXT.common.fallback }}</span>
          </div>
        </div>

        <!-- 变更明细 -->
        <div class="detail-section">
          <div class="detail-section-title">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
            变更明细
            <span class="diff-count" v-if="selectedLog.diffText?.length">{{ selectedLog.diffText.length }} 条</span>
          </div>
          <el-scrollbar max-height="55vh">
            <div v-if="selectedLog.diffText?.length" class="detail-lines">
              <div v-for="(line, idx) in selectedLog.diffText" :key="idx" class="detail-line-row">
                <span class="detail-line-index">{{ Number(idx) + 1 }}</span>
                <div class="detail-line-content">
                  <template v-if="parseDiffLine(formatDiffLine(line))">
                    <span class="diff-field">{{ parseDiffLine(formatDiffLine(line))!.field }}</span>
                    <div class="diff-values">
                      <span class="diff-old">{{ parseDiffLine(formatDiffLine(line))!.oldVal }}</span>
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12" class="diff-arrow"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
                      <span class="diff-new">{{ parseDiffLine(formatDiffLine(line))!.newVal }}</span>
                    </div>
                  </template>
                  <span v-else class="detail-line-text">{{ formatDiffLine(line) }}</span>
                </div>
              </div>
            </div>
            <div v-else class="empty-diff">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="32" height="32" style="color:var(--ctms-text-disabled)"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
              <span class="text-muted">无变更记录</span>
            </div>
          </el-scrollbar>
        </div>
      </template>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { ArrowDown } from "@element-plus/icons-vue";
import { createAuditEvent, fetchAuditLogs } from "../../api/auditLogs";
import { fetchApiEndpointPermissions } from "../../api/projectPermissions";
import { fetchUsers } from "../../api/users";
import { listMembers } from "../../api/members";
import { auditDict, normalizeAuditEvent } from "../../audit";
import { useStudyStore } from "../../store/study";
import { useAuthStore } from "../../store/auth";
import { roleDict, getDictLabel } from "../../dictionaries";
import { exportAuditCsv } from "../../audit/export/auditExportService";
import { displayDateTime } from "../../utils/display";
import { getProjectRole, isSystemAdmin } from "../../utils/roles";
import { isApiPermissionAllowed } from "../../utils/apiPermissionValue";
import { TEXT } from "../../locales";

const study = useStudyStore();
const auth = useAuthStore();
const router = useRouter();

const loading = ref(false);
const exportLoading = ref(false);
const logs = ref<any[]>([]);
const users = ref<any[]>([]);
const allLogs = ref<any[]>([]);
const permissionMatrix = ref<any | null>(null);
const page = ref(1);
const pageSize = ref(20);
const total = ref(0);
const detailVisible = ref(false);
const selectedLog = ref<any | null>(null);
const DIFF_PREVIEW_LIMIT = 2;

const filters = ref({
  eventType: "",
  operatorId: "",
  result: "",
  range: [],
});

const successCount = computed(() => allLogs.value.filter(l => (l.result || 'SUCCESS') === 'SUCCESS').length);
const failCount = computed(() => allLogs.value.filter(l => l.result === 'FAIL').length);
const operatorCount = computed(() => new Set(allLogs.value.map(l => l.actorId || l.actorName)).size);

const getInitials = (name: string) => {
  if (!name) return '?';
  const parts = name.trim().split(/\s+/);
  if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
  return name.slice(0, 2).toUpperCase();
};

const formatDiffLine = (line: any): string => {
  if (typeof line === 'string') return line;
  if (line === null || line === undefined) return '-';
  try {
    return JSON.stringify(line, null, 0);
  } catch {
    return String(line);
  }
};

const parseDiffLine = (line: string): { field: string; oldVal: string; newVal: string } | null => {
  const match = line.match(/^(.+?)：(.+?)\s*->\s*(.+)$/);
  if (!match) return null;
  return { field: match[1], oldVal: match[2], newVal: match[3] };
};

const eventTypeOptions = Object.entries(auditDict).map(([value, cfg]) => ({
  value,
  label: cfg.label,
}));
const resolveUserDisplayName = (u: any): string => {
  return u?.full_name || u?.display_name || u?.username || u?.email || u?.id || TEXT.common.fallback;
};

const userOptions = computed(() => users.value.map((u: any) => ({ label: resolveUserDisplayName(u), value: u.id })));
const isAdmin = computed(() => isSystemAdmin(auth.user));
const projectRole = computed(() => getProjectRole(study.currentStudy, study.currentStudyRole));
const canProjectExport = computed(() => {
  if (isAdmin.value) return true;
  return projectRole.value === "PM" && isApiPermissionAllowed(permissionMatrix.value?.[projectRole.value]?.["audit_logs:read"]);
});
const canAccessAuditLogs = computed(() => {
  if (isAdmin.value) return true;
  return projectRole.value === "PM" && isApiPermissionAllowed(permissionMatrix.value?.[projectRole.value]?.["audit_logs:read"]);
});

const ensureAccess = () => {
  if (!canAccessAuditLogs.value) {
    router.replace(study.currentStudy ? "/project/overview" : "/admin/projects");
  }
};

const loadPermissionMatrix = async () => {
  if (!study.currentStudy) return;
  const { data } = await fetchApiEndpointPermissions(study.currentStudy.id);
  permissionMatrix.value = data;
};

const loadUsers = async () => {
  try {
    if (isAdmin.value) {
      const { data } = await fetchUsers({ limit: 500 });
      users.value = Array.isArray(data) ? data : (data as any).items || [];
    } else if (study.currentStudy) {
      const { data } = await listMembers(study.currentStudy.id, { limit: 500 });
      const items = Array.isArray(data) ? data : (data as any).items || [];
      users.value = items.map((m: any) => ({
        id: m.user_id,
        full_name: m.user?.display_name || m.user?.full_name || m.user?.username || m.username,
      }));
    }
  } catch {
    users.value = [];
  }
};

const loadLogs = async () => {
  if (!study.currentStudy) return;
  loading.value = true;
  try {
    if (!permissionMatrix.value) {
      await loadPermissionMatrix();
    }
    const batchLimit = 500;
    let skip = 0;
    const allItems: any[] = [];
    const params: Record<string, any> = {
      skip,
      limit: batchLimit,
      action: filters.value.eventType || undefined,
      operator_id: filters.value.operatorId || undefined,
    };
    while (true) {
      params.skip = skip;
      const { data } = await fetchAuditLogs(study.currentStudy.id, params);
      const items = Array.isArray(data) ? data : (data as any).items || [];
      allItems.push(...items);
      if (items.length < batchLimit) break;
      skip += items.length;
    }
    enrichLogs(allItems);
    refreshPagedLogs();
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || TEXT.modules.adminAuditLogs.loadFailed);
  } finally {
    loading.value = false;
  }
};

const enrichLogs = (items: any[]) => {
  const userMap = users.value.reduce<Record<string, string>>((acc, cur) => {
    acc[cur.id] = resolveUserDisplayName(cur);
    return acc;
  }, {});
  allLogs.value = items
    .map((log) => normalizeAuditEvent(log, userMap))
    .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
  if (users.value.length === 0) {
    const byActor = new Map<string, string>();
    allLogs.value.forEach((log) => {
      if (!byActor.has(log.actorId)) {
        byActor.set(log.actorId, log.actorName || log.actorId);
      }
    });
    users.value = Array.from(byActor.entries()).map(([id, full_name]) => ({ id, full_name }));
  }
};

const filterLogs = (items: any[]) =>
  items.filter((log) => {
    if (filters.value.result && (log.result || "SUCCESS") !== filters.value.result) return false;
    if (filters.value.range?.length === 2) {
      const ts = new Date(log.timestamp);
      const start = new Date(filters.value.range[0]);
      const end = new Date(filters.value.range[1]);
      if (ts < start || ts > end) return false;
    }
    return true;
  });

const refreshPagedLogs = () => {
  const filtered = filterLogs(allLogs.value);
  total.value = filtered.length;
  const maxPage = Math.max(1, Math.ceil(total.value / pageSize.value));
  if (page.value > maxPage) page.value = maxPage;
  const start = (page.value - 1) * pageSize.value;
  logs.value = filtered.slice(start, start + pageSize.value);
};

const onServerFilterChange = () => {
  page.value = 1;
  loadLogs();
};

const onLocalFilterChange = () => {
  page.value = 1;
  refreshPagedLogs();
};

const onPageChange = (p: number) => {
  page.value = p;
  refreshPagedLogs();
};

const onPageSizeChange = (size: number) => {
  pageSize.value = size;
  page.value = 1;
  refreshPagedLogs();
};

const getDiffPreview = (log: any) => {
  const lines = Array.isArray(log?.diffText) ? log.diffText : [];
  return lines.slice(0, DIFF_PREVIEW_LIMIT);
};

const openDetail = (log: any) => {
  selectedLog.value = log;
  detailVisible.value = true;
};

const fetchAllForExport = async () => {
  if (!study.currentStudy) return [];
  exportLoading.value = true;
  try {
    const params: Record<string, any> = {
      skip: 0,
      limit: 2000,
      action: filters.value.eventType || undefined,
      operator_id: filters.value.operatorId || undefined,
    };
    const { data } = await fetchAuditLogs(study.currentStudy.id, params);
    const items = Array.isArray(data) ? data : (data as any).items || [];
    const userMap = users.value.reduce<Record<string, string>>((acc, cur) => {
      acc[cur.id] = resolveUserDisplayName(cur);
      return acc;
    }, {});
    const filtered = items.filter((log: any) => {
      if (filters.value.range?.length === 2) {
        const ts = new Date(log.created_at);
        const start = new Date(filters.value.range[0]);
        const end = new Date(filters.value.range[1]);
        if (ts < start || ts > end) return false;
      }
      return true;
    });
    return filtered.map((log: any) => normalizeAuditEvent(log, userMap));
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || TEXT.modules.adminAuditLogs.exportLoadFailed);
    return [];
  } finally {
    exportLoading.value = false;
  }
};

const handleExportCommand = (command: string) => {
  confirmExport(command as "system" | "project");
};

const confirmExport = async (scope: "system" | "project") => {
  const currentStudy = study.currentStudy;
  if (!currentStudy) return;
  const ok = await ElMessageBox.confirm(
    TEXT.modules.adminAuditLogs.exportConfirm,
    TEXT.modules.adminAuditLogs.exportConfirmTitle,
    { type: "warning", confirmButtonText: TEXT.common.actions.confirm, cancelButtonText: TEXT.common.actions.cancel }
  ).catch(() => null);
  if (!ok) return;
  const events = await fetchAllForExport();
  if (!events.length) {
    ElMessage.warning(TEXT.modules.adminAuditLogs.exportEmpty);
    return;
  }
  const fileName =
    scope === "system"
      ? `${TEXT.modules.adminAuditLogs.exportSystemName}_${new Date().toISOString().slice(0, 10)}`
      : `${TEXT.modules.adminAuditLogs.exportProjectName}_${currentStudy.code || ""}_${new Date().toISOString().slice(0, 10)}`;
  exportAuditCsv(events, { fileName });
  await createAuditEvent(currentStudy.id, {
    action: scope === "system" ? "AUDIT_EXPORT_SYSTEM" : "AUDIT_EXPORT_PROJECT",
    entity_type: "audit_log",
    entity_id: null,
    detail: JSON.stringify(
      { targetName: currentStudy.name, scope, exportedCount: events.length },
      null,
      0
    ),
  }).catch(() => null);
};

onMounted(async () => {
  if (!auth.user && auth.token) {
    await auth.fetchMe().catch(() => {});
  }
  await loadPermissionMatrix().catch(() => {});
  ensureAccess();
  await loadUsers();
  await loadLogs();
});
</script>

<style scoped>
/* 统计卡片 */
.stats-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  padding: 16px;
  border-bottom: 1px solid var(--unified-shell-divider);
}

.stat-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--ctms-neutral-100);
  transition: var(--ctms-transition);
}

.stat-card:hover {
  transform: translateY(-1px);
  box-shadow: var(--ctms-shadow-sm);
}

.stat-icon {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.stat-icon svg { width: 20px; height: 20px; }
.stat-card--total .stat-icon { background: #e8edf3; color: var(--ctms-primary); }
.stat-card--success .stat-icon { background: #e6f4ed; color: var(--ctms-success); }
.stat-card--fail .stat-icon { background: #fde8e8; color: var(--ctms-danger); }
.stat-card--operators .stat-icon { background: #eef2f6; color: var(--ctms-info); }

.stat-body { display: flex; flex-direction: column; }
.stat-value { font-size: 22px; font-weight: 700; line-height: 1.2; color: var(--ctms-text-main); }
.stat-label { font-size: 12px; color: var(--ctms-text-secondary); margin-top: 2px; }

/* 筛选栏 */
.audit-toolbar {
  border-bottom: 1px solid var(--unified-shell-divider);
}

.filter-form {
  display: flex;
  width: 100%;
  gap: 8px;
  align-items: center;
  flex-wrap: nowrap;
}

.filter-item-form {
  margin-bottom: 0 !important;
  margin-right: 0 !important;
  flex-shrink: 0;
}

.filter-select-comp { width: 132px; }
.filter-select-result { width: 96px; }
.date-range-picker-comp { width: 248px !important; }
.filter-spacer { flex: 1; }

.audit-export-dropdown { flex-shrink: 0; }
.audit-export-btn {
  border-radius: 8px !important;
  font-weight: 600;
  height: 32px;
  color: var(--ctms-primary) !important;
  border-color: #c0cdd7 !important;
  background: #f0f6ff !important;
}
.audit-export-btn:hover {
  color: var(--ctms-primary-hover) !important;
  border-color: #9bb1c2 !important;
  background: #e2edfa !important;
}

/* 表格 */
.table-section { padding: 0; }
.audit-table :deep(.el-table__inner-wrapper::before) { display: none; }

.time-text {
  font-size: 12px;
  color: var(--ctms-text-secondary);
  font-variant-numeric: tabular-nums;
}

.actor-cell {
  display: flex;
  align-items: center;
  gap: 8px;
}

.actor-avatar {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  background: linear-gradient(135deg, var(--ctms-primary), var(--ctms-primary-active));
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  flex-shrink: 0;
}

.actor-name {
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.event-tag {
  display: inline-flex;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  background: #f0f4f8;
  color: var(--ctms-text-regular);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.target-cell {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
}

.text-muted { color: var(--ctms-text-secondary); font-size: 12px; }

.result-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
}

.result--success { background: #e6f4ed; color: #166534; }
.result--fail { background: #fde8e8; color: #991b1b; }

.diff-container {
  font-size: 12px;
  color: var(--ctms-text-regular);
  line-height: 1.5;
  overflow: hidden;
}

.diff-line {
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.diff-more-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
}

.diff-more-text { font-size: 11px; color: var(--ctms-text-disabled); }

.pagination-wrap {
  margin-top: 10px;
  display: flex;
  justify-content: flex-end;
  padding: 8px 16px;
}

/* 详情抽屉 */
.drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.drawer-title-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.drawer-icon {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: var(--ctms-primary-light);
  color: var(--ctms-primary);
  display: flex;
  align-items: center;
  justify-content: center;
}

.drawer-title {
  font-size: 17px;
  font-weight: 700;
  color: var(--ctms-text-main);
}

.detail-meta-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 10px;
}

.detail-meta-full {
  grid-template-columns: 1fr;
}

.meta-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  border: 1px solid var(--ctms-border-color);
  border-radius: 10px;
  background: var(--ctms-neutral-100);
}

.meta-card-full {
  grid-column: 1 / -1;
}

.meta-label {
  font-size: 11px;
  color: var(--ctms-text-secondary);
  font-weight: 500;
}

.meta-value {
  font-size: 13px;
  color: var(--ctms-text-main);
  word-break: break-word;
}

.detail-section {
  margin-top: 14px;
}

.detail-section-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  font-weight: 700;
  color: var(--ctms-text-main);
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--ctms-border-color);
}

.detail-section-title svg {
  color: var(--ctms-primary);
}

.diff-count {
  font-size: 11px;
  font-weight: 600;
  color: var(--ctms-text-secondary);
  background: var(--ctms-neutral-100);
  padding: 1px 6px;
  border-radius: 4px;
  margin-left: auto;
}

.detail-lines {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.detail-line-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--ctms-border-color);
  background: #fff;
  transition: var(--ctms-transition);
}

.detail-line-row:hover {
  border-color: var(--ctms-border-color-hover);
  box-shadow: var(--ctms-shadow-sm);
}

.detail-line-index {
  color: var(--ctms-text-disabled);
  min-width: 20px;
  font-size: 11px;
  font-weight: 700;
  text-align: center;
  padding-top: 2px;
  background: var(--ctms-neutral-100);
  border-radius: 4px;
  height: 20px;
  line-height: 20px;
}

.detail-line-content {
  flex: 1;
  min-width: 0;
}

.diff-field {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  color: var(--ctms-primary);
  margin-bottom: 4px;
}

.diff-values {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  flex-wrap: wrap;
}

.diff-old {
  font-size: 12px;
  color: var(--ctms-danger);
  background: #fef2f2;
  padding: 2px 6px;
  border-radius: 4px;
  word-break: break-word;
  max-width: 45%;
  text-decoration: line-through;
  text-decoration-color: rgba(194, 75, 75, 0.3);
}

.diff-arrow {
  color: var(--ctms-text-disabled);
  flex-shrink: 0;
  margin-top: 3px;
}

.diff-new {
  font-size: 12px;
  color: var(--ctms-success);
  background: #f0fdf4;
  padding: 2px 6px;
  border-radius: 4px;
  word-break: break-word;
  max-width: 45%;
}

.detail-line-text {
  color: var(--ctms-text-regular);
  word-break: break-word;
  font-size: 13px;
  line-height: 1.5;
}

.empty-diff {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 30px 20px;
}

@media (max-width: 768px) {
  .stats-row { grid-template-columns: repeat(2, 1fr); }
  .filter-form { flex-wrap: wrap; }
}
</style>
