import { describe, it, expect, vi, beforeEach } from "vitest";
import { StdoutJsonAuditLog } from "./stdout-json-audit-log";
import type { AuditEntry } from "@repo/core-shared/audit";

const sample: AuditEntry = {
  actorId: "user_1",
  actorType: "user",
  actorRoles: ["admin"],
  action: "CREATE",
  resource: { type: "articles", id: "abc" },
  at: new Date("2026-05-11T10:00:00.000Z"),
  scope: { feature: "blog", environment: "production", tenant: "default" },
  from: { ipTruncated: "10.0.0.0", userAgent: "Mozilla/5.0" },
  containsPii: false,
  outcome: "success",
};

describe("StdoutJsonAuditLog", () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let writeSpy: ReturnType<typeof vi.spyOn<any, any>>;
  beforeEach(() => {
    writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
  });

  it("record() writes one JSON line per entry to stdout", async () => {
    const log = new StdoutJsonAuditLog();
    await log.record(sample);
    expect(writeSpy).toHaveBeenCalledOnce();
    const written = writeSpy.mock.calls[0]![0] as string;
    expect(written.endsWith("\n")).toBe(true);
    const parsed = JSON.parse(written.trimEnd());
    expect(parsed._type).toBe("audit");
    expect(parsed.actorId).toBe("user_1");
    expect(parsed.action).toBe("CREATE");
    expect(parsed.at).toBe("2026-05-11T10:00:00.000Z");  // ISO 8601 serialization
  });

  it("eraseSubject() emits a tombstone with mode + actorId", async () => {
    const log = new StdoutJsonAuditLog();
    await log.eraseSubject("user_1", "pseudonymize");
    expect(writeSpy).toHaveBeenCalledOnce();
    const written = writeSpy.mock.calls[0]![0] as string;
    const parsed = JSON.parse(written.trimEnd());
    expect(parsed._type).toBe("audit-erasure");
    expect(parsed.actorId).toBe("user_1");
    expect(parsed.mode).toBe("pseudonymize");
    expect(typeof parsed.at).toBe("string");  // ISO 8601 timestamp
  });
});
