use std::ops::Bound;

#[cfg(feature = "var-collections")]
use armdb::VarTree;
use armdb::{Config, ConstTree};
use tempfile::tempdir;

// Helper: insert N entries with keys [0..N) as big-endian u64
fn populate_const(tree: &ConstTree<[u8; 8], 8>, n: u64) {
    for i in 0..n {
        let key = i.to_be_bytes();
        let value = (i * 10).to_be_bytes();
        tree.put(&key, &value).unwrap();
    }
}

#[cfg(feature = "var-collections")]
fn populate_var(tree: &VarTree<[u8; 8]>, n: u64) {
    for i in 0..n {
        let key = i.to_be_bytes();
        let value = format!("val_{i}");
        tree.put(&key, value.as_bytes()).unwrap();
    }
}

// ===========================================================================
// ConstTree — reversed=true (default, primary use case)
// ===========================================================================

// ---------------------------------------------------------------------------
// ConstTree::iter()
// ---------------------------------------------------------------------------

#[test]
fn test_const_iter_all() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    let entries: Vec<_> = tree.iter().collect();
    assert_eq!(entries.len(), 20);

    // reversed=true → descending key order
    for i in 0..20u64 {
        let expected = 19 - i;
        assert_eq!(entries[i as usize].0, expected.to_be_bytes());
        assert_eq!(entries[i as usize].1, (expected * 10).to_be_bytes());
    }
}

#[test]
fn test_const_iter_empty() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();

    assert_eq!(tree.iter().count(), 0);
    assert!(tree.iter().next().is_none());
    assert!(tree.iter().next_back().is_none());
}

#[test]
fn test_const_iter_single_element() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    tree.put(&1u64.to_be_bytes(), &10u64.to_be_bytes()).unwrap();

    // Forward
    let mut iter = tree.iter();
    assert!(iter.next().is_some());
    assert!(iter.next().is_none());

    // Backward
    let mut iter = tree.iter();
    assert!(iter.next_back().is_some());
    assert!(iter.next_back().is_none());

    // Mixed
    let mut iter = tree.iter();
    let item = iter.next().unwrap();
    assert_eq!(item.0, 1u64.to_be_bytes());
    assert!(iter.next_back().is_none()); // already consumed
}

// ---------------------------------------------------------------------------
// ConstTree::range()
// ---------------------------------------------------------------------------

#[test]
fn test_const_range() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // Range [5, 10) with reversed=true → descending: 9,8,7,6,5
    let start = 5u64.to_be_bytes();
    let end = 10u64.to_be_bytes();
    let entries: Vec<_> = tree.range(&start, &end).collect();
    assert_eq!(entries.len(), 5);
    for (i, (key, _)) in entries.iter().enumerate() {
        assert_eq!(*key, (9 - i as u64).to_be_bytes());
    }
}

#[test]
fn test_const_range_empty() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 10);

    // Range where start == end → empty
    let key = 5u64.to_be_bytes();
    assert_eq!(tree.range(&key, &key).count(), 0);

    // Range beyond all keys
    let start = 100u64.to_be_bytes();
    let end = 200u64.to_be_bytes();
    assert_eq!(tree.range(&start, &end).count(), 0);
}

// ---------------------------------------------------------------------------
// ConstTree::prefix_iter()
// ---------------------------------------------------------------------------

#[test]
fn test_const_prefix_iter() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();

    // Composite key: [group:4][id:4]
    for (group, id) in [(1u32, 1u32), (1, 2), (1, 3), (2, 1), (2, 2)] {
        let mut key = [0u8; 8];
        key[..4].copy_from_slice(&group.to_be_bytes());
        key[4..].copy_from_slice(&id.to_be_bytes());
        tree.put(&key, &[0u8; 8]).unwrap();
    }

    // Prefix group 1: reversed → descending within group (id 3, 2, 1)
    let group1: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).collect();
    assert_eq!(group1.len(), 3);
    let ids: Vec<u32> = group1
        .iter()
        .map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
        .collect();
    assert_eq!(ids, vec![3, 2, 1]);

    // Prefix group 2: descending (id 2, 1)
    let group2: Vec<_> = tree.prefix_iter(&2u32.to_be_bytes()).collect();
    assert_eq!(group2.len(), 2);

    // Non-existent prefix
    let group3: Vec<_> = tree.prefix_iter(&3u32.to_be_bytes()).collect();
    assert_eq!(group3.len(), 0);
}

