import { describe, it, expect, vi } from "vitest";
import { createAuditErasureHook } from "./audit-erasure-hook";
import type { IAuditLog } from "../audit-log.interface";

function makeAuditLog(): IAuditLog {
  return {
    record: vi.fn().mockResolvedValue(undefined),
    eraseSubject: vi.fn().mockResolvedValue(undefined),
  };
}

/** Minimal CollectionAfterDeleteHook args shape (only `doc` matters here). */
function hookArgs(id: unknown) {
  return {
    doc: { id },
    req: {} as never,
    id: String(id),
    collection: {} as never,
    context: {},
  };
}

describe("createAuditErasureHook", () => {
  it("defaults to 'pseudonymize' mode", async () => {
    const auditLog = makeAuditLog();
    const hook = createAuditErasureHook({ auditLog });
    await hook(hookArgs("user_1") as never);
    expect(auditLog.eraseSubject).toHaveBeenCalledWith("user_1", "pseudonymize");
  });

  it("respects explicit mode='delete'", async () => {
    const auditLog = makeAuditLog();
    const hook = createAuditErasureHook({ auditLog, mode: "delete" });
    await hook(hookArgs("user_2") as never);
    expect(auditLog.eraseSubject).toHaveBeenCalledWith("user_2", "delete");
  });

  it("coerces numeric id to string", async () => {
    const auditLog = makeAuditLog();
    const hook = createAuditErasureHook({ auditLog });
    await hook(hookArgs(42) as never);
    expect(auditLog.eraseSubject).toHaveBeenCalledWith("42", "pseudonymize");
  });

  it("skips when doc.id is undefined", async () => {
    const auditLog = makeAuditLog();
    const hook = createAuditErasureHook({ auditLog });
    await hook(hookArgs(undefined) as never);
    expect(auditLog.eraseSubject).not.toHaveBeenCalled();
  });

  it("skips when doc.id is null", async () => {
    const auditLog = makeAuditLog();
    const hook = createAuditErasureHook({ auditLog });
    await hook(hookArgs(null) as never);
    expect(auditLog.eraseSubject).not.toHaveBeenCalled();
  });

  it("skips when doc.id is an object", async () => {
    const auditLog = makeAuditLog();
    const hook = createAuditErasureHook({ auditLog });
    await hook(hookArgs({ nested: true }) as never);
    expect(auditLog.eraseSubject).not.toHaveBeenCalled();
  });
});
