import { describe, expect, it, vi } from 'vitest';
import { EventBus } from '../EventBus';

describe('EventBus', () => {
    it('emit доставляет payload подписчикам темы', () => {
        const bus = new EventBus();
        const got: number[] = [];
        bus.on<number>('hit', (n) => got.push(n));
        bus.emit('hit', 5);
        bus.emit('hit', 7);
        expect(got).toEqual([5, 7]);
    });

    it('чужая тема не получает событий; emit без подписчиков — no-op', () => {
        const bus = new EventBus();
        const got: string[] = [];
        bus.on<string>('a', (s) => got.push(s));
        expect(() => bus.emit('b', 'x')).not.toThrow();
        bus.emit('a', 'y');
        expect(got).toEqual(['y']);
    });

    it('on возвращает off-функцию: отписка снимает только этот хендлер', () => {
        const bus = new EventBus();
        const a = vi.fn();
        const b = vi.fn();
        const offA = bus.on('t', a);
        bus.on('t', b);

        offA();
        bus.emit('t');
        expect(a).not.toHaveBeenCalled();
        expect(b).toHaveBeenCalledTimes(1);
    });

    it('повторная off-отписка не роняет (idempotent)', () => {
        const bus = new EventBus();
        const fn = vi.fn();
        const off = bus.on('t', fn);
        off();
        expect(() => off()).not.toThrow();
    });

    it('emit во время обработки: подписка внутри хендлера не роняет обход (снапшот Set)', () => {
        const bus = new EventBus();
        const late = vi.fn();
        let added = false;
        bus.on('t', () => {
            if (!added) {
                added = true;
                bus.on('t', late);
            }
        });

        bus.emit('t'); // снапшот на обходе: late на этом emit ещё не зовётся
        expect(late).not.toHaveBeenCalled();
        bus.emit('t');
        expect(late).toHaveBeenCalledTimes(1);
    });

    it('clear снимает все подписки', () => {
        const bus = new EventBus();
        const fn = vi.fn();
        bus.on('t', fn);
        bus.clear();
        bus.emit('t');
        expect(fn).not.toHaveBeenCalled();
    });

    it('один хендлер, подписанный дважды, зовётся один раз (Set)', () => {
        const bus = new EventBus();
        const fn = vi.fn();
        bus.on('t', fn);
        bus.on('t', fn);
        bus.emit('t');
        expect(fn).toHaveBeenCalledTimes(1);
    });
});