import { beforeEach, describe, expect, it, vi } from "vitest";

const prismaMock = vi.hoisted(() => ({
  auditLog: { create: vi.fn() },
  alertLog: { create: vi.fn() },
}));

vi.mock("@/lib/prisma", () => ({ prisma: prismaMock }));

import { recordSecurityEvent, writeAudit } from "@/modules/audit/service";

describe("writeAudit", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    prismaMock.auditLog.create.mockResolvedValue({ id: BigInt(1) });
    prismaMock.alertLog.create.mockResolvedValue({ id: BigInt(1) });
  });

  it("写入 audit_log 成功", async () => {
    await writeAudit("FORBIDDEN_ACCESS", "CUST_001", "QTE_B", {
      owner: "CUST_B",
    });

    expect(prismaMock.auditLog.create).toHaveBeenCalledWith({
      data: expect.objectContaining({
        action: "FORBIDDEN_ACCESS",
        actorId: "CUST_001",
        resource: "QTE_B",
      }),
    });
  });

  it("DB 写失败不抛异常", async () => {
    prismaMock.auditLog.create.mockRejectedValue(new Error("db down"));
    const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});

    await expect(
      writeAudit("TEST", "actor", "resource"),
    ).resolves.toBeUndefined();

    consoleSpy.mockRestore();
  });
});

describe("recordSecurityEvent", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    prismaMock.auditLog.create.mockResolvedValue({ id: BigInt(1) });
    prismaMock.alertLog.create.mockResolvedValue({ id: BigInt(1) });
  });

  it("同时写 audit + SECURITY 预警", async () => {
    await recordSecurityEvent(
      "SECURITY_VALIDATION_BLOCKED",
      "CUST_001",
      "POST /api/quotes",
      { message: "injection" },
    );

    expect(prismaMock.auditLog.create).toHaveBeenCalled();
    expect(prismaMock.alertLog.create).toHaveBeenCalledWith({
      data: expect.objectContaining({ alertType: "SECURITY" }),
    });
  });
});
