/**
 * Request-scoped context propagated via Node's AsyncLocalStorage.
 *
 * Holds correlation fields (`reqId`, `restaurantId`, `userId`, `route`) that
 * the shared Pino logger automatically merges into every log line for the
 * lifetime of the request. Use `runWithRequestContext` from the request
 * lifecycle middleware (and only there) to seed the context; downstream code
 * just calls `logger.info(...)` and the fields appear automatically.
 */
import { AsyncLocalStorage } from 'async_hooks';

export interface RequestContext {
  reqId: string;
  restaurantId?: string | null;
  userId?: string | null;
  route?: string | null;
}

const storage = new AsyncLocalStorage<RequestContext>();

export function getRequestContext(): RequestContext | undefined {
  return storage.getStore();
}

export function runWithRequestContext<T>(ctx: RequestContext, fn: () => T): T {
  return storage.run(ctx, fn);
}

/**
 * Run `fn` outside any inherited request context. Used by background workers
 * (timers, queue consumers) so that whatever request happened to be active
 * when the timer fired doesn't leak its `reqId`/`userId`/`restaurantId`/`route`
 * into the worker's log lines.
 */
export function runWithoutRequestContext<T>(fn: () => T): T {
  return storage.run(undefined as unknown as RequestContext, fn);
}

/**
 * Mutate the active request context in-place. Only the known correlation
 * fields on `RequestContext` (`restaurantId`, `userId`, `route`) are patched —
 * everything else is ignored. Used by `withAuth` to inject the resolved
 * `restaurantId`/`userId` after the request was already wrapped by the
 * lifecycle middleware. For per-call extras (e.g. `orderId`), pass them
 * directly into the log call: `log.info({ orderId }, '...')`.
 */
export function updateRequestContext(patch: Partial<RequestContext>): void {
  const ctx = storage.getStore();
  if (!ctx) return;
  if (patch.restaurantId !== undefined) ctx.restaurantId = patch.restaurantId;
  if (patch.userId !== undefined) ctx.userId = patch.userId;
  if (patch.route !== undefined) ctx.route = patch.route;
}
