// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import { delay } from "./delay.ts";

/** Options for {@linkcode deadline}. */
export interface DeadlineOptions {
  /** Signal used to abort the deadline. */
  signal?: AbortSignal;
}

/**
 * Error thrown when {@linkcode deadline} times out.
 *
 * @example Usage
 * ```ts no-assert
 * import { DeadlineError } from "@std/async/deadline";
 *
 * const error = new DeadlineError();
 * ```
 */
export class DeadlineError extends Error {
  constructor() {
    super("Deadline");
    this.name = this.constructor.name;
  }
}

/**
 * Create a promise which will be rejected with {@linkcode DeadlineError} when
 * a given delay is exceeded.
 *
 * Note: Prefer to use {@linkcode AbortSignal.timeout} instead for the APIs
 * that accept {@linkcode AbortSignal}.
 *
 * @typeParam T The type of the provided and returned promise.
 * @param p The promise to make rejectable.
 * @param ms Duration in milliseconds for when the promise should time out.
 * @param options Additional options.
 * @returns A promise that will reject if the provided duration runs out before resolving.
 *
 * @example Usage
 * ```ts no-eval
 * import { deadline } from "@std/async/deadline";
 * import { delay } from "@std/async/delay";
 *
 * const delayedPromise = delay(1000);
 * // Below throws `DeadlineError` after 10 ms
 * const result = await deadline(delayedPromise, 10);
 * ```
 */
export function deadline<T>(
  p: Promise<T>,
  ms: number,
  options: DeadlineOptions = {},
): Promise<T> {
  const controller = new AbortController();
  const { signal } = options;
  if (signal?.aborted) {
    return Promise.reject(new DeadlineError());
  }
  signal?.addEventListener("abort", () => controller.abort(signal.reason));
  const d = delay(ms, { signal: controller.signal })
    .catch(() => {}) // Do NOTHING on abort.
    .then(() => Promise.reject(new DeadlineError()));
  return Promise.race([p.finally(() => controller.abort()), d]);
}

// denoCacheMetadata={"headers":{"nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","accept-ranges":"bytes","set-cookie":"__cf_bm=loHGd94iOqtSYFRRicn_WX56stdWMVP0PvOtallyPgE-1785241874.9407115-1.0.1.1-_9S0gIV6f6DwgWFzh52etl_7FqRFs9fHiFIS8N3JEEu5H5OdVbWFI7EPsjOhobM2T2Z_ZSMfJd.GXKuoe93YK3_1A1w8qRAX8dHWeRlHgdGCtkJyGhQl5XPHR1KfN0_k; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:14 GMT","content-security-policy":"default-src 'none'; script-src 'none'; style-src 'none'; img-src 'none'; font-src 'none'; connect-src 'none'; frame-src 'none'; object-src 'none'; frame-ancestors 'none'; sandbox; form-action 'none';","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=lMYrw%2F7q5uSVptmNvVDY7uP5TynNV5%2B1Oy3B7h6eyP60MNw%2B2ayV29zxdvbfjI3tIiCm3XBi4zW%2Fny5TbOukEWeMe2qWhXAdchBMvykXC%2FTevcqeWzfWPaA%3D\"}]}","content-length":"2023","etag":"\"d8a950d070808f0e0cfe07122ae0b386\"","date":"Tue, 28 Jul 2026 12:31:14 GMT","access-control-expose-headers":"*","access-control-allow-origin":"*","vary":"Origin","cf-cache-status":"HIT","content-type":"text/typescript","x-content-type-options":"nosniff","x-jsr-backend":"modules","cf-ray":"a223e6564eb57db6-LAX","age":"4145","server":"cloudflare","cross-origin-resource-policy":"cross-origin","x-robots-tag":"noindex","alt-svc":"h3=\":443\"; ma=86400","cache-control":"public, max-age=31536000, immutable"},"url":"https://jsr.io/@std/async/0.224.2/deadline.ts","time":1785241874}