import { trace, Tracer } from '@opentelemetry/api';
import { vi, describe, it, expect, beforeEach, type MockInstance } from 'vitest';

import { TracedEventEmitter } from './tracedEventEmitter.js';

type EventMap = {
  test_event: [path: string, content: string, bool: boolean];
};

class TestEmitter extends TracedEventEmitter<EventMap> {}

describe('TracedEventEmitter', () => {
  let counter: number;
  let listenerFn: (path: string, content: string, bool: boolean) => void;
  let startActiveSpanSpy: MockInstance;
  let tracer: Tracer;
  let calledWith: [path: string, content: string, bool: boolean][];

  beforeEach(async () => {
    counter = 0;
    calledWith = [];
    // using the real tracer to ensure that things work in reality
    tracer = trace.getTracer('superblocks-test', 'v0');
    startActiveSpanSpy = vi.spyOn(tracer, 'startActiveSpan');

    listenerFn = function (a, b, c) {
      calledWith.push([a, b, c]);
      counter++;
    };
  });

  it('generates spans when an event is emitted', async () => {
    const testEmitter = new TestEmitter(tracer);

    testEmitter.on('test_event', listenerFn);
    // anonymous function to show that we handle it
    testEmitter.on('test_event', () => {
      counter++;
    });

    testEmitter.emit('test_event', 'a', 'b', true);

    expect(counter).toEqual(2);
    expect(calledWith).toEqual([['a', 'b', true]]);

    const spanNames = startActiveSpanSpy.mock.calls.map(([name]) => {
      return name;
    });
    expect(spanNames).toEqual([
      'TestEmitter.emit',
      'TestEmitter.emit listener on test_event: listenerFn',
      'TestEmitter.emit listener on test_event: anonymous'
    ]);
  });
});
