#![allow(deprecated)]
#![allow(dead_code)]

#[macro_use]
extern crate serde_json;

use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc, Mutex,
};

mod common;

use common::*;

// #[test]
fn collections_exec() {
    let ditto = common::get_builder_ditto(None).unwrap();
    collections_exec_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    collections_exec_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    collections_exec_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    collections_exec_impl(ditto);
}

fn collections_exec_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let store = ditto.store();

    let collection_one = store.collection("test1").unwrap();
    collection_one.upsert(json!({"a": 1})).unwrap();
    let collection_two = store.collection("test2").unwrap();
    collection_two.upsert(json!({"a": 2})).unwrap();

    let collections = store.collections().exec().unwrap();
    assert_eq!(collections.len(), 2);
    assert_eq!(collections[0].name(), collection_one.name());
    assert_eq!(collections[1].name(), collection_two.name());
}

// #[test]
fn collections_observe() -> Result<(), Box<dyn std::error::Error>> {
    let ditto = common::get_builder_ditto(None).unwrap();
    collections_observe_impl(ditto)?;

    let ditto = common::get_revamp_ditto(None).unwrap();
    collections_observe_impl(ditto)?;

    Ok(())
}

fn collections_observe_impl(
    ditto: dittolive_ditto::prelude::Ditto,
) -> Result<(), Box<dyn std::error::Error>> {
    let store = ditto.store();

    let collection_one = store.collection("test1").unwrap();
    collection_one.upsert(json!({"a": 1})).unwrap();

    let collection_two = store.collection("test2").unwrap();
    let collection_three = store.collection("test3").unwrap();

    let finished = Arc::new(AtomicBool::new(false));
    let finished_clone = Arc::clone(&finished);
    let counter = Arc::new(Mutex::new(0));
    assert_eq!(*counter.lock().unwrap(), 0);

    let handler = move |event: CollectionsEvent| {
        let counter_mtx = &*counter; // move (copy) and reborrow
        let mut c = counter_mtx.lock().unwrap();
        let count = &mut (*c);
        *count += 1;

        match *count {
            1 => {
                assert!(event.is_initial);
                assert_eq!(event.collections.len(), 1);
                assert_eq!(event.collections[0].name(), collection_one.name());

                collection_three.upsert(json!({"a": 3})).unwrap();
            }
            2 => {
                assert!(!event.is_initial);
                assert_eq!(event.collections.len(), 2);
                assert_eq!(event.old_collections.len(), 1);
                assert_eq!(event.insertions, vec![1].into());
                assert_eq!(event.collections[0].name(), collection_one.name());
                assert_eq!(event.collections[1].name(), collection_three.name());

                collection_two.upsert(json!({"a": 2})).unwrap();
            }
            3 => {
                assert!(!event.is_initial);
                assert_eq!(event.collections.len(), 3);
                assert_eq!(event.old_collections.len(), 2);
                assert_eq!(event.insertions, vec![1].into());
                assert_eq!(event.collections[0].name(), collection_one.name());
                assert_eq!(event.collections[1].name(), collection_two.name());
                assert_eq!(event.collections[2].name(), collection_three.name());

                finished_clone.store(true, Ordering::SeqCst);
            }
            _ => panic!("Callback called too many times"),
        }
    };
    let lq = store.collections().observe_local(handler)?;

    // wait for the LQ thread to finish
    while !finished.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }

    drop(lq); // In theory, the clone should drop here
    drop(ditto);

    Ok(())
}
