import {
  resolveTracingPort,
  runWithTracing,
  type TraceOperation,
  type TraceSpan,
  type TracingPort,
} from "./index.js";

type MaybePromise<T> = T | Promise<T>;

/**
 * Resolve a lazy application context inside a span when the runtime tracing
 * port is available independently from that context.
 */
export async function runWithResolvedTracingContext<Ctx, Result>(args: {
  tracing?: TracingPort;
  ctx: Ctx | (() => MaybePromise<Ctx>);
  operation: TraceOperation;
  run(ctx: Ctx, span?: TraceSpan): MaybePromise<Result>;
}): Promise<Result> {
  const resolveContext = async (): Promise<Ctx> =>
    typeof args.ctx === "function"
      ? await (args.ctx as () => MaybePromise<Ctx>)()
      : args.ctx;

  if (args.tracing) {
    return await runWithTracing(args.tracing, args.operation, async (span) =>
      args.run(await resolveContext(), span),
    );
  }

  const ctx = await resolveContext();
  const tracing = resolveTracingPort(ctx);
  return await runWithTracing(tracing, args.operation, (span) =>
    args.run(ctx, span),
  );
}
