/**
 * @module EventQueue
 * @description Event queue system for task events in the A2A protocol
 * 
 * This module provides an event queue implementation for managing and distributing
 * task-related events in the A2A protocol server. It supports event publishing,
 * subscription, history tracking, and hierarchical event propagation.
 */

import { EventEmitter } from 'node:events';
import type { Task, Artifact } from '@dexwox-labs/a2a-core';

/**
 * Type definition for events handled by the EventEmitter
 * @internal
 */
interface EventQueueEvents {
  event: [TaskEvent];
}

/**
 * Represents a task-related event in the A2A protocol
 * 
 * This type defines the structure of events that flow through the event queue,
 * including the event type, associated task, timestamp, and optional metadata.
 */
type TaskEvent = {
  /** The type of task event */
  type: 'taskCreated' | 'taskUpdated' | 'taskCompleted' | 'taskFailed' | 'taskCanceled' | 'taskInputRequired' | 'artifactAdded' | 'taskStatusUpdate' | 'taskArtifactUpdate';
  /** The task associated with this event */
  task: Task;
  /** Timestamp when the event occurred (milliseconds since epoch) */
  timestamp: number;
  /** Optional previous state of the task (for state transition events) */
  previousState?: string;
  /** Optional artifact associated with the event (for artifact events) */
  artifact?: Artifact;
};

/**
 * Event queue for task-related events
 * 
 * The EventQueue provides a publish-subscribe system for task-related events
 * in the A2A protocol server. It maintains a history of recent events, supports
 * hierarchical event propagation through child queues, and provides methods for
 * publishing and subscribing to events.
 * 
 * @example
 * ```typescript
 * // Create an event queue
 * const eventQueue = new EventQueue();
 * 
 * // Subscribe to events
 * const unsubscribe = eventQueue.subscribe(event => {
 *   console.log(`Event: ${event.type} for task ${event.task.id}`);
 *   
 *   if (event.type === 'taskCompleted') {
 *     console.log('Task completed successfully!');
 *   }
 * });
 * 
 * // Publish an event
 * eventQueue.publish({
 *   type: 'taskCreated',
 *   task: someTask,
 *   timestamp: Date.now()
 * });
 * 
 * // Later, unsubscribe
 * unsubscribe();
 * 
 * // Create a child queue that receives all events from the parent
 * const childQueue = eventQueue.tap();
 * ```
 */
export class EventQueue {
  /** Event emitter for handling event subscriptions */
  private readonly emitter = new EventEmitter<EventQueueEvents>();
  /** History of recent events */
  private readonly history: TaskEvent[] = [];
  /** Maximum number of events to keep in history */
  private readonly maxHistory = 1000;
  /** Child queues that receive events from this queue */
  private readonly children: EventQueue[] = [];
  /** Whether this queue has been shut down */
  private isShutdown = false;

  /**
   * Publishes an event to the queue
   * 
   * This method adds an event to the queue's history, emits it to all subscribers,
   * and propagates it to any child queues. If the queue has been shut down,
   * an error is thrown.
   * 
   * @param event - The event to publish
   * @throws Error if the queue has been shut down
   * 
   * @example
   * ```typescript
   * eventQueue.publish({
   *   type: 'taskUpdated',
   *   task: updatedTask,
   *   timestamp: Date.now(),
   *   previousState: 'submitted'
   * });
   * ```
   */
  publish(event: TaskEvent): void {
    if (this.isShutdown) {
      throw new Error('EventQueue has been shutdown');
    }
    // Add to history and enforce size limit
    this.history.push(event);
    if (this.history.length > this.maxHistory) {
      this.history.shift();
    }
    this.emitter.emit('event', event);
    // Propagate to child queues
    this.children.forEach(child => child.publish(event));
  }

  /**
   * Subscribes to events from the queue
   * 
   * This method registers a callback to be called for each event in the queue.
   * It immediately calls the callback with all historical events, then registers
   * it for future events. It returns a function that can be called to unsubscribe.
   * 
   * @param callback - Function to call for each event
   * @returns Function to call to unsubscribe
   * 
   * @example
   * ```typescript
   * const unsubscribe = eventQueue.subscribe(event => {
   *   if (event.type === 'taskCompleted') {
   *     console.log(`Task ${event.task.id} completed`);
   *   }
   * });
   * 
   * // Later, when no longer interested in events
   * unsubscribe();
   * ```
   */
  subscribe(callback: (event: TaskEvent) => void): () => void {
    // Send historical events first
    this.history.forEach(event => callback(event));
    // Subscribe to new events
    this.emitter.on('event', callback);
    return () => this.emitter.off('event', callback);
  }

  /**
   * Creates a child queue that receives all events from this queue
   * 
   * This method creates a new EventQueue and registers it as a child of this queue.
   * All events published to this queue will also be published to the child queue.
   * This is useful for creating specialized event handlers or for filtering events.
   * 
   * @returns A new EventQueue that receives all events from this queue
   * 
   * @example
   * ```typescript
   * // Create a main event queue
   * const mainQueue = new EventQueue();
   * 
   * // Create a specialized queue for completed tasks
   * const completedTasksQueue = mainQueue.tap();
   * completedTasksQueue.subscribe(event => {
   *   if (event.type === 'taskCompleted') {
   *     // Handle completed tasks
   *   }
   * });
   * ```
   */
  tap(): EventQueue {
    const child = new EventQueue();
    this.children.push(child);
    return child;
  }

  /**
   * Shuts down the event queue
   * 
   * This method marks the queue as shut down, closes all child queues,
   * and removes all event listeners. After calling this method, no more
   * events can be published to the queue.
   * 
   * @example
   * ```typescript
   * // When done with the event queue
   * eventQueue.close();
   * ```
   */
  close(): void {
    this.isShutdown = true;
    this.children.forEach(child => child.close());
    this.emitter.removeAllListeners();
  }

  /**
   * Gets a copy of the event history
   * 
   * This method returns a copy of the queue's event history. The returned array
   * is a new instance, so modifications to it will not affect the queue's history.
   * 
   * @returns A copy of the event history
   * 
   * @example
   * ```typescript
   * const history = eventQueue.getHistory();
   * console.log(`Queue has ${history.length} events in history`);
   * ```
   */
  getHistory(): TaskEvent[] {
    return [...this.history];
  }

  /**
   * Waits for and returns the next event from the queue
   * 
   * This method returns a promise that resolves with the next event published
   * to the queue. If the queue is shut down, it resolves with undefined.
   * This is useful for implementing event-driven workflows.
   * 
   * @returns Promise that resolves with the next event, or undefined if the queue is shut down
   * 
   * @example
   * ```typescript
   * // Process events one at a time
   * async function processEvents() {
   *   while (true) {
   *     const event = await eventQueue.dequeue();
   *     if (!event) {
   *       console.log('Queue has been shut down');
   *       break;
   *     }
   *     console.log(`Processing event: ${event.type}`);
   *   }
   * }
   * ```
   */
  async dequeue(): Promise<TaskEvent | undefined> {
    if (this.isShutdown) {
      return undefined;
    }

    return new Promise(resolve => {
      const unsubscribe = this.subscribe(event => {
        unsubscribe();
        resolve(event);
      });
    });
  }
}
