///|
/// Instance-level event resource. Create it before constructing publishers,
/// then transfer its lifetime to exactly one Agent with `events=system`.
/// Ordinary publishers receive only the owner-bound `Events` capability.
pub struct EventSystem {
  priv runtime : @event_runtime.EventRuntime
  priv mut owned : Bool
}

///|
/// Create an unclaimed resource. Close it explicitly if no Agent takes it over.
///
/// ```mbt check
/// test {
///   let system = @posoco.EventSystem()
///   let publisher = system.publisher(owner="provider").unwrap()
///   let subscription = publisher.subscribe(fn(_) { () }).unwrap()
///   assert_true(system.close() is Ok(_))
///   assert_true(system.publisher(owner="provider") is Err(Closed))
///   subscription.cancel()
/// }
/// ```
pub fn EventSystem::EventSystem() -> EventSystem {
  { runtime: @event_runtime.EventRuntime(), owned: false, }
}

///|
/// Grant publication in `Extension(owner)`, before or after Agent composition.
/// Granting a name is the resource holder's authority; a capability cannot
/// change its namespace or publish Posoco core events.
pub fn EventSystem::publisher(
  self : EventSystem,
  owner~ : String,
) -> Result[@events.Events, @events.EventError] {
  if self.runtime.is_closed() {
    return Err(Closed)
  }
  if owner.trim().is_empty() {
    return Err(InvalidOwner)
  }
  Ok(self.runtime.capability(owner))
}

///|
/// Subscribe before ports or the Agent exist. Registration order is shared
/// with Agent and extension listeners. Earlier events are never replayed.
pub fn EventSystem::subscribe(
  self : EventSystem,
  handler : (@events.EventEnvelope) -> Unit,
  namespace_~ : @events.EventNamespace? = None,
) -> Result[@events.Subscription, @events.EventError] {
  self.runtime.subscribe(handler, namespace_~)
}

///|
/// Release an unclaimed resource. Once an Agent owns it, only Agent.shutdown
/// may end its lifetime. Closing an already-closed resource is idempotent.
pub fn EventSystem::close(
  self : EventSystem,
) -> Result[Unit, @events.EventError] {
  if self.runtime.is_closed() {
    return Ok(())
  }
  if self.owned {
    return Err(Owned)
  }
  self.runtime.close()
  Ok(())
}

///|
fn EventSystem::claim(
  self : EventSystem,
) -> @event_runtime.EventRuntime raise @error.CompositionError {
  if self.runtime.is_closed() {
    raise EventSystemClosed
  }
  if self.owned {
    raise EventSystemAlreadyOwned
  }
  self.owned = true
  self.runtime
}