// Advanced find_hash functionality tests
// Integrated advanced test cases from original find_hash.rs, removed duplicate basic functionality tests

use hash_link::{HashList, Hashable};

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

    // Redefine test elements to avoid circular imports

    // 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
        }
    }

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

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

        // Insert many elements with same hash value
        let hash_value = 123u32;
        let element_count = 100;

        for i in 0..element_count {
            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(), element_count as usize);

        // Verify all found elements are correct
        for elem in &found {
            assert_eq!(elem.get_hash(), hash_value);
        }

        // Verify all IDs are found
        let found_ids: Vec<u32> = found.iter().map(|e| e.id).collect();
        for i in 0..element_count {
            assert!(found_ids.contains(&i));
        }
    }

    // ==================== Rehash and resize tests ====================

    #[test]
    fn test_find_hash_after_rehash() {
        let mut list = HashList::with_capacity(4, 2); // Small capacity forces rehash

        // Insert enough elements to trigger multiple rehashes
        for i in 0..10 {
            list.push(SimpleTestElement::new(i as u32 * 10, i)); // hash values: 0, 10, 20, 30...
        }

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

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

    #[test]
    fn test_find_hash_filters_same_bucket_different_full_hash() {
        let mut list = HashList::with_capacity(4, 4);

        // 1 and 5 land in the same bucket when mask == 3, but their full hashes differ.
        list.push(SimpleTestElement::new(1, 1));
        list.push(SimpleTestElement::new(5, 2));

        let found: Vec<_> = list.find_hash(1).map(|(_, elem)| *elem).collect();

        assert_eq!(found, vec![SimpleTestElement::new(1, 1)]);
    }

    #[test]
    fn test_find_hash_rehash_with_collisions() {
        let mut list = HashList::with_capacity(4, 2); // Small capacity forces rehash

        // Insert elements with same hash value to trigger rehash
        let elements = vec![
            SimpleTestElement::new(42, 1),
            SimpleTestElement::new(42, 2),  // same hash
            SimpleTestElement::new(106, 3), // triggers rehash
            SimpleTestElement::new(170, 4), // triggers another rehash
            SimpleTestElement::new(42, 5),  // same hash again
        ];

        for elem in &elements {
            list.push(*elem);
        }

        // Find specific hash value after rehash
        let target_hash = 42u32;
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(target_hash) {
            found.push(*elem);
        }

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

    #[test]
    fn test_find_hash_after_multiple_rehashes() {
        let mut list = HashList::with_capacity(2, 1); // Force multiple rehashes

        // Insert many elements to trigger multiple rehashes
        let element_count = 100;
        for i in 0..element_count {
            list.push(SimpleTestElement::new(i as u32 * 7, i)); // Use prime multiplier distribution
        }

        // Test finding various hash values after rehash
        let test_hashes = [0, 70, 350, 630]; // existing hash values
        for &hash_val in &test_hashes {
            let mut found = Vec::new();
            for (_idx, elem) in list.find_hash(hash_val) {
                found.push(*elem);
            }
            assert_eq!(found.len(), 1);
            assert_eq!(found[0].value, hash_val);
        }

        // Test non-existing hash
        let mut not_found = Vec::new();
        for (_idx, elem) in list.find_hash(9999) {
            not_found.push(*elem);
        }
        assert_eq!(not_found.len(), 0);
    }

    // ==================== Compaction and memory layout tests ====================

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

        // Insert and delete elements to create holes
        let indices = vec![
            list.push(SimpleTestElement::new(200, 1)),
            list.push(SimpleTestElement::new(200, 2)),
            list.push(SimpleTestElement::new(200, 3)),
            list.push(SimpleTestElement::new(200, 4)),
        ];

        // Delete 1st and 3rd elements
        list.remove_index(indices[0]);
        list.remove_index(indices[2]);

        // Compact operation
        list.compact();

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

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

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

        // Insert and delete elements to create memory holes
        let indices = vec![
            list.push(SimpleTestElement::new(300, 1)),
            list.push(SimpleTestElement::new(300, 2)),
            list.push(SimpleTestElement::new(400, 3)),
            list.push(SimpleTestElement::new(300, 4)),
            list.push(SimpleTestElement::new(500, 5)),
        ];

        // Delete every other element
        list.remove_index(indices[0]); // Delete 1
        list.remove_index(indices[2]); // Delete 3
        list.remove_index(indices[4]); // Delete 5

        // Compact reorganizes memory
        list.compact();

        // Find hash 300 - should find elements 2 and 4
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(300) {
            found.push(*elem);
        }

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

    // ==================== Iterator consistency tests ====================

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

        // Insert elements
        for i in 0..3 {
            list.push(SimpleTestElement::new(50, i));
        }

        // Get same hash iterator multiple times
        for _iteration in 0..3 {
            let mut iter = list.find_hash(50);
            let mut count = 0;

            while let Some((_idx, _elem)) = iter.next() {
                count += 1;
            }

            assert_eq!(
                count, 3,
                "Each iteration should find same number of elements"
            );
        }
    }

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

        // Insert elements
        for i in 0..5 {
            list.push(SimpleTestElement::new(100, i));
        }

        // Get iterators
        let iter1 = list.find_hash(100);
        let iter2 = list.find_hash(100);

        // Both iterators should see same elements
        let count1 = iter1.count();
        let count2 = iter2.count();

        assert_eq!(count1, 5);
        assert_eq!(count2, 5);
    }

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

        // Insert multiple elements with same hash
        for i in 0..10 {
            list.push(SimpleTestElement::new(200, i));
        }

        // Partially consume iterator
        let mut iter = list.find_hash(200);
        for _ in 0..5 {
            assert!(iter.next().is_some());
        }

        // Create new iterator - should see all elements
        let new_iter = list.find_hash(200);
        let count = new_iter.count();
        assert_eq!(count, 10);
    }

    // ==================== Special construction tests ====================

    #[test]
    fn test_find_hash_from_vec() {
        let vec = vec![
            SimpleTestElement::new(42, 1),
            SimpleTestElement::new(42, 2),
            SimpleTestElement::new(100, 3),
            SimpleTestElement::new(42, 4),
        ];

        let list = HashList::from_vec(vec);

        // Find hash value 42
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(42) {
            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(&2));
        assert!(found_ids.contains(&4));
    }

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

        // First fill
        list.push(SimpleTestElement::new(100, 1));
        list.push(SimpleTestElement::new(200, 2));

        // Clear
        list.clear();
        assert_eq!(list.count(), 0);

        // Refill
        list.push(SimpleTestElement::new(300, 3));
        list.push(SimpleTestElement::new(100, 4));

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

        assert_eq!(found.len(), 1);
        assert_eq!(found[0].id, 4); // Should be the refilled element
    }

    // ==================== Performance tests ====================

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

        // Insert 1000 elements, 100 have target hash
        let target_hash = 500u32;
        for i in 0..1000 {
            let hash_value = if i % 10 == 0 { target_hash } else { i as u32 };
            list.push(SimpleTestElement::new(hash_value, i));
        }

        // Find target hash value
        let start = std::time::Instant::now();
        let found: Vec<_> = list.find_hash(target_hash).collect();
        let duration = start.elapsed();

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

        // Performance check - should complete in reasonable time
        println!("Time to find 100 matches in 1000 elements: {:?}", duration);
        assert!(duration.as_millis() < 100, "Find operation should be fast");
    }

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

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

        // Test various boundary hash values
        let boundary_values = [u32::MIN, 1, 2, u32::MAX - 1, u32::MAX];
        for (i, &hash_val) in boundary_values.iter().enumerate() {
            list.push(SimpleTestElement::new(hash_val, i as u32));
        }

        // Find each boundary value
        for (i, &hash_val) in boundary_values.iter().enumerate() {
            let mut found = Vec::new();
            for (_idx, elem) in list.find_hash(hash_val) {
                found.push(*elem);
            }
            assert_eq!(found.len(), 1);
            assert_eq!(found[0].value, hash_val);
            assert_eq!(found[0].id, i as u32);
        }
    }

    // ==================== Complex scenario tests ====================

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

        // Phase 1: Initial insertion
        for i in 0..20 {
            list.push(SimpleTestElement::new((i % 5) as u32 * 10, i));
        }

        // Phase 2: Find some elements
        let mut found_10 = Vec::new();
        for (_idx, elem) in list.find_hash(10) {
            found_10.push(*elem);
        }
        assert_eq!(found_10.len(), 4);

        // Phase 3: Delete some elements
        let indices: Vec<_> = list
            .iter()
            .enumerate()
            .filter_map(|(idx, (_, elem))| {
                if elem.value == 10 && elem.id % 2 == 0 {
                    Some(idx)
                } else {
                    None
                }
            })
            .collect();

        for &idx in &indices {
            list.remove_index(idx);
        }

        // Phase 4: Find after deletion
        let mut found_10_after = Vec::new();
        for (_idx, elem) in list.find_hash(10) {
            found_10_after.push(*elem);
        }
        assert_eq!(found_10_after.len(), 2);

        // Phase 5: Find again after compaction
        list.compact();
        let mut found_10_compact = Vec::new();
        for (_idx, elem) in list.find_hash(10) {
            found_10_compact.push(*elem);
        }
        assert_eq!(found_10_compact.len(), 2);
    }

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

        // Insert elements
        for i in 0..50 {
            list.push(SimpleTestElement::new(i % 10, i));
        }

        // Simulate multiple "threads" accessing find_hash
        let mut results = Vec::new();
        for hash_val in 0..10 {
            let mut found = Vec::new();
            for (_idx, elem) in list.find_hash(hash_val) {
                found.push(*elem);
            }
            results.push((hash_val, found.len()));
        }

        // Verify each hash value appears correct number of times
        for (hash_val, count) in results {
            let _expected_count = 50 / 10 + if 0 < 50 % 10 { 1 } else { 0 };
            // Each hash should appear roughly evenly
            assert!(
                count >= 4 && count <= 6,
                "Hash {} count {} not in expected range",
                hash_val,
                count
            );
        }
    }

    // ==================== Error handling and robustness tests ====================

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

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

        // Delete all elements
        for &idx in &indices {
            list.remove_index(idx);
        }

        assert_eq!(list.count(), 0);
        assert_eq!(list.count_deleted(), 3);
        assert_eq!(list.len(), 3);

        // Find should return empty result
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(777) {
            found.push(*elem);
        }
        assert_eq!(found.len(), 0);

        list.compact();
        assert_eq!(list.count_deleted(), 0);
        assert_eq!(list.len(), 0);
    }

    #[test]
    fn test_find_hash_after_shrink_to_fit() {
        let mut list = HashList::with_capacity(100, 1000);

        // Insert some elements
        for i in 0..10 {
            list.push(SimpleTestElement::new(888, i));
        }

        // Shrink to fit
        list.shrink_to_fit();

        // Find should still work
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(888) {
            found.push(*elem);
        }

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

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

        // Insert and immediately delete
        let idx = list.push(SimpleTestElement::new(999, 1));
        list.remove_index(idx);

        // Should be empty
        assert_eq!(list.count(), 0);

        // Find should return nothing
        let mut found = Vec::new();
        for (_idx, elem) in list.find_hash(999) {
            found.push(*elem);
        }

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