#[test]
fn prefix_iter_oversized_prefix_returns_empty() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 4], 8>::open(dir.path(), Config::test()).unwrap();
    tree.put(&[1, 2, 3, 4], &[0u8; 8]).unwrap();

    let oversized_prefix = &[1, 2, 3, 4, 5, 6, 7, 8];
    let results: Vec<_> = tree.prefix_iter(oversized_prefix).collect();
    assert!(results.is_empty());

    let mut iter = tree.prefix_iter(oversized_prefix);
    assert!(iter.next_back().is_none());
}

#[test]
fn test_const_prefix_take() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();

    // Composite key: [group:4][id:4]
    for id in 1..=100u32 {
        let mut key = [0u8; 8];
        key[..4].copy_from_slice(&1u32.to_be_bytes());
        key[4..].copy_from_slice(&id.to_be_bytes());
        tree.put(&key, &[0u8; 8]).unwrap();
    }

    // "Latest 5 posts" = prefix_iter().take(5) — the primary pattern
    let latest: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).take(5).collect();
    assert_eq!(latest.len(), 5);
    let ids: Vec<u32> = latest
        .iter()
        .map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
        .collect();
    assert_eq!(ids, vec![100, 99, 98, 97, 96]);
}

// ---------------------------------------------------------------------------
// ConstTree: DoubleEndedIterator
// ---------------------------------------------------------------------------

#[test]
fn test_const_iter_rev() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 10);

    // .rev() on reversed tree → ascending order
    let entries: Vec<_> = tree.iter().rev().collect();
    assert_eq!(entries.len(), 10);
    for i in 0..10u64 {
        assert_eq!(entries[i as usize].0, i.to_be_bytes());
    }
}

#[test]
fn test_const_iter_next_back() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 5);

    // reversed=true: forward yields [4,3,2,1,0], backward yields [0,1,2,3,4]
    let mut iter = tree.iter();

    // next → largest key (4)
    let (k, _) = iter.next().unwrap();
    assert_eq!(k, 4u64.to_be_bytes());

    // next_back → smallest key (0)
    let (k, _) = iter.next_back().unwrap();
    assert_eq!(k, 0u64.to_be_bytes());

    // next → 3
    let (k, _) = iter.next().unwrap();
    assert_eq!(k, 3u64.to_be_bytes());

    // next_back → 1
    let (k, _) = iter.next_back().unwrap();
    assert_eq!(k, 1u64.to_be_bytes());

    // next → 2 (last remaining)
    let (k, _) = iter.next().unwrap();
    assert_eq!(k, 2u64.to_be_bytes());

    // Iterator exhausted
    assert!(iter.next().is_none());
    assert!(iter.next_back().is_none());
}

#[test]
fn test_const_range_rev() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // range [5, 10).rev() on reversed → ascending: 5,6,7,8,9
    let start = 5u64.to_be_bytes();
    let end = 10u64.to_be_bytes();
    let entries: Vec<_> = tree.range(&start, &end).rev().collect();
    assert_eq!(entries.len(), 5);
    for (i, (key, _)) in entries.iter().enumerate() {
        assert_eq!(*key, (i as u64 + 5).to_be_bytes());
    }
}

#[test]
fn test_const_prefix_iter_rev() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();

    // Composite key: [group:4][id:4]
    for (group, id) in [(1u32, 1u32), (1, 2), (1, 3)] {
        let mut key = [0u8; 8];
        key[..4].copy_from_slice(&group.to_be_bytes());
        key[4..].copy_from_slice(&id.to_be_bytes());
        let value = id.to_be_bytes();
        let mut val8 = [0u8; 8];
        val8[4..].copy_from_slice(&value);
        tree.put(&key, &val8).unwrap();
    }

    // prefix_iter().rev() on reversed → ascending within prefix: 1, 2, 3
    let entries: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).rev().collect();
    assert_eq!(entries.len(), 3);
    let ids: Vec<u32> = entries
        .iter()
        .map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
        .collect();
    assert_eq!(ids, vec![1, 2, 3]);
}

// ===========================================================================
// VarTree — reversed=true (default, primary use case)
// ===========================================================================

// ---------------------------------------------------------------------------
// VarTree::iter()
// ---------------------------------------------------------------------------

