use crate::utils::{db_put_simple, open_database, tmpdir};
use leveldb::database::slice::Slice;
use leveldb::iterator::{
    BoundedKeyPolicy, Context, EntryIterator, EntryIteratorRev, Iterable, KeyIterator,
    LevelDBIterator, ValueIterator,
};
use leveldb::options::ReadOptions;

#[test]
fn test_iterator() {
    let tmp = tmpdir("iter");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let mut iter: EntryIterator<'_> = database.iter(ReadOptions::new());

    let entry = iter.next();
    assert!(entry.is_some());
    let (key1, val1) = entry.unwrap();
    assert_eq!(key1.as_bytes(), &[1]);
    assert_eq!(val1.as_bytes(), &[1]);
    let entry2 = iter.next();
    assert!(entry2.is_some());
    let (key2, val2) = entry2.unwrap();
    assert_eq!(key2.as_bytes(), &[2]);
    assert_eq!(val2.as_bytes(), &[2]);
    assert!(iter.next().is_none());
    assert!(iter.get_error().is_none());
}

#[test]
fn test_iterator_reverse() {
    let tmp = tmpdir("iter");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let mut iter: EntryIteratorRev<'_> = database.iter(ReadOptions::new());

    let entry = iter.next();
    assert!(entry.is_some());
    let (key1, val1) = entry.unwrap();
    assert_eq!(key1.as_bytes(), &[2]);
    assert_eq!(val1.as_bytes(), &[2]);
    let entry2 = iter.next();
    assert!(entry2.is_some());
    let (key2, val2) = entry2.unwrap();
    assert_eq!(key2.as_bytes(), &[1]);
    assert_eq!(val2.as_bytes(), &[1]);
    assert!(iter.next().is_none());
}

#[test]
fn test_iterator_last() {
    let tmp = tmpdir("iter_last");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let iter: EntryIterator<'_> = database.iter(ReadOptions::new());

    let last_entry = iter.last();
    assert!(last_entry.is_some());
    let (key, value) = last_entry.unwrap();
    assert_eq!(key.as_bytes(), &[2]);
    assert_eq!(value.as_bytes(), &[2]);
}

#[test]
fn test_key_iterator_last() {
    let tmp = tmpdir("key_iter_last");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], b"a");
    db_put_simple(database, &[2], b"b");

    let iter: KeyIterator<'_> = database.iter(ReadOptions::new());

    let last_key = iter.last();
    assert!(last_key.is_some());
    let key = last_key.unwrap();
    assert_eq!(key.as_bytes(), &[2]);
}

#[test]
fn test_value_iterator_last() {
    let tmp = tmpdir("value_iter_last");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], b"a");
    db_put_simple(database, &[2], b"b");

    let iter: ValueIterator<'_> = database.iter(ReadOptions::new());

    let last_value = iter.last();
    assert!(last_value.is_some());
    let value = last_value.unwrap();
    assert_eq!(value.as_bytes(), b"b");
}

#[test]
fn test_iterator_error_handling() {
    let tmp = tmpdir("iter_error");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], b"test");
    let read_opts: ReadOptions<'_> = ReadOptions::new();
    let mut iter: EntryIterator<'_> = database.iter(read_opts);

    // Check error before iteration - should be none
    assert!(iter.get_error().is_none());

    while iter.next().is_some() {}

    // Check error after iteration - should still be none for normal case
    assert!(iter.get_error().is_none());
}

#[test]
fn test_iterator_from_to() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);
    db_put_simple(database, &[3], &[3]);
    db_put_simple(database, &[4], &[4]);
    db_put_simple(database, &[5], &[5]);

    let begin = &[2][..];
    let end = &[4][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let mut iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let (key1, val1) = iter.next().unwrap();
    assert_eq!(key1.as_bytes(), &[2]);
    assert_eq!(val1.as_bytes(), &[2]);
    let (key2, val2) = iter.last().unwrap();
    assert_eq!(key2.as_bytes(), &[3]);
    assert_eq!(val2.as_bytes(), &[3]);
}

