export class TinyEventEmitter<Events extends { [key: string]: any[] }> {
  #listeners = new Map<keyof Events, Set<(...args: any) => void>>()

  #getListeners(event: keyof Events) {
    let listeners = this.#listeners.get(event)
    if (!listeners) {
      listeners = new Set()
      this.#listeners.set(event, listeners)
    }
    return listeners
  }

  on<E extends keyof Events>(
    event: E,
    listener: (...args: Events[E]) => void,
  ): Disposable {
    this.#getListeners(event).add(listener)
    return {
      [Symbol.dispose]: () => this.off(event, listener),
    }
  }

  once<E extends keyof Events>(
    event: E,
    listener: (...args: Events[E]) => void,
  ): Disposable {
    const once = (...args: Events[E]) => {
      this.off(event, once)
      listener(...args)
    }
    return this.on(event, once)
  }

  off<E extends keyof Events>(event: E, listener: (...args: Events[E]) => void): this {
    this.#getListeners(event).delete(listener)
    return this
  }

  emit<E extends keyof Events>(event: E, ...args: Events[E]): boolean {
    const listeners = this.#getListeners(event)
    for (const listener of listeners) {
      listener(...args)
    }
    return listeners.size > 0
  }
}

export { TinyEventEmitter as EventEmitter }
