/**
 * Pause Queue — intercepts pipeline output for live debugging.
 *
 * When paused, content that has been through the pipeline is held in a queue
 * instead of being returned to the client. The operator can:
 *   - release: send the transformed content as-is
 *   - edit: modify the content before sending
 *   - drop: send empty content (suppresses the response)
 *
 * Singleton — shared across all sessions in the mcplocal process.
 */
import { randomUUID } from 'node:crypto';

export interface PausedItem {
  id: string;
  sessionId: string;
  projectName: string;
  contentType: string;
  sourceName: string;
  original: string;
  transformed: string;
  timestamp: number;
}

interface QueueEntry extends PausedItem {
  resolve: (content: string) => void;
}

type QueueListener = (items: PausedItem[]) => void;

class PauseQueue {
  private _paused = false;
  private queue: QueueEntry[] = [];
  private listeners = new Set<QueueListener>();

  get paused(): boolean {
    return this._paused;
  }

  setPaused(paused: boolean): void {
    this._paused = paused;
    if (!paused) {
      // Release all with their transformed content
      for (const entry of this.queue) {
        entry.resolve(entry.transformed);
      }
      this.queue = [];
    }
    this.notify();
  }

  /**
   * Enqueue content after pipeline processing.
   * If not paused, returns immediately with the transformed content.
   * If paused, blocks until the item is released/edited/dropped.
   */
  enqueue(item: Omit<PausedItem, 'id' | 'timestamp'>): Promise<string> {
    if (!this._paused) return Promise.resolve(item.transformed);

    return new Promise<string>((resolve) => {
      this.queue.push({
        ...item,
        id: randomUUID(),
        timestamp: Date.now(),
        resolve,
      });
      this.notify();
    });
  }

  /** Get all queued items (without resolve functions). */
  getItems(): PausedItem[] {
    return this.queue.map(({ resolve: _, ...item }) => item);
  }

  /** Release one item with its transformed content. */
  releaseOne(id: string): boolean {
    const idx = this.queue.findIndex((q) => q.id === id);
    if (idx < 0) return false;
    const entry = this.queue.splice(idx, 1)[0]!;
    entry.resolve(entry.transformed);
    this.notify();
    return true;
  }

  /** Edit the content and release. */
  editAndRelease(id: string, content: string): boolean {
    const idx = this.queue.findIndex((q) => q.id === id);
    if (idx < 0) return false;
    const entry = this.queue.splice(idx, 1)[0]!;
    entry.resolve(content);
    this.notify();
    return true;
  }

  /** Drop an item (sends empty content back to client). */
  dropOne(id: string): boolean {
    const idx = this.queue.findIndex((q) => q.id === id);
    if (idx < 0) return false;
    const entry = this.queue.splice(idx, 1)[0]!;
    entry.resolve('');
    this.notify();
    return true;
  }

  /** Release all items with their transformed content. Returns count released. */
  releaseAll(): number {
    const count = this.queue.length;
    for (const entry of this.queue) {
      entry.resolve(entry.transformed);
    }
    this.queue = [];
    this.notify();
    return count;
  }

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

  get size(): number {
    return this.queue.length;
  }

  private notify(): void {
    const items = this.getItems();
    for (const cb of this.listeners) {
      try {
        cb(items);
      } catch {
        // Don't let a bad listener break the queue
      }
    }
  }
}

/** Singleton pause queue shared across all sessions. */
export const pauseQueue = new PauseQueue();
