// Temporary compatibility helper.
//
// Node 22 cannot parse `await using` syntax in test files, but we still want to
// exercise the real syntax on newer Node versions.
//
// Remove when we can drop support for Node 22 and use `await using` directly in each test.
/* node:coverage ignore next 10 */
const supportsAsyncUsing = (() => {
  try {
    // Compile the syntax probe dynamically so older Node versions can parse this file.
    // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
    Function('async function test() { await using resource = null; }');
    return true;
  } catch {
    return false;
  }
})();

/* node:coverage ignore next 2 */
const asyncDispose: typeof Symbol.asyncDispose =
  Symbol.asyncDispose ?? Symbol.for('Symbol.asyncDispose');

export async function withAsyncUsing<T>(
  value: T,
  useResource: (resource: T) => void | Promise<void>,
): Promise<void> {
  // On Node 24+, supportsAsyncUsing is true and this branch is unreachable.
  // On Node 22, this is the branch that gets executed.
  /* node:coverage ignore next 26 */
  if (!supportsAsyncUsing) {
    try {
      await useResource(value);
    } finally {
      const dispose = (value as { [key: symbol]: unknown })[asyncDispose];
      if (typeof dispose === 'function') {
        await dispose.call(value);
      }
    }
    return;
  }

  {
    // Keep `await using` in a dynamically compiled function so Node 22 can still
    // parse this file while newer runtimes exercise the real syntax.
    // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
    const createRunWithAsyncUsing = new Function(
      'return async function(value, useResource) { await using resource = value; await useResource(resource); };',
    ) as () => (
      value: T,
      useResource: (resource: T) => void | Promise<void>,
    ) => Promise<void>;

    const runWithAsyncUsing = createRunWithAsyncUsing();
    await runWithAsyncUsing(value, useResource);
  }
}