//! Batch tests

#![allow(unused_imports)]

use revue::reactive::*;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;

#[test]
fn test_batch_basic() {
    let counter = Arc::new(AtomicUsize::new(0));
    let counter_clone = counter.clone();

    batch(|| {
        queue_update(move || {
            counter_clone.fetch_add(1, Ordering::SeqCst);
        });
    });

    assert_eq!(counter.load(Ordering::SeqCst), 1);
}

#[test]
fn test_batch_nested() {
    let counter = Arc::new(AtomicUsize::new(0));

    batch(|| {
        let c1 = counter.clone();
        queue_update(move || {
            c1.fetch_add(1, Ordering::SeqCst);
        });

        batch(|| {
            let c2 = counter.clone();
            queue_update(move || {
                c2.fetch_add(1, Ordering::SeqCst);
            });
        });

        assert_eq!(counter.load(Ordering::SeqCst), 0);
    });

    assert_eq!(counter.load(Ordering::SeqCst), 2);
}

#[test]
fn test_is_batching() {
    assert!(!is_batching());

    batch(|| {
        assert!(is_batching());
    });

    assert!(!is_batching());
}

#[test]
fn test_batch_guard() {
    assert!(!is_batching());

    {
        let _guard = BatchGuard::new();
        assert!(is_batching());
    }

    assert!(!is_batching());
}

#[test]
fn test_transaction_commit() {
    let counter = Arc::new(AtomicUsize::new(0));

    let mut tx = Transaction::new();
    let c1 = counter.clone();
    tx.update(move || {
        c1.fetch_add(1, Ordering::SeqCst);
    });
    let c2 = counter.clone();
    tx.update(move || {
        c2.fetch_add(1, Ordering::SeqCst);
    });

    assert_eq!(tx.len(), 2);
    assert_eq!(counter.load(Ordering::SeqCst), 0);

    tx.commit();
    assert_eq!(counter.load(Ordering::SeqCst), 2);
}

#[test]
fn test_transaction_rollback() {
    let counter = Arc::new(AtomicUsize::new(0));

    let mut tx = Transaction::new();
    let c1 = counter.clone();
    tx.update(move || {
        c1.fetch_add(1, Ordering::SeqCst);
    });

    tx.rollback();
    assert_eq!(counter.load(Ordering::SeqCst), 0);
}

#[test]
fn test_flush() {
    let counter = Arc::new(AtomicUsize::new(0));

    start_batch();

    let c1 = counter.clone();
    queue_update(move || {
        c1.fetch_add(1, Ordering::SeqCst);
    });

    flush();
    assert_eq!(counter.load(Ordering::SeqCst), 1);

    end_batch();
}

#[test]
fn test_queue_update_no_batch() {
    let counter = Arc::new(AtomicUsize::new(0));
    let c1 = counter.clone();

    queue_update(move || {
        c1.fetch_add(1, Ordering::SeqCst);
    });

    assert_eq!(counter.load(Ordering::SeqCst), 1);
}

#[test]
fn test_batch_depth() {
    assert_eq!(batch_depth(), 0);

    start_batch();
    assert_eq!(batch_depth(), 1);

    start_batch();
    assert_eq!(batch_depth(), 2);

    end_batch();
    assert_eq!(batch_depth(), 1);

    end_batch();
    assert_eq!(batch_depth(), 0);
}

#[test]
fn test_batch_count() {
    let initial = batch_count();

    batch(|| {});
    assert!(batch_count() > initial);

    batch(|| {
        batch(|| {});
    });
    assert!(batch_count() > initial + 1);
}

#[test]
fn test_pending_count() {
    assert_eq!(pending_count(), 0);

    start_batch();

    queue_update(|| {});
    assert_eq!(pending_count(), 1);

    queue_update(|| {});
    assert_eq!(pending_count(), 2);

    end_batch();
    assert_eq!(pending_count(), 0);
}

#[test]
fn test_transaction_is_empty() {
    let tx = Transaction::new();
    assert!(tx.is_empty());
    assert_eq!(tx.len(), 0);
}

#[test]
fn test_transaction_len() {
    let mut tx = Transaction::new();
    assert_eq!(tx.len(), 0);

    tx.update(|| {});
    assert_eq!(tx.len(), 1);

    tx.update(|| {});
    assert_eq!(tx.len(), 2);
}

#[test]
fn test_batch_return_value() {
    let result = batch(|| 42);
    assert_eq!(result, 42);

    let result = batch(|| "hello".to_string());
    assert_eq!(result, "hello");
}

#[test]
fn test_multiple_flushes() {
    let counter = Arc::new(AtomicUsize::new(0));

    start_batch();

    let c1 = counter.clone();
    queue_update(move || {
        c1.fetch_add(1, Ordering::SeqCst);
    });

    flush();
    assert_eq!(counter.load(Ordering::SeqCst), 1);

    let c2 = counter.clone();
    queue_update(move || {
        c2.fetch_add(1, Ordering::SeqCst);
    });

    flush();
    assert_eq!(counter.load(Ordering::SeqCst), 2);

    end_batch();
}

#[test]
fn test_batch_guard_drop() {
    let counter = Arc::new(AtomicUsize::new(0));

    {
        let _guard = BatchGuard::new();
        let c1 = counter.clone();
        queue_update(move || {
            c1.fetch_add(1, Ordering::SeqCst);
        });
        assert_eq!(counter.load(Ordering::SeqCst), 0);
    }

    assert_eq!(counter.load(Ordering::SeqCst), 1);
}

#[test]
fn test_nested_batch_guard() {
    let counter = Arc::new(AtomicUsize::new(0));

    {
        let _outer = BatchGuard::new();
        assert!(is_batching());

        {
            let _inner = BatchGuard::new();
            assert!(is_batching());

            let c1 = counter.clone();
            queue_update(move || {
                c1.fetch_add(1, Ordering::SeqCst);
            });
        }

        assert_eq!(counter.load(Ordering::SeqCst), 0);
        assert!(is_batching());
    }

    assert_eq!(counter.load(Ordering::SeqCst), 1);
    assert!(!is_batching());
}

#[test]
fn test_transaction_default() {
    let tx = Transaction::default();
    assert!(tx.is_empty());
}

#[test]
fn test_end_batch_without_start() {
    let depth_before = batch_depth();
    end_batch();
    let depth_after = batch_depth();

    assert!(depth_after <= depth_before);
}

#[test]
fn test_independent_thread_batches() {
    let counter = Arc::new(AtomicUsize::new(0));
    let mut handles = vec![];

    for _ in 0..4 {
        let c = counter.clone();
        handles.push(std::thread::spawn(move || {
            batch(|| {
                assert!(is_batching());
                queue_update(move || {
                    c.fetch_add(1, Ordering::SeqCst);
                });
                assert!(is_batching());
            });
            assert!(!is_batching());
        }));
    }

    for handle in handles {
        handle.join().expect("Thread panicked");
    }

    assert_eq!(counter.load(Ordering::SeqCst), 4);
}
