/**
 * In-process log broadcaster for SSE streaming.
 *
 * Collects the last MAX_BUFFER lines from pino and pushes new entries
 * to all connected SSE clients in real-time.
 */
const MAX_BUFFER = 500;

export interface LogEntry {
  time: string;
  level: number;
  levelName: string;
  msg: string;
  err?: Record<string, unknown>;
  raw: string;
}

export class LogBroadcaster {
  private buffer: string[] = [];
  private clients = new Set<ReadableStreamDefaultController>();

  /** Called by pino's custom destination stream. */
  addEntry(jsonLine: string): void {
    // Keep last MAX_BUFFER lines (ring buffer behavior)
    if (this.buffer.length >= MAX_BUFFER) {
      this.buffer.shift();
    }
    this.buffer.push(jsonLine);

    // Broadcast to all SSE clients
    const enc = new TextEncoder();
    for (const client of this.clients) {
      try {
        client.enqueue(enc.encode(`data: ${jsonLine}\n\n`));
      } catch {
        // Client disconnected — will be cleaned up by 'close' event
        this.clients.delete(client);
      }
    }
  }

  /** Returns a async generator that first yields buffered history, then new entries. */
  async *stream(): AsyncGenerator<string, void, unknown> {
    // First send history (without the SSE prefix — caller prepends it)
    for (const line of this.buffer) {
      yield line;
    }

    // Then wait for new entries via a local queue
    // (new entries are pushed by addEntry; we yield them directly)
    // Since addEntry broadcasts to all clients, we don't need a separate queue here.
    // Instead, we just let the client receive from this generator's ReadableStream.
    // The initial buffer + new entries are sufficient for SSE.
    // Note: we can't "wait" for new entries in a sync generator.
    // Instead, the SSE handler will use the ReadableStream from this client registration.
    return;
  }

  /**
   * Register a new SSE client. Returns a ReadableStream that yields SSE-formatted lines.
   */
  registerClient(): ReadableStream<Uint8Array> {
    const enc = new TextEncoder();
    const self = this;

    return new ReadableStream<Uint8Array>({
      start(controller: ReadableStreamDefaultController) {
        // Send buffer history
        for (const line of self.buffer) {
          try {
            controller.enqueue(enc.encode(`data: ${line}\n\n`));
          } catch {
            // Controller closed
            break;
          }
        }
        self.clients.add(controller);
      },
      cancel(controller: ReadableStreamDefaultController) {
        self.clients.delete(controller);
      },
    });
  }

  get clientCount(): number {
    return this.clients.size;
  }
}

/** Singleton instance shared across the server lifecycle. */
let instance: LogBroadcaster | undefined;

export function getLogBroadcaster(): LogBroadcaster {
  if (!instance) {
    instance = new LogBroadcaster();
  }
  return instance;
}
