interface IEventBusStore {
  listeners: Map<string, Set<(payload?: unknown) => void>>
  channel: BroadcastChannel | null
}

interface IEventBusWindow extends Window {
  __SEA_TIME_EVENT_BUS__?: IEventBusStore
}

export type EventBusHandler<T = unknown> = (payload?: T) => void

interface IEventBusMessage<T = unknown> {
  event: string
  payload?: T
}

/**
 * 全局事件总线扩展
 * 基于 BroadcastChannel 实现跨页面通信
 */
export class EventBusExtend {
  private static readonly CHANNEL_NAME = 'sea-time-event-bus'

  private static isClient(): boolean {
    return typeof window !== 'undefined'
  }

  private static supportsBroadcastChannel(): boolean {
    return typeof BroadcastChannel !== 'undefined'
  }

  private static createStore(): IEventBusStore {
    return {
      listeners: new Map(),
      channel: this.createChannel()
    }
  }

  private static getStore(): IEventBusStore | null {
    if (!this.isClient()) {
      return null
    }

    const target = window as IEventBusWindow
    target.__SEA_TIME_EVENT_BUS__ ||= this.createStore()

    return target.__SEA_TIME_EVENT_BUS__ || null
  }

  private static createChannel(): BroadcastChannel | null {
    if (!this.supportsBroadcastChannel()) {
      return null
    }

    const channel = new BroadcastChannel(this.CHANNEL_NAME)
    channel.onmessage = (messageEvent: MessageEvent<IEventBusMessage>) => {
      const data = messageEvent.data
      if (!data?.event) {
        return
      }

      this.dispatch(data.event, data.payload)
    }

    return channel
  }

  private static getListeners(event: string): Set<(payload?: unknown) => void> | null {
    const store = this.getStore()
    if (!store) {
      return null
    }

    if (!store.listeners.has(event)) {
      store.listeners.set(event, new Set())
    }

    return store.listeners.get(event) || null
  }

  private static removeHandler(event: string, handler: (payload?: unknown) => void): void {
    const listeners = this.getListeners(event)
    if (!listeners) {
      return
    }

    listeners.delete(handler)

    if (listeners.size === 0) {
      this.getStore()?.listeners.delete(event)
    }
  }

  /**
   * 订阅事件
   */
  static on<T = unknown>(
    event: string,
    handler: EventBusHandler<T>
  ): () => void {
    const listeners = this.getListeners(event)
    if (!listeners) {
      return () => undefined
    }

    const wrappedHandler = handler as (payload?: unknown) => void
    listeners.add(wrappedHandler)

    return () => {
      this.removeHandler(event, wrappedHandler)
    }
  }

  /**
   * 取消订阅
   * 不传 handler 时清空该事件下全部监听
   */
  static off<T = unknown>(event: string, handler?: EventBusHandler<T>): void {
    const listeners = this.getListeners(event)
    if (!listeners) {
      return
    }

    if (!handler) {
      listeners.clear()
      return
    }

    this.removeHandler(event, handler as (payload?: unknown) => void)
  }

  private static dispatch<T = unknown>(event: string, payload?: T): void {
    const listeners = this.getListeners(event)
    if (!listeners) {
      return
    }

    Array.from(listeners).forEach((listener) => {
      listener(payload)
    })
  }

  /**
   * 发布事件
   */
  static emit<T = unknown>(
    event: string,
    payload?: T
  ): void {
    const store = this.getStore()
    if (!store) {
      return
    }

    this.dispatch(event, payload)
    store.channel?.postMessage({
      event,
      payload
    } satisfies IEventBusMessage<T>)
  }

  /**
   * 清空监听
   */
  static clear(event?: string): void {
    const store = this.getStore()
    if (!store) {
      return
    }

    if (!event) {
      store.listeners.clear()
      return
    }

    store.listeners.delete(event)
  }
}
