import { EventEmitter } from "@dage/events";

declare global {
  /**
   * 声明映射关系
   */
  interface EventMapper {}
}
/**
 * 事件名称
 */
export type EventName = keyof EventMapper;
/**
 * 事件参数
 */
export type EventArgument<T extends EventName> = EventMapper[T];

export class EventBus {
  private emitter = new EventEmitter();
  private bufferMap = new Map<string, any>();

  private getBufferValue<T>(event: string): T | undefined {
    return this.bufferMap.get(event);
  }
  private setBufferValue(event: string, val: any) {
    this.bufferMap.set(event, val);
  }

  /**
   * 监听事件
   */
  on<N extends EventName, A extends EventArgument<N>>(
    event: N,
    callback: (arg: A) => void
  ) {
    const bufferValue = this.getBufferValue<A>(event);

    if (bufferValue) {
      callback(bufferValue);
    }

    this.emitter.on(event, callback);

    return () => {
      this.emitter.off(event, callback);
    };
  }

  /**
   * 监听一次事件
   */
  once<N extends EventName, A extends EventArgument<N>>(
    event: N,
    callback: (arg: A) => void
  ) {
    const bufferValue = this.getBufferValue<A>(event);
    if (bufferValue) {
      callback(bufferValue);
      return () => {};
    }

    this.emitter.once(event, callback);
    return () => {
      this.emitter.off(event, callback);
    };
  }

  /**
   * 触发事件
   * @param event
   * @param arg
   * @param replay - 是否重放该值
   */
  emit<N extends EventName, A extends EventArgument<N>>(
    event: N,
    arg: A,
    replay: boolean = false
  ) {
    if (replay) {
      this.setBufferValue(event, arg);
    }
    this.emitter.emit(event, arg);
  }

  /**
   * 需要最新值的时候 可以调用此方法重置
   */
  resetReplayValue<N extends EventName>(event: N) {
    this.bufferMap.delete(event);
  }

  /**
   * 监听新的订阅者
   */
  onSubscriber<N extends EventName, A extends EventArgument<N>>(
    event: N,
    callback: (listener: (arg: A) => void) => void
  ) {
    const listener = (name: string, subscriber: any) => {
      if (name === event) {
        callback(subscriber);
      }
    };
    this.emitter.on("newListener", listener);

    return () => {
      this.emitter.off("newListener", listener);
    };
  }
}
