///|
/// 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)

///|
async fn dummy_behavior(_context : Context, state : Int, msg : DummyMsg) -> Int {
  @async.pause()
  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~)

    // 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()
      match final_state {
        User(val) =>
          if val != 0 {
            fail("Expected state to be 0 after restart")
          }
        _ => fail("Expected User message")
      }
    } catch {
      _ => fail("Failed to get state")
    }
  })
}