///|
/// A queue for receiving external events from the environment.
///
/// The `ExternalEventQueue` provides a thread-safe, non-blocking mechanism for
/// external sources (IDE, user input, environment) to communicate with the agent.
/// The environment pushes events via `send()`, and the agent polls via `poll()`.
///
/// # Example
///
/// ```moonbit no-check
/// let queue = ExternalEventQueue::new()
///
/// // From external source (e.g., IDE integration)
/// queue.send(Diagnostics(diagnostics))
///
/// // From agent (polling during conversation)
/// let events = queue.poll() // Returns all pending events
/// ```
struct ExternalEventQueue {
  uuid : @uuid.Generator
  queue : @aqueue.Queue[Event]
}

///|
/// Creates a new `ExternalEventQueue` with an unbounded async queue.
///
/// The queue is unbounded to prevent blocking when external sources send events,
/// ensuring that event producers never need to wait.
///
/// # Returns
///
/// A new `ExternalEventQueue` instance ready to receive events.
pub fn ExternalEventQueue::new() -> ExternalEventQueue raise {
  {
    uuid: @uuid.generator(@rand.chacha8()),
    queue: @aqueue.Queue::new(kind=Unbounded),
  }
}

///|
/// Sends an external event to the agent.
///
/// This method is designed to be called from external contexts (environment,
/// IDE integration, user input handlers) to communicate with the agent.
///
/// # Parameters
///
/// - `event`: The `ExternalEvent` to send to the agent.
///
/// # Behavior
///
/// - **Non-blocking**: Returns immediately without waiting.
/// - **Thread-safe**: Can be safely called from any context.
/// - **Fire-and-forget**: Silently ignores failures (queue full scenario).
///
/// # Example
///
/// ```moonbit no-check
/// // Send IDE diagnostics
/// queue.send(Diagnostics(diagnostics))
///
/// // Request cancellation
/// queue.send(Cancelled)
///
/// // Send user message
/// queue.send(UserMessage("Stop and explain"))
/// ```
pub fn ExternalEventQueue::send(
  self : ExternalEventQueue,
  desc : EventDesc,
) -> Unit {
  let created = @clock.epoch.now()
  let _ = self.queue.try_put({ id: self.uuid.v4(), created, desc }) catch {
    _ => false
  }
}

///|
/// Polls and retrieves all pending external events from the queue.
///
/// This method is called by the agent to check for and process any external
/// events that have been sent since the last poll. It drains the queue,
/// returning all accumulated events.
///
/// # Returns
///
/// An `Array[ExternalEvent]` containing all pending events. Returns an empty
/// array if no events are pending.
///
/// # Behavior
///
/// - **Non-blocking**: Returns immediately with available events.
/// - **Draining**: Removes all events from the queue.
/// - **Order-preserving**: Events are returned in FIFO order.
/// - **Incoming-only**: Only returns events intended for the consumption of the
///   agent.
///
/// # Example
///
/// ```moonbit no-check
/// // During conversation loop
/// let external_events = queue.poll()
/// for event in external_events {
///   match event {
///     Cancelled => return // Stop conversation
///     Diagnostics(d) => process_diagnostics(d)
///     UserMessage(msg) => handle_message(msg)
///   }
/// }
/// ```
pub fn ExternalEventQueue::poll(self : ExternalEventQueue) -> Array[Event] {
  let events = []
  while true {
    let event = self.queue.try_get() catch { _ => None }
    guard event is Some(event) && event.is_incoming() else { break }
    events.push(event)
  }
  events
}