use std::sync::Arc;
use tokio::time;
use context_async::{Context, Error, TimeChecker, Timer, With};

#[tokio::test]
async fn with_simple() {
    let tc = TimeChecker::new();

    let timer = Timer::with_timeout(time::Duration::from_secs(4));

    let err = time::sleep(time::Duration::from_secs(100))
        .with(timer)
        .await
        .err()
        .unwrap();

    assert_eq!(err, Error::ContextTimeout);

    assert!(tc.not_exceed(time::Duration::from_secs(5)));

}

#[derive(Debug, Clone)]
struct DataContext {
    timer: Timer,
    data: Arc<u8>,
}

#[async_trait::async_trait]
impl Context for DataContext {
    type SubContext = Self;
    
    fn timer(&self) -> Timer {
        self.timer.clone()
    }

    async fn spawn(&self) -> Self {
        Self {
            timer: self.timer.spawn().await,
            data: self.data.clone(),
        }
    }

    async fn spawn_with_timeout(&self, timeout: time::Duration) -> Self {
        Self {
            timer: self.timer.spawn_with_timeout(timeout).await,
            data: self.data.clone(),
        }
    }
}

#[tokio::test]
async fn context_with_data() {
    let ctx = DataContext {
        timer: Timer::with_timeout(time::Duration::from_secs(4)),
        data: Arc::new(42),
    };

    let c1 = ctx.clone();
    let t1 = tokio::spawn(async move {
        time::sleep(time::Duration::from_secs(1))
            .with(c1)
            .await
            .unwrap();
    });

    t1.await.unwrap();

    let c2 = ctx.clone();
    let t2 = tokio::spawn(async move {
        let fut = time::sleep(time::Duration::from_secs(1));
        c2.handle(fut).await.unwrap();
    });

    t2.await.unwrap();
}