// Drop behavior tests
// Integrated Drop related tests from original hash_list.rs

use hash_link::{HashList, Hashable};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

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

    /// Test element, tracks drop calls
    #[derive(Debug, Clone)]
    struct TestElement {
        value: usize,
        drop_counter: Option<Arc<AtomicUsize>>,
    }

    impl PartialEq for TestElement {
        fn eq(&self, other: &Self) -> bool {
            self.value == other.value
        }
    }

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

    impl Drop for TestElement {
        fn drop(&mut self) {
            if let Some(counter) = &self.drop_counter {
                counter.fetch_add(1, Ordering::SeqCst);
            }
        }
    }

    #[test]
    fn test_hashlink_drop_releases_remaining_elements() {
        let drop_counter = Arc::new(AtomicUsize::new(0));

        // Create test elements
        let mut elements = Vec::new();
        for i in 0..5 {
            elements.push(TestElement {
                value: i,
                drop_counter: Some(Arc::clone(&drop_counter)),
            });
        }

        // Create HashList and add elements
        let mut list = HashList::new();
        let _ = list.push(elements[0].clone());
        let _ = list.push(elements[1].clone());
        let _ = list.push(elements[2].clone());
        let _ = list.push(elements[3].clone());
        let _ = list.push(elements[4].clone());

        // Remove some elements (these should not be dropped when list is dropped)
        assert!(list.remove_index(1)); // Remove element with value 1
        assert!(list.remove_index(3)); // Remove element with value 3

        // Elements 0, 2, 4 should remain in list
        assert_eq!(list.count(), 3);

        // Reset counter before dropping list
        drop_counter.store(0, Ordering::SeqCst);

        // Drop list - this should drop remaining elements (0, 2, 4)
        drop(list);

        // Should have dropped exactly 3 elements (0, 2, 4)
        assert_eq!(drop_counter.load(Ordering::SeqCst), 3);
    }

    #[test]
    fn test_hashlink_drop_does_not_double_drop_removed_elements() {
        let drop_counter = Arc::new(AtomicUsize::new(0));

        // Create test elements
        let mut elements = Vec::new();
        for i in 0..3 {
            elements.push(TestElement {
                value: i,
                drop_counter: Some(Arc::clone(&drop_counter)),
            });
        }

        // Create HashList and add elements
        let mut list = HashList::new();
        let _ = list.push(elements[0].clone());
        let _ = list.push(elements[1].clone());
        let _ = list.push(elements[2].clone());

        // Remove some elements
        assert!(list.remove_index(0)); // Remove first element
        assert!(list.remove_index(1)); // Remove second element (originally index 2)

        // Only element 2 should remain in list
        assert_eq!(list.count(), 1);

        // Reset counter before dropping list
        drop_counter.store(0, Ordering::SeqCst);

        // Drop list - this should only drop remaining element
        drop(list);

        // Should have dropped exactly 1 element (the remaining one)
        assert_eq!(drop_counter.load(Ordering::SeqCst), 1);
    }

    // ==================== Other important HashList operation tests ====================

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

        let _idx1 = list.push(TestElement {
            value: 100,
            drop_counter: None,
        });
        let idx2 = list.push(TestElement {
            value: 200,
            drop_counter: None,
        });
        let _idx3 = list.push(TestElement {
            value: 300,
            drop_counter: None,
        });

        // Remove by index
        assert!(list.remove_index(idx2));
        assert_eq!(list.count(), 2);
        assert_eq!(
            list.index_of(&TestElement {
                value: 200,
                drop_counter: None
            }),
            None
        );
        assert_eq!(list.get(idx2), None);

        // Remove invalid index
        assert!(!list.remove_index(999));
        assert!(!list.remove_index(idx2)); // already removed index
    }

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

        // Insert some elements
        for i in 0..5 {
            list.push(TestElement {
                value: i,
                drop_counter: None,
            });
        }

        let _original_len = list.len();

        // Remove some elements
        list.remove(&TestElement {
            value: 1,
            drop_counter: None,
        });
        list.remove(&TestElement {
            value: 3,
            drop_counter: None,
        });

        assert_eq!(list.count(), 3);
        assert_eq!(list.count_deleted(), 2);

        // Compact
        list.compact();

        assert_eq!(list.count(), 3);
        assert_eq!(list.count_deleted(), 0);
        assert_eq!(list.len(), 3); // Should be less than original length

        // Verify remaining elements
        assert!(
            list.index_of(&TestElement {
                value: 0,
                drop_counter: None
            })
            .is_some()
        );
        assert!(
            list.index_of(&TestElement {
                value: 2,
                drop_counter: None
            })
            .is_some()
        );
        assert!(
            list.index_of(&TestElement {
                value: 4,
                drop_counter: None
            })
            .is_some()
        );
        assert!(
            list.index_of(&TestElement {
                value: 1,
                drop_counter: None
            })
            .is_none()
        );
        assert!(
            list.index_of(&TestElement {
                value: 3,
                drop_counter: None
            })
            .is_none()
        );
    }

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

        // Insert enough elements to trigger resize
        for i in 0..10 {
            list.push(TestElement {
                value: i,
                drop_counter: None,
            });
        }

        // All elements should still be findable
        for i in 0..10 {
            assert!(
                list.index_of(&TestElement {
                    value: i,
                    drop_counter: None
                })
                .is_some()
            );
        }

        assert_eq!(list.count(), 10);
    }

    #[test]
    fn test_reserve() {
        let mut list = HashList::<TestElement>::new();
        let initial_capacity = list.capacity();
        list.reserve(10);
        // Capacity should be at least current element count + 10 (i.e., at least 10)
        assert!(list.capacity() >= 10);
        // Capacity may increase, or stay the same (if already sufficient)
        assert!(list.capacity() >= initial_capacity);
        // Ensure elements can still be inserted after operation
        list.push(TestElement {
            value: 1,
            drop_counter: None,
        });
        assert_eq!(list.count(), 1);
    }

    #[test]
    fn test_try_reserve() {
        let mut list = HashList::<TestElement>::new();
        // try_reserve should succeed
        assert!(list.try_reserve(10).is_ok());
        let capacity = list.capacity();
        // Try reserve again, capacity may already be sufficient
        assert!(list.try_reserve(5).is_ok());
        // Capacity may not change
        assert!(list.capacity() >= capacity);
    }

    #[test]
    fn test_shrink_to_fit() {
        let mut list = HashList::with_capacity(4, 10);
        // Insert some elements
        for i in 0..5 {
            list.push(TestElement {
                value: i,
                drop_counter: None,
            });
        }
        let capacity_before = list.capacity();
        assert!(capacity_before >= 10);
        // Remove some elements to create free space
        list.remove(&TestElement {
            value: 0,
            drop_counter: None,
        });
        list.remove(&TestElement {
            value: 1,
            drop_counter: None,
        });
        // Compact removed elements
        list.compact();
        // Shrink to fit
        list.shrink_to_fit();
        let capacity_after = list.capacity();
        // After shrinking, capacity should be less than or equal to before, and at least element count
        assert!(capacity_after <= capacity_before);
        assert!(capacity_after >= list.count());
    }

    #[test]
    fn test_iter_with_index() {
        let mut list = HashList::new();
        list.push(TestElement {
            value: 10,
            drop_counter: None,
        });
        list.push(TestElement {
            value: 20,
            drop_counter: None,
        });
        list.push(TestElement {
            value: 30,
            drop_counter: None,
        });

        let mut iter = list.iter();
        assert_eq!(
            iter.next(),
            Some((
                0,
                &TestElement {
                    value: 10,
                    drop_counter: None
                }
            ))
        );
        assert_eq!(
            iter.next(),
            Some((
                1,
                &TestElement {
                    value: 20,
                    drop_counter: None
                }
            ))
        );
        assert_eq!(
            iter.next(),
            Some((
                2,
                &TestElement {
                    value: 30,
                    drop_counter: None
                }
            ))
        );
        assert_eq!(iter.next(), None);

        // After removing element, iterator should skip removed elements
        list.remove(&TestElement {
            value: 20,
            drop_counter: None,
        });
        let mut iter2 = list.iter();
        assert_eq!(
            iter2.next(),
            Some((
                0,
                &TestElement {
                    value: 10,
                    drop_counter: None
                }
            ))
        );
        assert_eq!(
            iter2.next(),
            Some((
                2,
                &TestElement {
                    value: 30,
                    drop_counter: None
                }
            ))
        );
        assert_eq!(iter2.next(), None);
    }
}
