import { describe, expect, it, vi } from "vitest";
import type { PrismaClient } from "@prisma/client";
import { writeAudit } from "../../src/audit.js";

/// Minimal mock: only the surface `writeAudit` touches. Cast through unknown
/// so we don't have to implement the rest of the PrismaClient surface — this
/// is a unit test, not a DB test.
interface AuditEntryMock {
  create: ReturnType<typeof vi.fn>;
}
function mockPrisma(): PrismaClient & { auditEntry: AuditEntryMock } {
  return { auditEntry: { create: vi.fn() } } as unknown as PrismaClient & {
    auditEntry: AuditEntryMock;
  };
}

describe("writeAudit (A-8 audit write path)", () => {
  it("creates a row with all fields set", async () => {
    const prisma = mockPrisma();
    prisma.auditEntry.create.mockResolvedValue({ id: "aud-1" });

    await writeAudit(prisma, {
      runId: "run-1",
      projectId: "proj-1",
      actorUserId: "user-1",
      action: "run.triggered",
      metadata: { source: "feishu" },
    });

    expect(prisma.auditEntry.create).toHaveBeenCalledTimes(1);
    expect(prisma.auditEntry.create).toHaveBeenCalledWith({
      data: {
        runId: "run-1",
        projectId: "proj-1",
        actorUserId: "user-1",
        action: "run.triggered",
        metadata: { source: "feishu" },
      },
    });
  });

  it("creates a row with only required fields (action + metadata)", async () => {
    const prisma = mockPrisma();
    prisma.auditEntry.create.mockResolvedValue({ id: "aud-2" });

    await writeAudit(prisma, {
      action: "permission.denied",
      metadata: { reason: "no-edit-role" },
    });

    expect(prisma.auditEntry.create).toHaveBeenCalledTimes(1);
    expect(prisma.auditEntry.create).toHaveBeenCalledWith({
      data: {
        action: "permission.denied",
        metadata: { reason: "no-edit-role" },
      },
    });
  });

  it("swallows a prisma error (best-effort, must not throw)", async () => {
    const prisma = mockPrisma();
    prisma.auditEntry.create.mockRejectedValue(new Error("db down"));

    // Must not throw — audit is observability, not control.
    await expect(
      writeAudit(prisma, {
        action: "run.triggered",
        metadata: {},
      }),
    ).resolves.toBeUndefined();
    expect(prisma.auditEntry.create).toHaveBeenCalledTimes(1);
  });
});
