//@ts-check

/**
 * @template T
 * @typedef {(args:T) => void} Listener<T>
 */
/**
 * @typedef {{signal?: AbortSignal, once?: boolean}} ListenerOptions
 */

/**
 * Returns an event emitter for a specific signal
 * The initial passed value is optional, discarded, and only used to provide
 * automatic typing where applicable.
 * @template T
 * @param {T} [_initial]
 * @returns
 */
export const makeSignal = (_initial) => {
  /** @type {Set<Listener<T>>} */
  const listeners = new Set();
  let enabled = true
  /**
   *
   * @param {Listener<T>} fn
   * @param {ListenerOptions} [options]
   */
  const connect = (fn, { once, signal } = {}) => {
    if (once) {
      const _bound = fn;
      fn = (args) => {
        listeners.delete(fn);
        _bound(args);
      };
    }
    listeners.add(fn);
    const _disconnect = () => disconnect(fn);
    signal && signal.addEventListener("abort", _disconnect);
    return _disconnect;
  };
  /**
   * @param {Listener<T>} fn
   * @returns
   */
  const disconnect = (fn) => listeners.delete(fn);

  /**
   * @param {T} [args]
   * @returns
   */
  // @ts-ignore
  const emit = (args) => enabled && listeners.forEach((fn) => fn(args));

  const disable = () => {enabled = false}

  return { connect, disconnect, emit, disable };
};

export default makeSignal