#![cfg(feature = "bytemuck-codec")]

use armdb::{BytemuckCodec, BytemuckSliceCodec, Config, TypedTree};
use tempfile::tempdir;

// ---------------------------------------------------------------------------
// BytemuckSliceCodec — Vec<T> where T: Pod
// ---------------------------------------------------------------------------

type VecTree = TypedTree<[u8; 8], Vec<u64>, BytemuckSliceCodec>;

fn open_vec_tree(dir: &std::path::Path) -> VecTree {
    TypedTree::open(dir, Config::test(), BytemuckSliceCodec).unwrap()
}

#[test]
fn test_slice_put_get() {
    let dir = tempdir().unwrap();
    let tree = open_vec_tree(dir.path());

    let key = 1u64.to_be_bytes();
    tree.put(&key, vec![10u64, 20, 30]).unwrap();

    let r = tree.get(&key).unwrap();
    assert_eq!(&*r, &[10u64, 20, 30]);
}

#[test]
fn test_slice_empty_vec() {
    let dir = tempdir().unwrap();
    let tree = open_vec_tree(dir.path());

    tree.put(&1u64.to_be_bytes(), vec![]).unwrap();
    let r = tree.get(&1u64.to_be_bytes()).unwrap();
    assert!(r.is_empty());
}

#[test]
fn test_slice_put_returns_old() {
    let dir = tempdir().unwrap();
    let tree = open_vec_tree(dir.path());

    let key = 1u64.to_be_bytes();
    tree.put(&key, vec![1, 2, 3]).unwrap();

    let old = tree.put(&key, vec![4, 5]).unwrap().unwrap();
    assert_eq!(&*old, &[1u64, 2, 3]);

    let current = tree.get(&key).unwrap();
    assert_eq!(&*current, &[4u64, 5]);
}

#[test]
fn test_slice_delete() {
    let dir = tempdir().unwrap();
    let tree = open_vec_tree(dir.path());

    tree.put(&1u64.to_be_bytes(), vec![42, 43]).unwrap();
    let old = tree.delete(&1u64.to_be_bytes()).unwrap().unwrap();
    assert_eq!(&*old, &[42u64, 43]);
    assert!(tree.get(&1u64.to_be_bytes()).is_none());
}

#[test]
fn test_slice_update() {
    let dir = tempdir().unwrap();
    let tree = open_vec_tree(dir.path());

    tree.put(&1u64.to_be_bytes(), vec![10, 20]).unwrap();

    let r = tree
        .update(&1u64.to_be_bytes(), |v| {
            let mut new = v.clone();
            new.push(30);
            new
        })
        .unwrap()
        .unwrap();
    assert_eq!(&*r, &[10u64, 20, 30]);
}

#[test]
fn test_slice_iter() {
    let dir = tempdir().unwrap();
    let tree = open_vec_tree(dir.path());

    for i in 0..5u64 {
        tree.put(&i.to_be_bytes(), vec![i; (i + 1) as usize])
            .unwrap();
    }

    // reversed=true → descending: key 4 first, key 0 last
    let entries: Vec<_> = tree.iter().collect();
    assert_eq!(entries.len(), 5);
    assert_eq!(entries[0].1, &[4u64, 4, 4, 4, 4]);
    assert_eq!(entries[4].1, &[0u64]);
}

#[test]
fn test_slice_recovery() {
    let dir = tempdir().unwrap();

    {
        let tree = open_vec_tree(dir.path());
        for i in 0..10u64 {
            tree.put(&i.to_be_bytes(), vec![i * 10, i * 100]).unwrap();
        }
        tree.close().unwrap();
    }

    let tree = open_vec_tree(dir.path());
    assert_eq!(tree.len(), 10);

    let r = tree.get(&5u64.to_be_bytes()).unwrap();
    assert_eq!(&*r, &[50u64, 500]);
}

// ---------------------------------------------------------------------------
// BytemuckCodec — single T: Pod
// ---------------------------------------------------------------------------

#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct Stats {
    count: u64,
    sum: f64,
}

type StatsTree = TypedTree<[u8; 8], Stats, BytemuckCodec>;

fn open_stats_tree(dir: &std::path::Path) -> StatsTree {
    TypedTree::open(dir, Config::test(), BytemuckCodec).unwrap()
}

#[test]
fn test_pod_put_get() {
    let dir = tempdir().unwrap();
    let tree = open_stats_tree(dir.path());

    let s = Stats {
        count: 42,
        sum: std::f64::consts::PI,
    };
    tree.put(&1u64.to_be_bytes(), s).unwrap();

    let r = tree.get(&1u64.to_be_bytes()).unwrap();
    assert_eq!(r.count, 42);
    assert_eq!(r.sum, std::f64::consts::PI);
}

#[test]
fn test_pod_update() {
    let dir = tempdir().unwrap();
    let tree = open_stats_tree(dir.path());

    tree.put(
        &1u64.to_be_bytes(),
        Stats {
            count: 10,
            sum: 1.0,
        },
    )
    .unwrap();

    let r = tree
        .update(&1u64.to_be_bytes(), |s| Stats {
            count: s.count + 1,
            sum: s.sum + 2.5,
        })
        .unwrap()
        .unwrap();
    assert_eq!(r.count, 11);
    assert_eq!(r.sum, 3.5);
}

#[test]
fn test_pod_recovery() {
    let dir = tempdir().unwrap();

    {
        let tree = open_stats_tree(dir.path());
        for i in 0..20u64 {
            tree.put(
                &i.to_be_bytes(),
                Stats {
                    count: i,
                    sum: i as f64,
                },
            )
            .unwrap();
        }
        tree.close().unwrap();
    }

    let tree = open_stats_tree(dir.path());
    assert_eq!(tree.len(), 20);

    let r = tree.get(&10u64.to_be_bytes()).unwrap();
    assert_eq!(r.count, 10);
    assert_eq!(r.sum, 10.0);
}

// ---------------------------------------------------------------------------
// Composite key prefix iteration with Vec<T>
// ---------------------------------------------------------------------------

#[test]
fn test_slice_prefix_iter() {
    let dir = tempdir().unwrap();
    let tree: TypedTree<[u8; 16], Vec<u32>, BytemuckSliceCodec> =
        TypedTree::open(dir.path(), Config::test(), BytemuckSliceCodec).unwrap();

    // Group 1: 3 entries
    for i in 0u64..3 {
        let mut key = [0u8; 16];
        key[3] = 1;
        key[8..].copy_from_slice(&i.to_be_bytes());
        tree.put(&key, vec![i as u32; 3]).unwrap();
    }

    // Group 2: 2 entries
    for i in 0u64..2 {
        let mut key = [0u8; 16];
        key[3] = 2;
        key[8..].copy_from_slice(&i.to_be_bytes());
        tree.put(&key, vec![100 + i as u32]).unwrap();
    }

    assert_eq!(tree.prefix_iter(&[0, 0, 0, 1]).count(), 3);
    assert_eq!(tree.prefix_iter(&[0, 0, 0, 2]).count(), 2);
}