#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_all() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
    populate_var(&tree, 20);

    let entries: Vec<_> = tree.iter().collect();
    assert_eq!(entries.len(), 20);

    // reversed=true → descending key order
    for i in 0..20u64 {
        let expected = 19 - i;
        assert_eq!(entries[i as usize].0, expected.to_be_bytes());
        assert_eq!(
            entries[i as usize].1.as_bytes(),
            format!("val_{expected}").as_bytes()
        );
    }
}

#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_empty() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();

    assert_eq!(tree.iter().count(), 0);
    assert!(tree.iter().next_back().is_none());
}

// ---------------------------------------------------------------------------
// VarTree::range()
// ---------------------------------------------------------------------------

#[cfg(feature = "var-collections")]
#[test]
fn test_var_range() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
    populate_var(&tree, 20);

    // Range [5, 10) reversed → descending: 9,8,7,6,5
    let start = 5u64.to_be_bytes();
    let end = 10u64.to_be_bytes();
    let entries: Vec<_> = tree.range(&start, &end).collect();
    assert_eq!(entries.len(), 5);
    for (i, (key, _)) in entries.iter().enumerate() {
        assert_eq!(*key, (9 - i as u64).to_be_bytes());
    }
}

// ---------------------------------------------------------------------------
// VarTree::prefix_iter()
// ---------------------------------------------------------------------------

#[cfg(feature = "var-collections")]
#[test]
fn test_var_prefix_take() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();

    for id in 1..=50u32 {
        let mut key = [0u8; 8];
        key[..4].copy_from_slice(&1u32.to_be_bytes());
        key[4..].copy_from_slice(&id.to_be_bytes());
        tree.put(&key, format!("msg_{id}").as_bytes()).unwrap();
    }

    // "Latest 5 messages" = prefix_iter().take(5)
    let latest: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).take(5).collect();
    assert_eq!(latest.len(), 5);
    let ids: Vec<u32> = latest
        .iter()
        .map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
        .collect();
    assert_eq!(ids, vec![50, 49, 48, 47, 46]);
}

// ---------------------------------------------------------------------------
// VarTree: DoubleEndedIterator
// ---------------------------------------------------------------------------

#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_rev() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
    populate_var(&tree, 10);

    // .rev() on reversed → ascending
    let entries: Vec<_> = tree.iter().rev().collect();
    assert_eq!(entries.len(), 10);
    for i in 0..10u64 {
        assert_eq!(entries[i as usize].0, i.to_be_bytes());
    }
}

#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_next_back() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
    populate_var(&tree, 5);

    let mut iter = tree.iter();

    // next → largest (4)
    let (k, _) = iter.next().unwrap();
    assert_eq!(k, 4u64.to_be_bytes());

    // next_back → smallest (0)
    let (k, _) = iter.next_back().unwrap();
    assert_eq!(k, 0u64.to_be_bytes());

    // next → 3
    let (k, _) = iter.next().unwrap();
    assert_eq!(k, 3u64.to_be_bytes());

    // next_back → 1
    let (k, _) = iter.next_back().unwrap();
    assert_eq!(k, 1u64.to_be_bytes());

    // next → 2
    let (k, _) = iter.next().unwrap();
    assert_eq!(k, 2u64.to_be_bytes());

    assert!(iter.next().is_none());
    assert!(iter.next_back().is_none());
}

#[cfg(feature = "var-collections")]
#[test]
fn test_var_range_rev() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
    populate_var(&tree, 20);

    // range [5, 10).rev() on reversed → ascending: 5,6,7,8,9
    let start = 5u64.to_be_bytes();
    let end = 10u64.to_be_bytes();
    let entries: Vec<_> = tree.range(&start, &end).rev().collect();
    assert_eq!(entries.len(), 5);
    for (i, (key, _)) in entries.iter().enumerate() {
        assert_eq!(*key, (i as u64 + 5).to_be_bytes());
    }
}

