/**
 * Traffic capture for the MCP inspector.
 *
 * Records all MCP traffic flowing through mcplocal — both client-facing
 * messages and internal upstream routing. Events are stored in a ring
 * buffer and streamed to SSE subscribers in real-time.
 */

export type TrafficEventType =
  | 'client_request'
  | 'client_response'
  | 'client_notification'
  | 'upstream_request'
  | 'upstream_response'
  | 'session_created'
  | 'session_closed';

export interface TrafficEvent {
  timestamp: string;
  projectName: string;
  sessionId: string;
  eventType: TrafficEventType;
  method?: string | undefined;
  upstreamName?: string | undefined;
  body: unknown;
  durationMs?: number | undefined;
  correlationId?: string | undefined;
}

export interface ActiveSession {
  sessionId: string;
  projectName: string;
  startedAt: string;
}

export interface TrafficFilter {
  project?: string | undefined;
  session?: string | undefined;
}

type Listener = (event: TrafficEvent) => void;

const DEFAULT_MAX_BUFFER = 5000;

export class TrafficCapture {
  private listeners = new Set<Listener>();
  private buffer: TrafficEvent[] = [];
  private readonly maxBuffer: number;
  private activeSessions = new Map<string, ActiveSession>();

  constructor(maxBuffer = DEFAULT_MAX_BUFFER) {
    this.maxBuffer = maxBuffer;
  }

  emit(event: TrafficEvent): void {
    // Track active sessions
    if (event.eventType === 'session_created') {
      this.activeSessions.set(event.sessionId, {
        sessionId: event.sessionId,
        projectName: event.projectName,
        startedAt: event.timestamp,
      });
    } else if (event.eventType === 'session_closed') {
      this.activeSessions.delete(event.sessionId);
    }

    // Ring buffer
    this.buffer.push(event);
    if (this.buffer.length > this.maxBuffer) {
      this.buffer.splice(0, this.buffer.length - this.maxBuffer);
    }

    // Notify subscribers
    for (const listener of this.listeners) {
      try {
        listener(event);
      } catch {
        // Don't let a bad listener break the pipeline
      }
    }
  }

  /** Subscribe to live events. Returns unsubscribe function. */
  subscribe(cb: Listener): () => void {
    this.listeners.add(cb);
    return () => {
      this.listeners.delete(cb);
    };
  }

  /** Get buffered events, optionally filtered. */
  getBuffer(filter?: TrafficFilter): TrafficEvent[] {
    let events = this.buffer;
    if (filter?.project) {
      events = events.filter((e) => e.projectName === filter.project);
    }
    if (filter?.session) {
      events = events.filter((e) => e.sessionId === filter.session);
    }
    return events;
  }

  /** Get all currently active sessions. */
  getActiveSessions(): ActiveSession[] {
    return [...this.activeSessions.values()];
  }

  /** Number of subscribers (for health/debug). */
  get subscriberCount(): number {
    return this.listeners.size;
  }

  /** Total events in buffer. */
  get bufferSize(): number {
    return this.buffer.length;
  }
}