#[test]
fn test_iterator_bounded_next() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);
    db_put_simple(database, &[3], &[3]);
    db_put_simple(database, &[4], &[4]);
    db_put_simple(database, &[5], &[5]);

    let begin = &[2][..];
    let end = &[4][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert_eq!(keys, vec![[2], [3]]);
}

#[test]
fn test_iterator_bounded_next_2() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[3], &[3]);
    db_put_simple(database, &[5], &[5]);
    db_put_simple(database, &[7], &[7]);
    db_put_simple(database, &[9], &[9]);

    let begin = &[2][..];
    let end = &[6][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert_eq!(keys, vec![[3], [5]]);
}

#[test]
fn test_iterator_bounded_next_3() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[2], &[2]);
    db_put_simple(database, &[3], &[3]);
    db_put_simple(database, &[4], &[4]);
    db_put_simple(database, &[5], &[5]);
    db_put_simple(database, &[6], &[6]);

    let begin = &[1][..];
    let end = &[7][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert_eq!(keys, vec![[2], [3], [4], [5], [6]]);
}

#[test]
fn test_iterator_bounded_next_4() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[2], &[2]);
    db_put_simple(database, &[3], &[3]);
    db_put_simple(database, &[4], &[4]);
    db_put_simple(database, &[5], &[5]);
    db_put_simple(database, &[6], &[6]);

    let begin = &[1][..];
    let end = &[5][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert_eq!(keys, vec![[2], [3], [4]]);
}

#[test]
fn test_iterator_bounded_next_5() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[2], &[2]);
    db_put_simple(database, &[3], &[3]);
    db_put_simple(database, &[4], &[4]);
    db_put_simple(database, &[5], &[5]);
    db_put_simple(database, &[6], &[6]);

    let begin = &[4][..];
    let end = &[7][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert_eq!(keys, vec![[4], [5], [6]]);
}

#[test]
fn test_iterator_bounded_next_6() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);

    let begin = &[2][..];
    let end = &[3][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert!(keys.is_empty());
}

#[test]
fn test_iterator_bounded_next_7() {
    let tmp = tmpdir("from_to");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[3], &[3]);

    let begin = &[1][..];
    let end = &[2][..];
    let policy = BoundedKeyPolicy::new(Some(Slice::from(begin)), Some(Slice::from(end)));
    let iter: EntryIterator<'_, _> = database.iter_with_policy(ReadOptions::new(), policy);

    // Seek to the begin position for bounded iteration
    iter.seek(&Slice::from(begin));

    let mut keys = vec![];
    for (k, _) in iter {
        let mut bytes = [0];
        bytes.copy_from_slice(k.as_bytes());
        keys.push(bytes);
    }
    assert!(keys.is_empty());
}

#[test]
fn test_key_iterator() {
    let tmp = tmpdir("key_iter");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let mut iter: KeyIterator<'_> = database.iter(ReadOptions::new());
    let value = iter.next().unwrap();
    assert_eq!(value.as_bytes(), &[1]);
}

#[test]
fn test_value_iterator() {
    let tmp = tmpdir("value_iter");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let mut iter: ValueIterator<'_> = database.iter(ReadOptions::new());
    let value = iter.next().unwrap();
    assert_eq!(value.as_bytes(), &[1]);
}

#[test]
fn test_seek_before_inserted() {
    let tmp = tmpdir("iter");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let iter: EntryIterator<'_> = database.iter(ReadOptions::new());

    iter.seek(&Slice::from(&[0][..]));
    assert!(iter.valid());
    assert_eq!(unsafe { iter.key() }.unwrap().as_bytes(), &[1][..]);
}

#[test]
fn test_seek_after_inserted() {
    let tmp = tmpdir("iter");
    let database = &mut open_database(tmp.path(), true);
    db_put_simple(database, &[1], &[1]);
    db_put_simple(database, &[2], &[2]);

    let iter: EntryIterator<'_> = database.iter(ReadOptions::new());

    iter.seek(&Slice::from(&[3][..]));
    assert!(!iter.valid());
}
