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

/**
 * Fan-out wrapper. Delivers each entry to every inner sink with settle-all
 * semantics — one failing sink doesn't drop the audit entry from others.
 *
 * Failures emit a structured `audit-sink-error` JSON line to stderr.
 * Stderr (not via OTel/Sentry) avoids recursion: if Sentry is one of the
 * sinks failing and we routed the error back through Sentry's reporter,
 * we'd loop. Stderr is consumed by the same log shipper as audit entries
 * themselves, so the operator sees the failure in their aggregator.
 */
export class MultiSinkAuditLog implements IAuditLog {
  constructor(private readonly sinks: IAuditLog[]) {}

  async record(entry: AuditEntry): Promise<void> {
    const results = await Promise.allSettled(this.sinks.map((s) => s.record(entry)));
    for (const r of results) {
      if (r.status === "rejected") {
        this.reportSinkError(r.reason);
      }
    }
  }

  async eraseSubject(actorId: string, mode: "pseudonymize" | "delete"): Promise<void> {
    const results = await Promise.allSettled(
      this.sinks.map((s) => s.eraseSubject(actorId, mode)),
    );
    for (const r of results) {
      if (r.status === "rejected") {
        this.reportSinkError(r.reason);
      }
    }
  }

  private reportSinkError(reason: unknown): void {
    const line = JSON.stringify({
      _type: "audit-sink-error",
      error: String(reason),
      at: new Date().toISOString(),
    });
    process.stderr.write(line + "\n");
  }
}
