<template>
  <div class="page">
    <div class="main-content-flat unified-shell">
      <div class="audit-toolbar">
        <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">
              导出 <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%" stripe table-layout="fixed">
          <el-table-column prop="timestamp" :label="TEXT.modules.adminAuditLogs.columns.time" align="center" show-overflow-tooltip>
            <template #default="scope">
              <span class="text-gray-500">{{ displayDateTime(scope.row.timestamp) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="actorName" :label="TEXT.modules.adminAuditLogs.columns.actor" show-overflow-tooltip />
          <el-table-column prop="eventLabel" :label="TEXT.modules.adminAuditLogs.columns.event" show-overflow-tooltip>
            <template #default="scope">
               <el-tag effect="plain">{{ scope.row.eventLabel }}</el-tag>
            </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" show-overflow-tooltip>
            <template #default="scope">
              <span v-if="scope.row.targetTypeLabel">
                <strong>{{ scope.row.targetTypeLabel }}</strong>
                <span class="text-gray-500" v-if="scope.row.targetName">：{{ scope.row.targetName }}</span>
              </span>
              <span v-else>{{ TEXT.common.fallback }}</span>
            </template>
          </el-table-column>
          <el-table-column :label="TEXT.modules.adminAuditLogs.columns.diff" show-overflow-tooltip>
            <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">{{ 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" @click="openDetail(scope.row)">{{ TEXT.common.actions.view }}</el-button>
                </div>
              </div>
              <span v-else class="text-gray-400">{{ TEXT.audit.emptyValue }}</span>
            </template>
          </el-table-column>
          <el-table-column :label="TEXT.modules.adminAuditLogs.columns.result" width="110" align="center">
            <template #default="scope">
              <el-tag :type="scope.row.result === 'SUCCESS' ? 'success' : 'danger'" effect="dark" size="small">
                {{ scope.row.resultLabel }}
              </el-tag>
            </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="560px"
      :close-on-click-modal="false"
      :show-close="false"
      class="audit-detail-drawer"
    >
      <template #header>
        <div class="drawer-header">
          <div class="drawer-title">审计日志详情</div>
          <el-button @click="detailVisible = false">{{ TEXT.common.actions.close }}</el-button>
        </div>
      </template>
      <template v-if="selectedLog">
        <div class="detail-meta-grid">
          <div class="meta-item"><span class="meta-label">时间</span><span>{{ displayDateTime(selectedLog.timestamp) }}</span></div>
          <div class="meta-item"><span class="meta-label">操作人</span><span>{{ selectedLog.actorName || TEXT.common.fallback }}</span></div>
          <div class="meta-item"><span class="meta-label">事件</span><span>{{ selectedLog.eventLabel || TEXT.common.fallback }}</span></div>
          <div class="meta-item"><span class="meta-label">结果</span><span>{{ selectedLog.resultLabel || TEXT.common.fallback }}</span></div>
          <div class="meta-item meta-item-full">
            <span class="meta-label">对象</span>
            <span>{{ selectedLog.targetTypeLabel || TEXT.common.fallback }}<span v-if="selectedLog.targetName">：{{ selectedLog.targetName }}</span></span>
          </div>
          <div class="meta-item meta-item-full"><span class="meta-label">动作</span><span>{{ selectedLog.actionText || TEXT.common.fallback }}</span></div>
        </div>
        <div class="detail-section">
          <div class="detail-section-title">变更明细</div>
          <el-scrollbar max-height="58vh">
            <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>
                <span class="detail-line-text">{{ line }}</span>
              </div>
            </div>
            <div v-else class="text-gray-400">{{ TEXT.audit.emptyValue }}</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 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" : "/workbench");
  }
};

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>
.page {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.main-content-flat {
  width: 100%;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

.main-content-flat :deep(.el-card__body) {
  padding: 0;
}

.table-section {
  padding: 0;
}

.audit-toolbar {
  padding: 10px 16px;
  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: #3b6cb5 !important;
  border-color: #b3cce6 !important;
  background: #f0f6ff !important;
}

.audit-export-btn:hover,
.audit-export-btn:focus {
  color: #2a5699 !important;
  border-color: #8cb4de !important;
  background: #e2edfa !important;
}

.diff-container {
  font-size: 13px;
  color: #4b5563;
  line-height: 1.5;
}

.diff-line {
  margin-bottom: 2px;
  word-break: break-all;
}

.diff-more-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.diff-more-text {
  font-size: 12px;
  color: #94a3b8;
}

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

.audit-table :deep(.el-table__inner-wrapper::before) {
  display: none;
}

.drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.drawer-title {
  font-size: 20px;
  font-weight: 700;
  color: #11264d;
}

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

.meta-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px;
  border: 1px solid #e6edf7;
  border-radius: 8px;
  background: #fbfdff;
  font-size: 13px;
  color: #334155;
}

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

.meta-label {
  font-size: 12px;
  color: #64748b;
}

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

.detail-section-title {
  font-size: 14px;
  font-weight: 700;
  color: #1e3a5f;
  margin-bottom: 8px;
}

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

.detail-line-row {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 13px;
  line-height: 1.5;
}

.detail-line-index {
  color: #94a3b8;
  min-width: 20px;
}

.detail-line-text {
  color: #475569;
  word-break: break-word;
}
</style>
