// Integrated HashList basic operation tests
// Merged basic functionality tests from original hash_list.rs and find_hash.rs

use hash_link::{HashList, Hashable};

#[cfg(test)]
mod hash_link_tests {
    use super::*;

    // ==================== Shared test element definitions ====================

    /// Simple Copy test element
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct SimpleTestElement {
        value: u32,
        id: u32,
    }

    impl SimpleTestElement {
        fn new(value: u32, id: u32) -> Self {
            Self { value, id }
        }
    }

    impl Hashable for SimpleTestElement {
        fn get_hash(&self) -> u32 {
            self.value
        }
    }

    /// Complex non-Copy test element
    #[derive(Debug, Clone, PartialEq, Eq)]
    struct ComplexTestElement {
        value: u32,
        id: u32,
        data: String,
    }

    impl ComplexTestElement {
        fn new(value: u32, id: u32, data: &str) -> Self {
            Self {
                value,
                id,
                data: data.to_string(),
            }
        }
    }

    impl Hashable for ComplexTestElement {
        fn get_hash(&self) -> u32 {
            self.value
        }
    }

    // ==================== Basic operation tests ====================

    #[test]
    fn test_basic_operations() {
        let mut list = HashList::new();

        // Test insertion
        let idx1 = list.push(SimpleTestElement::new(10, 1));
        let idx2 = list.push(SimpleTestElement::new(20, 2));
        let idx3 = list.push(SimpleTestElement::new(30, 3));

        // Test search
        assert_eq!(list.index_of(&SimpleTestElement::new(10, 1)), Some(idx1));
        assert_eq!(list.index_of(&SimpleTestElement::new(20, 2)), Some(idx2));
        assert_eq!(list.index_of(&SimpleTestElement::new(30, 3)), Some(idx3));
        assert_eq!(list.index_of(&SimpleTestElement::new(40, 4)), None);

        // Test retrieval
        assert_eq!(list.get(idx1), Some(&SimpleTestElement::new(10, 1)));
        assert_eq!(list.get(idx2), Some(&SimpleTestElement::new(20, 2)));

        // Test deletion
        assert!(list.remove(&SimpleTestElement::new(20, 2)));
        assert_eq!(list.index_of(&SimpleTestElement::new(20, 2)), None);
        assert_eq!(list.get(idx2), None); // Removed element returns None

        // Test length
        assert_eq!(list.count(), 2);
    }

    #[test]
    fn test_iteration_order() {
        let mut list = HashList::new();

        let values = vec![
            SimpleTestElement::new(1, 1),
            SimpleTestElement::new(2, 2),
            SimpleTestElement::new(3, 3),
        ];

        for value in &values {
            list.push(*value);
        }

        // Iterator should return elements in insertion order
        let collected: Vec<SimpleTestElement> = list.iter().map(|(_, x)| *x).collect();
        assert_eq!(collected.len(), 3);

        for (i, value) in collected.iter().enumerate() {
            assert_eq!(*value, values[i]);
        }
    }

    #[test]
    fn test_first_last() {
        let mut list = HashList::new();

        assert_eq!(list.first(), None);
        assert_eq!(list.last(), None);

        list.push(SimpleTestElement::new(10, 1));
        list.push(SimpleTestElement::new(20, 2));
        list.push(SimpleTestElement::new(30, 3));

        assert_eq!(list.first(), Some(&SimpleTestElement::new(10, 1)));
        assert_eq!(list.last(), Some(&SimpleTestElement::new(30, 3)));

        list.remove(&SimpleTestElement::new(10, 1));
        assert_eq!(list.first(), Some(&SimpleTestElement::new(20, 2)));
    }

    #[test]
    fn test_clear() {
        let mut list = HashList::new();

        for i in 0..5 {
            list.push(SimpleTestElement::new(i as u32, i));
        }

        list.clear();
        assert_eq!(list.count(), 0);
        assert_eq!(list.len(), 0);
        assert!(list.is_empty());
    }