#[cfg(feature = "var-collections")]
#[test]
fn test_var_prefix_iter_rev() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();

    for (group, id) in [(1u32, 1u32), (1, 2), (1, 3)] {
        let mut key = [0u8; 8];
        key[..4].copy_from_slice(&group.to_be_bytes());
        key[4..].copy_from_slice(&id.to_be_bytes());
        tree.put(&key, format!("msg_{id}").as_bytes()).unwrap();
    }

    // prefix_iter().rev() on reversed → ascending: 1, 2, 3
    let entries: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).rev().collect();
    assert_eq!(entries.len(), 3);
    let ids: Vec<u32> = entries
        .iter()
        .map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
        .collect();
    assert_eq!(ids, vec![1, 2, 3]);
}

// ===========================================================================
// Ascending (reversed=false) — secondary coverage
// ===========================================================================

fn ascending() -> Config {
    let mut c = Config::test();
    c.reversed = false;
    c
}

#[test]
fn test_const_ascending_iter() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
    populate_const(&tree, 10);

    let entries: Vec<_> = tree.iter().collect();
    assert_eq!(entries.len(), 10);
    for i in 0..10u64 {
        assert_eq!(entries[i as usize].0, i.to_be_bytes());
    }

    // .rev() → descending
    let rev: Vec<_> = tree.iter().rev().collect();
    assert_eq!(rev[0].0, 9u64.to_be_bytes());
    assert_eq!(rev[9].0, 0u64.to_be_bytes());
}

#[test]
fn test_const_ascending_range() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
    populate_const(&tree, 20);

    let start = 5u64.to_be_bytes();
    let end = 10u64.to_be_bytes();
    let entries: Vec<_> = tree.range(&start, &end).collect();
    assert_eq!(entries.len(), 5);
    for (i, (key, _)) in entries.iter().enumerate() {
        assert_eq!(*key, (i as u64 + 5).to_be_bytes());
    }
}

#[cfg(feature = "var-collections")]
#[test]
fn test_var_ascending_iter() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), ascending()).unwrap();
    populate_var(&tree, 10);

    let entries: Vec<_> = tree.iter().collect();
    assert_eq!(entries.len(), 10);
    for i in 0..10u64 {
        assert_eq!(entries[i as usize].0, i.to_be_bytes());
    }

    // .rev() → descending
    let rev: Vec<_> = tree.iter().rev().collect();
    assert_eq!(rev[0].0, 9u64.to_be_bytes());
    assert_eq!(rev[9].0, 0u64.to_be_bytes());
}

// ===========================================================================
// range_bounds — reversed=true (DESC, default)
// ===========================================================================

fn key(v: u64) -> [u8; 8] {
    v.to_be_bytes()
}

fn keys_from(entries: &[([u8; 8], [u8; 8])]) -> Vec<u64> {
    entries
        .iter()
        .map(|(k, _)| u64::from_be_bytes(*k))
        .collect()
}

#[test]
fn test_const_range_bounds_included_included() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // [5, 10] → DESC: 10, 9, 8, 7, 6, 5
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
        .collect();
    assert_eq!(keys_from(&entries), vec![10, 9, 8, 7, 6, 5]);
}

#[test]
fn test_const_range_bounds_excluded_excluded() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // (5, 10) → DESC: 9, 8, 7, 6
    let entries: Vec<_> = tree
        .range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(10)))
        .collect();
    assert_eq!(keys_from(&entries), vec![9, 8, 7, 6]);
}

#[test]
fn test_const_range_bounds_excluded_included() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // (5, 10] → DESC: 10, 9, 8, 7, 6
    let entries: Vec<_> = tree
        .range_bounds(Bound::Excluded(&key(5)), Bound::Included(&key(10)))
        .collect();
    assert_eq!(keys_from(&entries), vec![10, 9, 8, 7, 6]);
}

#[test]
fn test_const_range_bounds_included_excluded() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // [5, 10) → same as range() → DESC: 9, 8, 7, 6, 5
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Excluded(&key(10)))
        .collect();
    let via_range: Vec<_> = tree.range(&key(5), &key(10)).collect();
    assert_eq!(keys_from(&entries), vec![9, 8, 7, 6, 5]);
    assert_eq!(entries, via_range);
}

#[test]
fn test_const_range_bounds_unbounded_start() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // (.., 5) → DESC: 4, 3, 2, 1, 0
    let entries: Vec<_> = tree
        .range_bounds(Bound::Unbounded, Bound::Excluded(&key(5)))
        .collect();
    assert_eq!(keys_from(&entries), vec![4, 3, 2, 1, 0]);
}

