/**
 * Async input queue — copy of the helper from `harnesses/claude.ts:37-68`.
 *
 * The Claude harness uses this exact shape as the input prompt to the Claude
 * Agent SDK's long-lived `query()`. The pi session loop uses the same pattern
 * so the non-blocking live-conversation behavior matches Claude byte-for-byte
 * at the queue level: pushMessage() never awaits the model.
 */
export interface AsyncQueue<T> extends AsyncIterable<T> {
  push(item: T): void;
  end(): void;
  /**
   * Non-blocking drain: return every item currently buffered without waiting
   * for a new one. Used by the session to fold mid-turn user messages into
   * the model's history without breaking the outer `for await` consumer.
   */
  drainPending(): T[];
}

export function createAsyncQueue<T>(): AsyncQueue<T> {
  const pending: T[] = [];
  let resolve: ((value: IteratorResult<T>) => void) | null = null;
  let done = false;

  return {
    push(item: T) {
      if (done) return;
      if (resolve) {
        resolve({ value: item, done: false });
        resolve = null;
      } else {
        pending.push(item);
      }
    },
    end() {
      done = true;
      if (resolve) resolve({ value: undefined as any, done: true });
    },
    drainPending() {
      if (pending.length === 0) return [];
      return pending.splice(0, pending.length);
    },
    [Symbol.asyncIterator]() {
      return {
        next(): Promise<IteratorResult<T>> {
          if (pending.length > 0) {
            return Promise.resolve({ value: pending.shift()!, done: false });
          }
          if (done) return Promise.resolve({ value: undefined as any, done: true });
          return new Promise((r) => { resolve = r; });
        },
      };
    },
  };
}
