Documentation
use crate::{Context, EventExt, EventExtLayer, EventOnceExt, EventOnceExtLayer, EventSchedule};
use std::fmt::Debug;
use std::future::Future;
use wd_tools::AsBytes;

#[derive(Debug, Clone, Default)]
pub struct EventEngine {
    schedule: EventSchedule,
}

impl EventEngine {
    pub fn get_schedule(&self) -> EventSchedule {
        self.schedule.clone()
    }
    pub async fn register_event<T, I, E>(&self, trigger: T, event: E)
    where
        T: AsBytes,
        I: Debug + Sync + Send + 'static,
        E: EventExt<I> + Send + 'static,
    {
        let trigger = trigger.as_byte();
        let event_handle = EventExtLayer::<I, E>::new(event);
        self.schedule
            .raw_register_event(trigger.to_vec(), event_handle)
            .await;
    }
    pub fn register_event_once<T, I, E>(&self, trigger: T, event: E)
    where
        T: AsBytes,
        I: Debug + Sync + Send + 'static,
        E: EventOnceExt<I> + Send + 'static,
    {
        let trigger = trigger.as_byte();
        let event_handle = EventOnceExtLayer::<I, E>::new(event);
        self.schedule
            .raw_register_once_event(trigger.to_vec(), event_handle);
    }

    pub async fn invoke<T: AsBytes, V: Debug + Sync + Send + 'static>(
        &self,
        trigger: T,
        input: V,
    ) -> anyhow::Result<Context> {
        let trigger = trigger.as_byte();
        let mut ctx = Context::new(input, trigger, &self.schedule).await;
        ctx.raw_next().await?;
        Ok(ctx)
    }
    pub fn launch<T: AsBytes + Send + 'static, V: Debug + Sync + Send + 'static>(
        &self,
        trigger: T,
        input: V,
    ) {
        let this = self.clone();
        tokio::spawn(async move {
            if let Err(e) = this.invoke(trigger, input).await {
                wd_log::log_error_ln!("[WD_EVENT::launch] error:{}", e);
            }
        });
    }
    pub fn launch_fn<T: AsBytes + Send + 'static, V: Debug + Sync + Send + 'static>(
        &self,
        trigger: T,
        input: V,
        over_fn: impl FnOnce(Context) + Send + 'static,
    ) {
        let this = self.clone();
        tokio::spawn(async move {
            match this.invoke(trigger, input).await {
                Ok(ctx) => over_fn(ctx),
                Err(e) => {
                    wd_log::log_error_ln!("[WD_EVENT::launch] error:{}", e);
                }
            }
        });
    }
    pub fn launch_fut<
        T: AsBytes + Send + 'static,
        V: Debug + Sync + Send + 'static,
        F: Future<Output = ()> + Send + 'static,
    >(
        &self,
        trigger: T,
        input: V,
        over_fn: impl FnOnce(Context) -> F + Send + 'static,
    ) {
        let this = self.clone();
        tokio::spawn(async move {
            match this.invoke(trigger, input).await {
                Ok(ctx) => over_fn(ctx).await,
                Err(e) => {
                    wd_log::log_error_ln!("[WD_EVENT::launch] error:{}", e);
                }
            }
        });
    }
}