import type { Disposable } from "../types/Common";
import type {
  IEventEmitter,
  ITypedEventEmitter,
} from "../interfaces/IEventEmitter";

/**
 * Simple event emitter implementation that file systems can use
 * Implementations are free to use their own event system
 */
export class EventEmitter implements IEventEmitter {
  private events = new Map<string | symbol, Set<Function>>();

  on(event: string | symbol, handler: (...args: any[]) => void): Disposable {
    if (!this.events.has(event)) {
      this.events.set(event, new Set());
    }

    this.events.get(event)!.add(handler);

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

  once(event: string | symbol, handler: (...args: any[]) => void): Disposable {
    const wrappedHandler = (...args: any[]) => {
      handler(...args);
      this.off(event, wrappedHandler);
    };

    return this.on(event, wrappedHandler);
  }

  emit(event: string | symbol, ...args: any[]): boolean {
    const handlers = this.events.get(event);
    if (!handlers || handlers.size === 0) {
      return false;
    }

    handlers.forEach((handler) => {
      try {
        handler(...args);
      } catch (error) {
        console.error(`Error in event handler for ${String(event)}:`, error);
      }
    });

    return true;
  }

  off(event: string | symbol, handler?: (...args: any[]) => void): void {
    if (!handler) {
      this.events.delete(event);
      return;
    }

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

  removeAllListeners(event?: string | symbol): void {
    if (event) {
      this.events.delete(event);
    } else {
      this.events.clear();
    }
  }
}

/**
 * Typed event emitter implementation
 */
export class TypedEventEmitter<T extends Record<string, any>>
  implements ITypedEventEmitter<T>
{
  private emitter = new EventEmitter();

  on<K extends keyof T>(
    event: K,
    handler: (payload: T[K]) => void,
  ): Disposable {
    return this.emitter.on(event as string, handler);
  }

  once<K extends keyof T>(
    event: K,
    handler: (payload: T[K]) => void,
  ): Disposable {
    return this.emitter.once(event as string, handler);
  }

  emit<K extends keyof T>(event: K, payload: T[K]): boolean {
    return this.emitter.emit(event as string, payload);
  }

  off<K extends keyof T>(event: K, handler?: (payload: T[K]) => void): void {
    this.emitter.off(event as string, handler);
  }

  removeAllListeners<K extends keyof T>(event?: K): void {
    this.emitter.removeAllListeners(event as string);
  }
}
