/**
 * 文件库审计 sink(契约 C3 的入驻适配)。
 *
 * 契约原文:本地 outbox 表(与业务同事务)→ 中继 POST 到独立审计服务。
 * 入驻 hub 后的适配:审计同事 = 本库 AuditEntry,与业务写在同一 Prisma 事务
 * 内落库 —— 同库同事务天然满足"操作成功则日志必存在",比 outbox+relay 更强。
 * 若审计团队日后独立成服务,只换本文件的实现,action 词汇表保持不变。
 */

import type { Prisma } from "@prisma/client";

/** C3 §6.3:文件库审计动作词汇表(与契约文档逐条对应,改词需升契约版本)。 */
export const FILE_LIB_AUDIT_ACTIONS = {
  folderCreate: "folder.create",
  folderRename: "folder.rename",
  folderMove: "folder.move",
  folderDelete: "folder.delete",
  projectCreate: "project.create",
  projectRename: "project.rename",
  projectMove: "project.move",
  projectDelete: "project.delete",
  permissionGrant: "permission.grant",
  permissionUpdate: "permission.update",
  permissionRevoke: "permission.revoke",
  independentEnable: "project.independent_permission.enable",
  independentDisable: "project.independent_permission.disable",
  independentChange: "project.independent_permission.change",
  fileUpload: "file.upload",
  fileRename: "file.rename",
  fileDelete: "file.delete",
  fileCommit: "file.commit",
  fileConflictDetected: "file.conflict_detected",
  exportRun: "export.run",
  adminForceAdjust: "admin.force_adjust",
} as const;

export type FileLibAuditObjectType = "folder" | "project" | "file" | "grant" | "export_job";

export interface FileLibAuditEntry {
  readonly action: string;
  readonly actorUserId: string;
  readonly organizationId: string;
  readonly objectType: FileLibAuditObjectType;
  readonly objectId: string;
  /** 节点 id 路径(pathIds)或项目内文件路径,便于按路径检索。 */
  readonly objectPath: string;
  readonly detail?: Record<string, unknown> | undefined;
}

/**
 * 在调用方的事务里写一条审计。刻意不吞错:写不出来整个业务操作回滚
 * (需求 5.1"操作成功则日志必存在"的强保证)。
 */
export async function writeFileLibAudit(
  tx: Prisma.TransactionClient,
  entry: FileLibAuditEntry,
): Promise<void> {
  const metadata: Record<string, unknown> = {
    objectType: entry.objectType,
    objectId: entry.objectId,
    objectPath: entry.objectPath,
    ...(entry.detail ?? {}),
  };
  await tx.auditEntry.create({
    data: {
      action: entry.action,
      actorUserId: entry.actorUserId,
      organizationId: entry.organizationId,
      metadata: metadata as Prisma.InputJsonValue,
    },
  });
}
