/// Defines the strategy used by a supervisor when a child actor fails.
pub enum SupervisionStrategy {
  /// Restart only the failed child actor.
  OneForOne
  /// Restart all child actors managed by the supervisor.
  OneForAll
  /// Restart the failed child actor and any actors started after it.
  RestForOne
}

/// A Supervisor manages a set of child actors and applies a supervision strategy upon failure.
pub struct Supervisor {
  strategy : SupervisionStrategy
  max_retries : Int
}

/// Creates a new Supervisor with the specified strategy and retry limit.
pub fn Supervisor::new(strategy : SupervisionStrategy, max_retries : Int) -> Supervisor {
  { strategy, max_retries }
}

test "supervisor creation" {
  let _s1 = Supervisor::new(OneForOne, 3)
  let _s2 = Supervisor::new(OneForAll, 3)
  let _s3 = Supervisor::new(RestForOne, 3)
}

priv enum DummyMsg {
  Increment
  FailOnce
  GetState(ActorRef[Int])
}

suberror DummyError derive(Debug)

fn dummy_behavior(_context : Context, state : Int, msg : DummyMsg) -> Int raise {
  match msg {
    Increment => state + 1
    FailOnce => {
      if state > 0 {
        raise DummyError
      }
      state
    }
    GetState(reply_to) => {
      reply_to.send(state)
      state
    }
  }
}

async test "supervision one-for-one restart" {
  @async.with_task_group((group) => {
    let system = ActorSystem::new("test_system", group)
    let supervisor = Supervisor::new(OneForOne, 3)
    let actor = system.spawn(dummy_behavior, 0, supervisor=supervisor)
    
    // Create an actor to receive the reply
    let reply_box = @aqueue.Queue(kind=@aqueue.Unbounded)
    let reply_ref = { id: 99, mailbox: reply_box }
    
    actor.send(Increment)       // state becomes 1
    actor.send(FailOnce)        // state is 1 > 0, raises error -> supervisor restarts actor -> state reset to 0
    actor.send(GetState(reply_ref))
    
    @async.sleep(50)
    
    try {
      let final_state = reply_box.get()
      // Since it restarted, state should be 0
      if final_state != 0 {
        fail("Expected state to be 0 after restart")
      }
    } catch {
      _ => fail("Failed to get state")
    }
  })
}