import { EventEmitter } from 'node:events';
import type { SSEEvent, SSEEventType } from '@zyphon/shared-types';

export class EventBus extends EventEmitter {
  private buffer: SSEEvent[] = [];
  private readonly maxBufferSize: number;
  private eventCounter = 0;
  private heartbeatTimer: NodeJS.Timeout | null = null;

  constructor(maxBufferSize = 1000) {
    super();
    this.maxBufferSize = maxBufferSize;

    // Start 15s heartbeat
    this.heartbeatTimer = setInterval(() => {
      this.publish('heartbeat', { uptime: process.uptime() });
    }, 15000);
    if (this.heartbeatTimer.unref) {
      this.heartbeatTimer.unref();
    }
  }

  public publish<T = unknown>(eventType: SSEEventType, data: T): SSEEvent<T> {
    this.eventCounter++;
    const event: SSEEvent<T> = {
      id: `evt_${Date.now()}_${this.eventCounter}`,
      event: eventType,
      data,
      timestamp: Date.now(),
    };

    this.buffer.push(event as SSEEvent);
    if (this.buffer.length > this.maxBufferSize) {
      this.buffer.shift();
    }

    this.emit('event', event);
    this.emit(eventType, event);
    return event;
  }

  public getEventsSince(lastEventId: string): SSEEvent[] {
    const idx = this.buffer.findIndex((e) => e.id === lastEventId);
    if (idx === -1) {
      // Event not in buffer anymore or unknown id, return last 50 events
      return this.buffer.slice(-50);
    }
    return this.buffer.slice(idx + 1);
  }

  public getRecentEvents(limit = 100): SSEEvent[] {
    return this.buffer.slice(-limit);
  }

  public stop(): void {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
    this.removeAllListeners();
  }
}

