import type { AuditLogPort } from "../ports/index.js";
import { getActiveRequestContext } from "./request-context.js";

/**
 * Wrap an audit log port so missing `actor`, `tenant`, `requestId`, and
 * `traceId` fields are filled from the ambient request context at record time.
 *
 * The server keeps the ambient context current for requests (including
 * identity elevated by hooks) and for service contexts created for jobs,
 * listeners, schedules, and tasks. Because enrichment happens when
 * `record(...)` runs, the wrapper also works for ports rebuilt per
 * transaction inside a unit of work — wrap both the top-level audit port and
 * the per-transaction rebuild.
 *
 * Fields provided on the entry always win; only missing fields are filled.
 * When no ambient context is active (for example on runtimes without
 * `AsyncLocalStorage` propagation), entries pass through unchanged.
 *
 * @example
 * ```ts
 * const audit = createAmbientAuditLog(
 *   createInstrumentedAuditLog({
 *     audit: createDrizzleSqliteAuditLogPort(db),
 *     instrumentation: ports,
 *   }),
 * );
 * await audit.record({ action: "posts.publish", resource: { type: "post", id } });
 * ```
 *
 * @param audit - Underlying audit log port to write enriched entries to.
 * @returns An audit log port that fills missing context fields before writing.
 */
export function createAmbientAuditLog(audit: AuditLogPort): AuditLogPort {
  return {
    record(entry) {
      const context = getActiveRequestContext();
      if (!context) return audit.record(entry);

      return audit.record({
        ...entry,
        actor: entry.actor ?? context.actor,
        tenant: entry.tenant ?? context.tenant,
        requestId: entry.requestId ?? context.requestId,
        traceId: entry.traceId ?? context.traceId,
      });
    },
  };
}
