/**
 * Event handler function type
 */
export type EventHandler<T = any> = (data: T) => void | Promise<void>;

/**
 * Event subscription
 */
export interface EventSubscription {
  unsubscribe(): void;
}

/**
 * Event bus interface
 */
export interface IEventBus {
  /**
   * Emit an event
   */
  emit<T>(event: string, data: T): Promise<void>;
  
  /**
   * Subscribe to an event
   */
  on<T>(event: string, handler: EventHandler<T>): EventSubscription;
  
  /**
   * Subscribe to an event once
   */
  once<T>(event: string, handler: EventHandler<T>): EventSubscription;
  
  /**
   * Unsubscribe from an event
   */
  off(event: string, handler: EventHandler): void;
  
  /**
   * Remove all listeners for an event
   */
  removeAllListeners(event?: string): void;
  
  /**
   * Get listener count for an event
   */
  listenerCount(event: string): number;
}

/**
 * Event bus implementation
 */
export class EventBus implements IEventBus {
  private listeners = new Map<string, Set<EventHandler>>();
  private onceListeners = new Map<string, Set<EventHandler>>();

  /**
   * Emit an event to all subscribers
   */
  async emit<T>(event: string, data: T): Promise<void> {
    const handlers = this.listeners.get(event);
    const onceHandlers = this.onceListeners.get(event);

    // Execute regular handlers
    if (handlers) {
      const promises: Promise<void>[] = [];
      const handlerArray = Array.from(handlers);
      for (const handler of handlerArray) {
        try {
          const result = handler(data);
          if (result instanceof Promise) {
            promises.push(result);
          }
        } catch (error) {
          console.error(`Error in event handler for ${event}:`, error);
        }
      }
      await Promise.all(promises);
    }

    // Execute once handlers and remove them
    if (onceHandlers) {
      const promises: Promise<void>[] = [];
      const onceHandlerArray = Array.from(onceHandlers);
      for (const handler of onceHandlerArray) {
        try {
          const result = handler(data);
          if (result instanceof Promise) {
            promises.push(result);
          }
        } catch (error) {
          console.error(`Error in once event handler for ${event}:`, error);
        }
      }
      await Promise.all(promises);
      this.onceListeners.delete(event);
    }
  }

  /**
   * Subscribe to an event
   */
  on<T>(event: string, handler: EventHandler<T>): EventSubscription {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    
    this.listeners.get(event)!.add(handler);

    return {
      unsubscribe: () => this.off(event, handler)
    };
  }

  /**
   * Subscribe to an event once
   */
  once<T>(event: string, handler: EventHandler<T>): EventSubscription {
    if (!this.onceListeners.has(event)) {
      this.onceListeners.set(event, new Set());
    }
    
    this.onceListeners.get(event)!.add(handler);

    return {
      unsubscribe: () => {
        const handlers = this.onceListeners.get(event);
        if (handlers) {
          handlers.delete(handler);
          if (handlers.size === 0) {
            this.onceListeners.delete(event);
          }
        }
      }
    };
  }

  /**
   * Unsubscribe from an event
   */
  off(event: string, handler: EventHandler): void {
    const handlers = this.listeners.get(event);
    if (handlers) {
      handlers.delete(handler);
      if (handlers.size === 0) {
        this.listeners.delete(event);
      }
    }

    const onceHandlers = this.onceListeners.get(event);
    if (onceHandlers) {
      onceHandlers.delete(handler);
      if (onceHandlers.size === 0) {
        this.onceListeners.delete(event);
      }
    }
  }

  /**
   * Remove all listeners for an event or all events
   */
  removeAllListeners(event?: string): void {
    if (event) {
      this.listeners.delete(event);
      this.onceListeners.delete(event);
    } else {
      this.listeners.clear();
      this.onceListeners.clear();
    }
  }

  /**
   * Get listener count for an event
   */
  listenerCount(event: string): number {
    const regularCount = this.listeners.get(event)?.size ?? 0;
    const onceCount = this.onceListeners.get(event)?.size ?? 0;
    return regularCount + onceCount;
  }

  /**
   * Get all event names
   */
  eventNames(): string[] {
    const events = new Set<string>();
    const listenerKeys = Array.from(this.listeners.keys());
    for (const event of listenerKeys) {
      events.add(event);
    }
    const onceListenerKeys = Array.from(this.onceListeners.keys());
    for (const event of onceListenerKeys) {
      events.add(event);
    }
    return Array.from(events);
  }
}

// Global event bus instance
export const eventBus = new EventBus();