/**
 * Audit write path — A-8. The `AuditEntry` table existed but had no writer;
 * callers (trigger path, permission denials, slash commands) had no way to
 * record what happened. This module is that single write seam.
 *
 * ADR reference: spec Audit. The skeleton pins only the `runId` relation;
 * event type / actor / timestamp / details are content-OPEN. This helper
 * exposes `action` (event type) + `actorUserId` (actor) + `metadata`
 * (details) + `projectId` (locates audit points that don't have a run, e.g.
 * permission denials) + `runId` (loose string ref, not a Prisma relation —
 * see schema). `createdAt` is `@default(now())` so we don't pass it.
 *
 * Best-effort by design: an audit failure must never break the caller's path
 * (a trigger failing because the audit log is down would be a spec breach —
 * audit is observability, not control). Errors are swallowed here. There's no
 * logger in this layer yet; a future logger can be threaded in without
 * changing the signature.
 */
import type { PrismaClient, Prisma } from "@prisma/client";

export interface AuditInput {
  readonly runId?: string | undefined;
  readonly projectId?: string | undefined;
  readonly actorUserId?: string | undefined;
  readonly action: string;
  readonly metadata: Record<string, unknown>;
}

/**
 * Write one audit entry. Best-effort: any Prisma error is swallowed so the
 * caller's path (trigger, permission gate, slash command) is unaffected.
 *
 * Optional fields are only included when defined — under
 * `exactOptionalPropertyTypes`, assigning `undefined` to an optional key in a
 * Prisma `create` payload is a type error (and would also write an explicit
 * NULL rather than omit the column). Conditional assignment keeps the payload
 * honest. The data object is typed as `AuditEntryUncheckedCreateInput` (the
 * direct-FK variant) so it matches one branch of Prisma's `create` input
 * union exactly — the relation-connect branch has no `projectId`/`runId`
 * columns and would otherwise reject these fields.
 */
export async function writeAudit(
  prisma: PrismaClient,
  entry: AuditInput,
): Promise<void> {
  const data: Prisma.AuditEntryUncheckedCreateInput = {
    action: entry.action,
    metadata: entry.metadata as Prisma.InputJsonValue,
  };
  if (entry.runId !== undefined) data.runId = entry.runId;
  if (entry.projectId !== undefined) data.projectId = entry.projectId;
  if (entry.actorUserId !== undefined) data.actorUserId = entry.actorUserId;

  try {
    await prisma.auditEntry.create({ data });
  } catch {
    // Swallow: audit is best-effort and must not break the caller's path.
  }
}
