Documentation
use crate::EResult;
use crate::Error;
use eva_common::prelude::*;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{Mutex, Notify};

#[macro_export]
macro_rules! periodic_worker {
    ($name: expr, $target: expr, $interval: expr) => {
        let (trigger, _fut) = bmart::worker!($target);
        $crate::workers::recreate_scheduler($name, trigger, $interval, false).await?;
    };
    ($name: expr, $target: expr, $interval: expr, $($arg:tt)+) => {
        let (trigger, _fut) = bmart::worker!($target, $($arg)+);
        $crate::workers::recreate_scheduler($name, trigger, $interval, false).await?;
    };
}

#[macro_export]
macro_rules! cleaner {
    ($name: expr, $target: expr, $interval: expr) => {
        $crate::periodic_worker!(&format!("cleaner::{}", $name), $target, $interval);
    };
    ($name: expr, $target: expr, $interval: expr, $($arg:tt)+) => {
        $crate::periodic_worker!(&format!("cleaner::{}", $name), $target, $interval, $($arg)+);
    };
}

impl From<bmart::Error> for Error {
    fn from(error: bmart::Error) -> Self {
        match error.kind {
            bmart::ErrorKind::Duplicate => {
                EvaError::newc(EvaErrorKind::ResourceAlreadyExists, error.message).into()
            }
            bmart::ErrorKind::NotFound => {
                EvaError::newc(EvaErrorKind::ResourceNotFound, error.message).into()
            }
            bmart::ErrorKind::InternalError => {
                EvaError::newc(EvaErrorKind::CoreError, error.message).into()
            }
            bmart::ErrorKind::Timeout => {
                EvaError::newc(EvaErrorKind::Timeout, error.message).into()
            }
        }
    }
}

lazy_static! {
    static ref WORKERS: Mutex<bmart::workers::WorkerFactory> =
        Mutex::new(bmart::workers::WorkerFactory::new());
}

/// # Errors
///
/// Will return `Err` if failed to recreate the worker
pub async fn recreate_scheduler(
    worker_id: &str,
    trigger: Arc<Notify>,
    interval: Duration,
    instant: bool,
) -> EResult<()> {
    WORKERS
        .lock()
        .await
        .recreate_scheduler(worker_id, trigger, interval, instant)
        .map_err(Into::into)
}

/// # Errors
///
/// Will return `Err` if the worker is not found
pub async fn destroy_scheduler(worker_id: &str) -> EResult<()> {
    WORKERS
        .lock()
        .await
        .destroy_scheduler(worker_id)
        .map_err(Into::into)
}