use async_trait::async_trait;
use theatre::actors::supervisor::{ActorStopReason, SuperviseActor, Supervised, Supervisor};
use theatre::prelude::*;

struct PanickingWorker;

impl Actor for PanickingWorker {}

#[async_trait]
impl Supervised for PanickingWorker {
    async fn new(_: &mut Context<Self>) -> Self
    where
        Self: Sized,
    {
        println!("Creating a new actor");
        PanickingWorker
    }

    async fn restarting(reason: ActorStopReason, _: &mut Context<Self>)
    where
        Self: Sized + Supervised,
    {
        match reason {
            ActorStopReason::Panic(err) => {
                if let Some(e) = err.downcast_ref::<&str>() {
                    println!("Actor panicked! Reason: {}", e)
                }
            }
        }
    }
}

struct DoPanic;

impl Message for DoPanic {
    type Return = ();
}

#[async_trait]
impl Handler<DoPanic> for PanickingWorker {
    async fn handle(&mut self, _: DoPanic, _: &mut Context<Self>) -> <DoPanic as Message>::Return {
        panic!("Ouch!")
    }
}

struct DoGoodThing;

impl Message for DoGoodThing {
    type Return = ();
}

#[async_trait]
impl Handler<DoGoodThing> for PanickingWorker {
    async fn handle(
        &mut self,
        _: DoGoodThing,
        _: &mut Context<Self>,
    ) -> <DoGoodThing as Message>::Return {
        println!("A good thing!")
    }
}

#[tokio::main]
async fn main() {
    let mut supervisor = Supervisor::new().spawn().await;
    let bad_worker = PanickingWorker;
    let mut bad_worker_addr = supervisor.send(SuperviseActor(bad_worker)).await.unwrap().0;

    let result = bad_worker_addr.send(DoPanic).await;
    println!("Result from the message: {:?}", result);
    bad_worker_addr.send(DoGoodThing).await.unwrap();
}