    #[test]
    fn test_capacity_grows_by_doubling() {
        let mut list = HashList::new();

        assert_eq!(list.capacity(), 2);

        list.push(SimpleTestElement::new(1, 1));
        list.push(SimpleTestElement::new(2, 2));
        assert_eq!(list.capacity(), 2);

        list.push(SimpleTestElement::new(3, 3));
        assert_eq!(list.capacity(), 4);

        list.push(SimpleTestElement::new(4, 4));
        assert_eq!(list.capacity(), 4);

        list.push(SimpleTestElement::new(5, 5));
        assert_eq!(list.capacity(), 8);
    }

    #[test]
    fn test_from_vec() {
        let vec = vec![
            SimpleTestElement::new(5, 5),
            SimpleTestElement::new(6, 6),
            SimpleTestElement::new(7, 7),
        ];
        let list = HashList::from_vec(vec);

        assert_eq!(list.count(), 3);
        assert!(list.contains(&SimpleTestElement::new(5, 5)));
        assert!(list.contains(&SimpleTestElement::new(6, 6)));
        assert!(list.contains(&SimpleTestElement::new(7, 7)));
        assert_eq!(list.hash_bucket_count(), 8);
    }

    // ==================== find_hash basic functionality tests ====================

    #[test]
    fn test_find_hash_basic_existing() {
        let mut list = HashList::new();

        // Insert elements with different hash values
        list.push(SimpleTestElement::new(10, 1));
        list.push(SimpleTestElement::new(20, 2));
        list.push(SimpleTestElement::new(30, 3));

        // Find existing hash values
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(20) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 1);
        assert_eq!(found[0].value, 20);
        assert_eq!(found[0].id, 2);
    }

    #[test]
    fn test_find_hash_basic_nonexisting() {
        let mut list = HashList::new();

        list.push(SimpleTestElement::new(10, 1));
        list.push(SimpleTestElement::new(20, 2));

        // Find non-existing hash values
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(999) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 0);
    }

    #[test]
    fn test_find_hash_empty_list() {
        let list: HashList<SimpleTestElement> = HashList::new();

        // Search in empty list
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(42) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 0);
    }

    #[test]
    fn test_find_hash_single_element() {
        let mut list = HashList::new();
        list.push(SimpleTestElement::new(777, 42));

        // Find existing hash values
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(777) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 1);
        assert_eq!(found[0].value, 777);
        assert_eq!(found[0].id, 42);

        // Find non-existing hash values
        let mut found_empty = Vec::new();
        for (_idx, elem) in list.find_hash(999) {
            found_empty.push(*elem);
        }

        assert_eq!(found_empty.len(), 0);
    }

    // ==================== Hash collision tests ====================

    #[test]
    fn test_find_hash_multiple_same_hash() {
        let mut list = HashList::new();

        // Insert 5 elements with same hash value
        let hash_value = 42u32;
        for i in 0..5 {
            list.push(SimpleTestElement::new(hash_value, i));
        }

        // Find this hash value
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(hash_value) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 5);
        for elem in &found {
            assert_eq!(elem.get_hash(), hash_value);
        }
    }

    #[test]
    fn test_find_hash_mixed_hashes() {
        let mut list = HashList::new();

        // Insert mixed hash values
        list.push(SimpleTestElement::new(10, 1));
        list.push(SimpleTestElement::new(20, 2));
        list.push(SimpleTestElement::new(10, 3)); // duplicate hash
        list.push(SimpleTestElement::new(30, 4));
        list.push(SimpleTestElement::new(20, 5)); // duplicate hash

        // Find hash value 10
        let mut found_10 = Vec::new();
        for (_idx, elem) in list.find_hash(10) {
            found_10.push(*elem);
        }
        assert_eq!(found_10.len(), 2);
        assert!(found_10.iter().all(|e| e.value == 10));

        // Find hash value 20
        let mut found_20 = Vec::new();
        for (_idx, elem) in list.find_hash(20) {
            found_20.push(*elem);
        }
        assert_eq!(found_20.len(), 2);
        assert!(found_20.iter().all(|e| e.value == 20));

        // Find hash value 30
        let mut found_30 = Vec::new();
        for (_idx, elem) in list.find_hash(30) {
            found_30.push(*elem);
        }
        assert_eq!(found_30.len(), 1);
        assert_eq!(found_30[0].value, 30);
    }

    // ==================== Deletion and compaction tests ====================

    #[test]
    fn test_find_hash_after_deletion() {
        let mut list = HashList::new();

        // Insert elements
        let indices = vec![
            list.push(SimpleTestElement::new(100, 1)),
            list.push(SimpleTestElement::new(100, 2)),
            list.push(SimpleTestElement::new(100, 3)),
            list.push(SimpleTestElement::new(100, 4)),
            list.push(SimpleTestElement::new(100, 5)),
        ];

        // Delete some elements
        list.remove_index(indices[1]); // Remove 2nd
        list.remove_index(indices[3]); // Remove 4th

        // Find remaining elements
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(100) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 3);
        let found_ids: Vec<u32> = found.iter().map(|e| e.id).collect();
        assert!(found_ids.contains(&1));
        assert!(found_ids.contains(&3));
        assert!(found_ids.contains(&5));
        assert!(!found_ids.contains(&2));
        assert!(!found_ids.contains(&4));
    }

    // ==================== Iterator behavior tests ====================

    #[test]
    fn test_find_hash_iterator_reuse() {
        let mut list = HashList::new();

        list.push(SimpleTestElement::new(50, 1));
        list.push(SimpleTestElement::new(50, 2));
        list.push(SimpleTestElement::new(50, 3));

        // Get iterator and reuse multiple times
        let iter1 = list.find_hash(50);
        let iter2 = list.find_hash(50);

        // Consume first iterator
        let count1 = iter1.count();
        assert_eq!(count1, 3);

        // Consume second iterator
        let count2 = iter2.count();
        assert_eq!(count2, 3);
    }

    #[test]
    fn test_find_hash_iterator_chaining() {
        let mut list = HashList::new();

        list.push(SimpleTestElement::new(100, 1));
        list.push(SimpleTestElement::new(200, 2));
        list.push(SimpleTestElement::new(100, 3));
        list.push(SimpleTestElement::new(300, 4));
        list.push(SimpleTestElement::new(100, 5));

        // Use iterator chaining
        let hash_100_elements: Vec<_> = list
            .find_hash(100)
            .filter(|(_, elem)| elem.id % 2 == 1) // Keep only odd IDs
            .map(|(_, elem)| *elem)
            .collect();

        assert_eq!(hash_100_elements.len(), 3);
        assert!(hash_100_elements.iter().all(|e| e.id % 2 == 1));
        assert!(hash_100_elements.iter().all(|e| e.value == 100));
    }

    // ==================== Complex data type tests ====================

    #[test]
    fn test_find_hash_with_complex_elements() {
        let mut list = HashList::new();

        // Insert elements with complex string data
        list.push(ComplexTestElement::new(
            42,
            1,
            "Hello, World! This is a longer string.",
        ));
        list.push(ComplexTestElement::new(
            42,
            2,
            "Another complex string with special chars: @#$%^&*()",
        ));
        list.push(ComplexTestElement::new(100, 3, "Simple string"));

        // Find hash value 42
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(42) {
            found.push(elem.clone());
        }

        assert_eq!(found.len(), 2);

        // Verify complex data is preserved
        let found_strings: Vec<String> = found.iter().map(|e| e.data.clone()).collect();
        assert!(found_strings.contains(&"Hello, World! This is a longer string.".to_string()));
        assert!(
            found_strings
                .contains(&"Another complex string with special chars: @#$%^&*()".to_string())
        );
    }

    // ==================== Boundary value tests ====================

    #[test]
    fn test_find_hash_zero_value() {
        let mut list = HashList::new();

        // Insert elements with zero hash value
        list.push(SimpleTestElement::new(0, 1));
        list.push(SimpleTestElement::new(0, 2));
        list.push(SimpleTestElement::new(1, 3));

        // Find zero hash value
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(0) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 2);
        assert!(found.iter().all(|e| e.value == 0));
    }

    #[test]
    fn test_find_hash_max_value() {
        let mut list = HashList::new();

        let max_hash = u32::MAX;
        list.push(SimpleTestElement::new(max_hash, 1));
        list.push(SimpleTestElement::new(max_hash, 2));
        list.push(SimpleTestElement::new(max_hash - 1, 3));

        // Find max hash value
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(max_hash) {
            found.push(*elem);
        }

        assert_eq!(found.len(), 2);
        assert!(found.iter().all(|e| e.value == max_hash));
    }

    // ==================== find_hash iteration order tests ====================

    #[test]
    fn test_find_hash_lifo_order() {
        let mut list = HashList::new();

        // Insert 5 elements with the same hash value, insertion order: id 1, 2, 3, 4, 5
        let hash_value = 42u32;
        let insertion_order = vec![
            list.push(SimpleTestElement::new(hash_value, 1)),
            list.push(SimpleTestElement::new(hash_value, 2)),
            list.push(SimpleTestElement::new(hash_value, 3)),
            list.push(SimpleTestElement::new(hash_value, 4)),
            list.push(SimpleTestElement::new(hash_value, 5)),
        ];

        // Use find_hash to get all elements with the same hash
        let mut found_elements = Vec::new();
        let mut found_indices = Vec::new();

        for (idx, elem) in list.find_hash(hash_value) {
            found_elements.push(*elem);
            found_indices.push(idx);
        }

        // Verify the number of elements found
        assert_eq!(
            found_elements.len(),
            5,
            "Should find 5 elements with the same hash"
        );

        // Verify that all elements have the correct hash value
        for elem in &found_elements {
            assert_eq!(elem.get_hash(), hash_value);
        }

        // Verify iteration order: should be LIFO (Last In First Out)
        // That is, the most recently inserted element (id=5) should be accessed first
        println!(
            "Found element ID order: {:?}",
            found_elements.iter().map(|e| e.id).collect::<Vec<_>>()
        );
        println!("Corresponding index order: {:?}", found_indices);

        // Verify the first found element is the most recently inserted (id=5)
        assert_eq!(
            found_elements[0].id, 5,
            "The first found element should be the most recently inserted (id=5)"
        );
        assert_eq!(
            found_elements[1].id, 4,
            "The second found element should be id=4"
        );
        assert_eq!(
            found_elements[2].id, 3,
            "The third found element should be id=3"
        );
        assert_eq!(
            found_elements[3].id, 2,
            "The fourth found element should be id=2"
        );
        assert_eq!(
            found_elements[4].id, 1,
            "The fifth found element should be the earliest inserted (id=1)"
        );

        // Verify that the index order is also as expected (from back to front)
        assert_eq!(
            found_indices[0], insertion_order[4],
            "The first index should be the index of the last inserted element"
        );
        assert_eq!(
            found_indices[4], insertion_order[0],
            "The last index should be the index of the first inserted element"
        );
    }

    #[test]
    fn test_find_hash_lifo_order_after_compaction() {
        let mut list = HashList::new();

        // Insert elements and delete some, then compact
        let hash_value = 100u32;
        let indices = vec![
            list.push(SimpleTestElement::new(hash_value, 1)),
            list.push(SimpleTestElement::new(hash_value, 2)),
            list.push(SimpleTestElement::new(hash_value, 3)),
            list.push(SimpleTestElement::new(hash_value, 4)),
            list.push(SimpleTestElement::new(hash_value, 5)),
        ];

        // Delete middle elements
        list.remove_index(indices[1]); // Delete id=2
        list.remove_index(indices[3]); // Delete id=4

        // Compact the list
        list.compact();

        // Verify that find_hash iteration order is still LIFO
        let mut found_elements = Vec::new();
        for (_idx, elem) in list.find_hash(hash_value) {
            found_elements.push(*elem);
        }

        assert_eq!(found_elements.len(), 3);
        println!(
            "Element ID order after compaction: {:?}",
            found_elements.iter().map(|e| e.id).collect::<Vec<_>>()
        );

        // Even after compaction, LIFO order should remain unchanged
        assert_eq!(
            found_elements[0].id, 5,
            "The first element should be the most recently inserted (id=5)"
        );
        assert_eq!(found_elements[1].id, 3, "The second element should be id=3");
        assert_eq!(
            found_elements[2].id, 1,
            "The third element should be the earliest inserted (id=1)"
        );
    }
}
