import type { IAuditLog } from "./audit-log.interface";
import { attachBrand } from "@repo/core-shared/conformance";

/**
 * Phantom-type brand attached at wrap time by `withAudit`. The conformance
 * system uses this as the type-level seam for mutating use cases that
 * declare `audits: [...]` in their manifest — without `__audited`, the
 * binding is not assignable to `ProductionUseCase<I, O, M>` when M demands
 * it. At runtime the brand is a non-enumerable property attached by
 * `attachBrand` from `@repo/core-shared/conformance`, so the boot-time
 * assertion can verify the binding went through the audit-aware path.
 */
export type Audited<F> = F & { readonly __audited: true };

/**
 * Use-case wrapper applied at DI bind time. The wrapper is a thin closure
 * that forwards to `fn` unchanged and carries the `__audited` brand. The
 * forward closure (instead of returning `fn` directly) keeps the brand on
 * a fresh function so the caller's original `fn` is not mutated — important
 * when the same factory output is used elsewhere unwrapped (dev-seed paths,
 * tests).
 */
export function withAudit<Args extends unknown[], R>(
  // TODO(conformance milestone iii+): wire automated recording from manifest
  // `audits[]` declarations. For now, the wrapper exists to:
  //   (1) require callers to pass the auditLog at bind time (dep is available)
  //   (2) attach the `__audited` brand so the boot-time assertion can verify
  //       mutating use cases were bound through the audit-aware path.
  auditLog: IAuditLog,
  fn: (...args: Args) => Promise<R>,
): Audited<(...args: Args) => Promise<R>> {
  void auditLog;
  const wrapped: (...args: Args) => Promise<R> = (...args) => fn(...args);
  attachBrand(wrapped, "__audited");
  return wrapped as Audited<(...args: Args) => Promise<R>>;
}
