/// An ActorSystem manages the lifecycle and execution of actors.
pub struct ActorSystem {
  name : String
  next_actor_id : Ref[Int]
  group : @async.TaskGroup[Unit]
  is_terminated : Ref[Bool]
}

/// Creates a new ActorSystem.
pub fn ActorSystem::new(name : String, group : @async.TaskGroup[Unit]) -> ActorSystem {
  { name, next_actor_id: { val: 0 }, group, is_terminated: { val: false } }
}

/// Terminates the ActorSystem, stopping the scheduler.
pub fn ActorSystem::terminate(self : ActorSystem) -> Unit {
  self.is_terminated.val = true
}

/// Spawns a new actor with the given behavior and initial state.
pub fn[State, Msg] ActorSystem::spawn(
  self : ActorSystem,
  behavior : Behavior[State, Msg],
  initial_state : State,
  supervisor? : Supervisor
) -> ActorRef[Msg] {
  let id = self.next_actor_id.val
  self.next_actor_id.val = id + 1
  
  let mailbox = @aqueue.Queue(kind=@aqueue.Unbounded)
  let actor_ref = { id, mailbox }
  
  let context = { system_id: 0, actor_id: id }
  let sv = match supervisor {
    Some(s) => s
    None => Supervisor::new(OneForOne, 3)
  }
  
  self.group.spawn_bg(no_wait=true, () => {
    actor_loop(context, initial_state, behavior, mailbox, sv)
  })
  
  actor_ref
}

async fn[State, Msg] actor_loop(
  context : Context,
  initial_state : State,
  behavior : Behavior[State, Msg],
  mailbox : @aqueue.Queue[Msg],
  supervisor : Supervisor
) -> Unit {
  let mut state = initial_state
  let mut retries = 0
  try {
    for ;; {
      let msg = mailbox.get()
      try {
        state = behavior(context, state, msg)
      } catch {
        err => {
          if retries < supervisor.max_retries {
            retries += 1
            // Akka-style restart: reset state to initial_state
            state = initial_state
          } else {
            raise err
          }
        }
      }
    }
  } catch {
    _ => ()
  }
}