//! Latest wins, call count, window timing, and re-arm across windows.

mod common;

use std::convert::Infallible;

use debounce_promise::Debounce;

use common::{sleep, Counter};

#[tokio::test]
async fn returns_the_result_of_the_latest_operation() {
    // README timeline: debounced() -> P(1) for every caller, refresh() runs once
    // with the last argument. Here the last argument is "qux".
    let debounced =
        Debounce::latest(|value: &'static str| async move { Ok::<_, Infallible>(value) })
            .wait_ms(100)
            .build();

    let calls = ["foo", "bar", "baz", "qux"].map(|v| debounced.call(v));
    let mut results = Vec::new();
    for c in calls {
        results.push(c.await);
    }

    assert_eq!(results, vec![Ok("qux"), Ok("qux"), Ok("qux"), Ok("qux")]);
}

#[tokio::test]
async fn does_not_call_the_function_repeatedly() {
    let calls = Counter::new();
    let c = calls.clone();
    let debounced = Debounce::latest(move |_: i32| {
        let c = c.clone();
        async move { Ok::<_, Infallible>(c.bump()) }
    })
    .wait_ms(100)
    .build();

    let futures = [1, 2, 3, 4].map(|n| debounced.call(n));
    for f in futures {
        let _ = f.await;
    }

    assert_eq!(calls.get(), 1);
}

#[tokio::test]
async fn waits_until_the_wait_time_has_passed() {
    let calls = Counter::new();
    let c = calls.clone();
    let debounced = Debounce::latest(move |_: i32| {
        let c = c.clone();
        async move { Ok::<_, Infallible>(c.bump()) }
    })
    .wait_ms(10)
    .build();

    let _a = debounced.call(1);
    let _b = debounced.call(2);
    let _c = debounced.call(3);
    assert_eq!(calls.get(), 0);

    sleep(20).await;
    assert_eq!(calls.get(), 1);
}

#[tokio::test]
async fn calls_the_function_again_if_wait_time_has_passed() {
    let calls = Counter::new();
    let c = calls.clone();
    let debounced = Debounce::latest(move |_: i32| {
        let c = c.clone();
        async move { Ok::<_, Infallible>(c.bump()) }
    })
    .wait_ms(10)
    .build();

    let first = debounced.call(1);
    sleep(20).await;
    assert_eq!(calls.get(), 1);
    drop(first);

    let second = debounced.call(2);
    sleep(20).await;
    assert_eq!(calls.get(), 2);
    drop(second);
}
