// src/services/eventBus.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { EventBus } from './eventBus';

describe('EventBus', () => {
  let eventBus: EventBus;

  beforeEach(() => {
    // Create a new instance for each test to ensure isolation
    eventBus = new EventBus();
  });

  it('should call a listener when an event is dispatched', () => {
    const callback = vi.fn();
    eventBus.on('test-event', callback);
    eventBus.dispatch('test-event');
    expect(callback).toHaveBeenCalledTimes(1);
  });

  it('should pass data to the listener when dispatched', () => {
    const callback = vi.fn();
    const data = { message: 'hello' };
    eventBus.on('data-event', callback);
    eventBus.dispatch('data-event', data);
    expect(callback).toHaveBeenCalledWith(data);
  });

  it('should call multiple listeners for the same event', () => {
    const callback1 = vi.fn();
    const callback2 = vi.fn();
    eventBus.on('multi-event', callback1);
    eventBus.on('multi-event', callback2);
    eventBus.dispatch('multi-event');
    expect(callback1).toHaveBeenCalledTimes(1);
    expect(callback2).toHaveBeenCalledTimes(1);
  });

  it('should stop calling a listener after it has been removed', () => {
    const callback = vi.fn();
    eventBus.on('remove-event', callback);
    eventBus.dispatch('remove-event');
    expect(callback).toHaveBeenCalledTimes(1);

    eventBus.off('remove-event', callback);
    eventBus.dispatch('remove-event');
    // The callback should still have been called only once from the first dispatch
    expect(callback).toHaveBeenCalledTimes(1);
  });

  it('should not throw an error when dispatching an event with no listeners', () => {
    expect(() => eventBus.dispatch('no-listener-event')).not.toThrow();
  });

  it('should not throw an error when removing a listener that does not exist for an event', () => {
    const existentCallback = vi.fn();
    const nonExistentCallback = () => {};
    eventBus.on('some-event', existentCallback);
    expect(() => eventBus.off('some-event', nonExistentCallback)).not.toThrow();
  });

  it('should not throw an error when removing a listener from an event that has no listeners', () => {
    const callback = vi.fn();
    expect(() => eventBus.off('non-existent-event', callback)).not.toThrow();
  });

  it('should handle removing one of multiple listeners correctly', () => {
    const callback1 = vi.fn();
    const callback2 = vi.fn();
    eventBus.on('multi-remove-event', callback1);
    eventBus.on('multi-remove-event', callback2);

    eventBus.dispatch('multi-remove-event');
    expect(callback1).toHaveBeenCalledTimes(1);
    expect(callback2).toHaveBeenCalledTimes(1);

    eventBus.off('multi-remove-event', callback1);
    eventBus.dispatch('multi-remove-event');

    // callback1 should not be called again
    expect(callback1).toHaveBeenCalledTimes(1);
    // callback2 should be called again
    expect(callback2).toHaveBeenCalledTimes(2);
  });
});