#[test]
fn test_const_range_bounds_unbounded_end() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // [15, ..) → DESC: 19, 18, 17, 16, 15
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(15)), Bound::Unbounded)
        .collect();
    assert_eq!(keys_from(&entries), vec![19, 18, 17, 16, 15]);
}

#[test]
fn test_const_range_bounds_both_unbounded() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // (.., ..) → same as iter()
    let via_bounds: Vec<_> = tree
        .range_bounds(Bound::Unbounded, Bound::Unbounded)
        .collect();
    let via_iter: Vec<_> = tree.iter().collect();
    assert_eq!(via_bounds, via_iter);
    assert_eq!(via_bounds.len(), 20);
}

#[test]
fn test_const_range_bounds_rev() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // [5, 10].rev() on reversed tree → ASC: 5, 6, 7, 8, 9, 10
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
        .rev()
        .collect();
    assert_eq!(keys_from(&entries), vec![5, 6, 7, 8, 9, 10]);
}

#[test]
fn test_const_range_bounds_single_element() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
    populate_const(&tree, 20);

    // [5, 5] → single element
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Included(&key(5)))
        .collect();
    assert_eq!(keys_from(&entries), vec![5]);

    // (5, 5) → empty
    let entries: Vec<_> = tree
        .range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(5)))
        .collect();
    assert!(entries.is_empty());
}

// ===========================================================================
// range_bounds — reversed=false (ASC)
// ===========================================================================

#[test]
fn test_const_ascending_range_bounds_included_included() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
    populate_const(&tree, 20);

    // [5, 10] → ASC: 5, 6, 7, 8, 9, 10
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
        .collect();
    assert_eq!(keys_from(&entries), vec![5, 6, 7, 8, 9, 10]);
}

#[test]
fn test_const_ascending_range_bounds_excluded_excluded() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
    populate_const(&tree, 20);

    // (5, 10) → ASC: 6, 7, 8, 9
    let entries: Vec<_> = tree
        .range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(10)))
        .collect();
    assert_eq!(keys_from(&entries), vec![6, 7, 8, 9]);
}

#[test]
fn test_const_ascending_range_bounds_unbounded() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
    populate_const(&tree, 20);

    // (.., 3] → ASC: 0, 1, 2, 3
    let entries: Vec<_> = tree
        .range_bounds(Bound::Unbounded, Bound::Included(&key(3)))
        .collect();
    assert_eq!(keys_from(&entries), vec![0, 1, 2, 3]);

    // (16, ..) → ASC: 17, 18, 19
    let entries: Vec<_> = tree
        .range_bounds(Bound::Excluded(&key(16)), Bound::Unbounded)
        .collect();
    assert_eq!(keys_from(&entries), vec![17, 18, 19]);
}

#[test]
fn test_const_ascending_range_bounds_matches_range() {
    let dir = tempdir().unwrap();
    let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
    populate_const(&tree, 20);

    // range_bounds(Included, Excluded) == range()
    let via_bounds: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Excluded(&key(10)))
        .collect();
    let via_range: Vec<_> = tree.range(&key(5), &key(10)).collect();
    assert_eq!(via_bounds, via_range);
}

// ===========================================================================
// range_bounds — VarTree
// ===========================================================================

#[cfg(feature = "var-collections")]
#[test]
fn test_var_range_bounds_included_included() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
    populate_var(&tree, 20);

    // [5, 10] → DESC: 10, 9, 8, 7, 6, 5
    let entries: Vec<_> = tree
        .range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
        .collect();
    let keys: Vec<u64> = entries
        .iter()
        .map(|(k, _)| u64::from_be_bytes(*k))
        .collect();
    assert_eq!(keys, vec![10, 9, 8, 7, 6, 5]);
}

#[cfg(feature = "var-collections")]
#[test]
fn test_var_ascending_range_bounds_excluded_excluded() {
    let dir = tempdir().unwrap();
    let tree = VarTree::<[u8; 8]>::open(dir.path(), ascending()).unwrap();
    populate_var(&tree, 20);

    // (5, 10) → ASC: 6, 7, 8, 9
    let entries: Vec<_> = tree
        .range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(10)))
        .collect();
    let keys: Vec<u64> = entries
        .iter()
        .map(|(k, _)| u64::from_be_bytes(*k))
        .collect();
    assert_eq!(keys, vec![6, 7, 8, 9]);
}
