//! Panic-freedom smoke tests for prelude utility functions.
//!
//! **Scope (read this before relying on results):** this module
//! exercises the prelude's general-purpose helpers (hex encoding,
//! UUID generation, error-path branches, concurrent access) and
//! asserts they do not panic on representative inputs. It is NOT a
//! memory-safety analyzer — Rust's type system + the workspace-wide
//! `unsafe_code = "forbid"` lint already guarantee memory safety for
//! every line of source in this crate. The module's value is
//! "regression-catch the helpers' panic posture under unusual
//! inputs," not "audit memory-safety."
//!
//! Use this module's `UtilityMemorySafetyTester` when you want a
//! coarse sanity check that a new utility function does not panic on
//! the standard suite of edge-case inputs. Do not use the results as
//! evidence of memory safety beyond what `#![forbid(unsafe_code)]` +
//! the Rust borrow checker already provide.

#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]

use crate::prelude::error::{LatticeArcError, Result};

/// Memory safety tester for utilities.
///
/// Provides comprehensive memory safety testing for common utility operations
/// used throughout the LatticeArc library.
pub struct UtilityMemorySafetyTester;

impl Default for UtilityMemorySafetyTester {
    fn default() -> Self {
        Self::new()
    }
}

impl UtilityMemorySafetyTester {
    /// Creates a new memory safety tester instance.
    #[must_use]
    pub fn new() -> Self {
        Self {}
    }

    /// Test memory safety of utility operations.
    ///
    /// # Errors
    ///
    /// Returns an error if any memory safety test fails validation.
    pub fn test_memory_safety_succeeds(&self) -> Result<()> {
        tracing::info!("Testing utility memory safety");

        // Test hex operations memory safety
        let invalid_hex = ["g", "gg", "invalid", "z", "G", "xyz", "123", "abcdefg"];
        for &hex_str in &invalid_hex {
            let result = hex::decode(hex_str);
            if result.is_ok() {
                return Err(LatticeArcError::ValidationError {
                    message: format!("Hex string '{hex_str}' should be invalid"),
                });
            }
        }
        // Empty string is actually valid (decodes to empty bytes)
        if hex::decode("").is_err() {
            return Err(LatticeArcError::ValidationError {
                message: "Empty hex string should be valid".to_string(),
            });
        }

        // Test UUID operations memory safety
        for _ in 0..100 {
            let uuid = uuid::Uuid::new_v4();
            if uuid.is_nil() {
                return Err(LatticeArcError::ValidationError {
                    message: "UUID should not be nil".to_string(),
                });
            }
            let uuid_str = uuid.to_string();
            if uuid_str.len() != 36 {
                return Err(LatticeArcError::ValidationError {
                    message: format!("UUID string should be 36 chars, got {}", uuid_str.len()),
                });
            }
        }

        // Test error operations memory safety
        let test_errors = vec![
            LatticeArcError::InvalidInput("test".to_string()),
            LatticeArcError::IoError(
                std::io::Error::new(std::io::ErrorKind::NotFound, "test").to_string(),
            ),
        ];

        for error in test_errors {
            let _display = format!("{error}");
            let _debug = format!("{:?}", error);
            // Should not panic
        }

        tracing::info!("Utility memory safety tests passed");
        Ok(())
    }

    /// Test hex operations memory safety.
    ///
    /// # Errors
    ///
    /// Returns an error if hex encoding/decoding memory safety tests fail.
    pub fn test_hex_memory_safety_succeeds(&self) -> Result<()> {
        // Test invalid hex strings - hex decode requires even length and valid hex chars
        let invalid_hex = ["g", "gg", "invalid", "z", "G", "xyz", "123", "abcdefg"];
        for &hex_str in &invalid_hex {
            let result = hex::decode(hex_str);
            if result.is_ok() {
                return Err(LatticeArcError::ValidationError {
                    message: format!("Hex string '{hex_str}' should be invalid"),
                });
            }
        }

        // Empty string is actually valid (decodes to empty bytes)
        if hex::decode("").is_err() {
            return Err(LatticeArcError::ValidationError {
                message: "Empty hex string should be valid".to_string(),
            });
        }

        Ok(())
    }

    /// Test UUID operations memory safety.
    ///
    /// # Errors
    ///
    /// Returns an error if UUID parsing fails during memory safety validation.
    pub fn test_uuid_memory_safety_succeeds(&self) -> Result<()> {
        // Test UUID generation (should never panic)
        for _ in 0..100 {
            let uuid = uuid::Uuid::new_v4();
            if uuid.is_nil() {
                return Err(LatticeArcError::ValidationError {
                    message: "UUID should not be nil".to_string(),
                });
            }
            if uuid.get_version_num() != 4 {
                return Err(LatticeArcError::ValidationError {
                    message: format!("UUID version should be 4, got {}", uuid.get_version_num()),
                });
            }

            // Test string conversion
            let uuid_str = uuid.to_string();
            if uuid_str.len() != 36 {
                return Err(LatticeArcError::ValidationError {
                    message: format!("UUID string should be 36 chars, got {}", uuid_str.len()),
                });
            }

            // Test parsing back
            let parsed = uuid::Uuid::parse_str(&uuid_str)?;
            if parsed != uuid {
                return Err(LatticeArcError::ValidationError {
                    message: "Parsed UUID should match original".to_string(),
                });
            }
        }

        Ok(())
    }

    /// Test error handling memory safety.
    ///
    /// # Errors
    ///
    /// Returns an error if error serialization/deserialization fails.
    pub fn test_error_memory_safety_fails(&self) -> Result<()> {
        // Test various error types for serialization safety
        let errors = vec![
            LatticeArcError::InvalidInput("test".to_string()),
            LatticeArcError::NetworkError("connection failed".to_string()),
            LatticeArcError::IoError("file not found".to_string()),
            LatticeArcError::EncryptionError("cipher failed".to_string()),
        ];

        for error in errors {
            // `LatticeArcError` is serialize-only by design. The memory-
            // safety check here is that the outbound serialization path
            // does not panic or leak; round-trip-via-deserialize is not
            // possible (no Deserialize derive — see the type-level docs).
            let json = serde_json::to_string(&error)?;
            if json.is_empty() {
                return Err(LatticeArcError::ValidationError {
                    message: "Error JSON serialization produced empty output".to_string(),
                });
            }
            let _value: serde_json::Value = serde_json::from_str(&json)?;
        }

        Ok(())
    }

    /// Test concurrent access safety.
    ///
    /// # Errors
    ///
    /// Returns an error if a thread panics or concurrent operations fail.
    pub fn test_concurrent_safety_succeeds(&self) -> Result<()> {
        use std::thread;

        let mut handles = vec![];

        // Spawn threads performing utility operations
        for i in 0..4 {
            let handle: thread::JoinHandle<Result<()>> = thread::spawn(move || {
                for j in 0..100 {
                    // Test hex operations
                    let data = format!("thread_{}_{}", i, j).into_bytes();
                    let encoded = hex::encode(&data);
                    let _decoded = hex::decode(&encoded)?;

                    // Test UUID generation
                    let _uuid = uuid::Uuid::new_v4();

                    // Test error handling
                    let _error = LatticeArcError::InvalidInput(format!("test_{}_{}", i, j));
                }
                Ok::<(), LatticeArcError>(())
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle
                .join()
                // Thread join errors are Box<dyn Any> which can't be formatted,
                // but we can try to downcast to common panic types
                .map_err(|e| {
                    let msg = e
                        .downcast_ref::<&str>()
                        .map(ToString::to_string)
                        .or_else(|| e.downcast_ref::<String>().cloned())
                        .unwrap_or_else(|| "Unknown panic payload".to_string());
                    LatticeArcError::ConcurrencyError(format!("Thread panic: {msg}"))
                })??;
        }

        Ok(())
    }
}

/// Leak detector for utilities.
///
/// Monitors utility operations for potential resource leaks by
/// repeatedly executing operations and tracking success/failure rates.
pub struct UtilityLeakDetector;

impl Default for UtilityLeakDetector {
    fn default() -> Self {
        Self::new()
    }
}

impl UtilityLeakDetector {
    /// Creates a new leak detector instance.
    #[must_use]
    pub fn new() -> Self {
        Self
    }

    /// Monitor for resource leaks in utility operations.
    ///
    /// Executes the provided operation 1000 times and tracks success/failure
    /// rates to detect potential resource leaks or instability.
    ///
    /// # Errors
    ///
    /// Returns an error if **any** invocation of the monitored operation
    /// fails. All 1000 iterations still run (so the log reflects the
    /// full error rate of the sample) and the *first* observed failure
    /// is returned. `monitor_leaks(...).is_ok()` is therefore a true
    /// correctness signal: it is `Ok` only when every iteration
    /// succeeded.
    pub fn monitor_leaks<F>(&self, operation: F) -> Result<()>
    where
        F: Fn() -> Result<()>,
    {
        let mut success_count = 0;
        let mut error_count = 0;
        let mut first_error: Option<LatticeArcError> = None;
        for i in 0..1000 {
            match operation() {
                #[expect(
                    clippy::arithmetic_side_effects,
                    reason = "arithmetic bounded by callsite invariants; overflow impossible at this site"
                )]
                Ok(_) => success_count += 1,
                #[expect(
                    clippy::arithmetic_side_effects,
                    reason = "arithmetic bounded by callsite invariants; overflow impossible at this site"
                )]
                Err(e) => {
                    error_count += 1;
                    tracing::warn!("Operation {} failed: {}", i, e);
                    if first_error.is_none() {
                        first_error = Some(e);
                    }
                }
            }
        }
        tracing::info!(
            "Leak detection completed: {} successes, {} errors out of 1000 operations",
            success_count,
            error_count
        );
        if let Some(e) = first_error { Err(e) } else { Ok(()) }
    }
}

#[cfg(test)]
#[expect(clippy::panic, reason = "test code: variant-mismatch panics are the assertion path")]
mod tests {
    use super::*;

    #[test]
    fn test_memory_safety_passes_for_utility_tester_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_concurrent_safety_passes_for_utility_tester_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_concurrent_safety_succeeds().is_ok());
    }

    #[test]
    fn test_hex_memory_safety_passes_for_utility_tester_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_hex_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_uuid_memory_safety_passes_for_utility_tester_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_uuid_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_error_memory_safety_passes_for_utility_tester_fails() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_error_memory_safety_fails().is_ok());
    }

    #[test]
    fn test_leak_detector_monitor_leaks_succeeds() {
        let detector = UtilityLeakDetector::new();

        let result = detector.monitor_leaks(|| {
            let _data = vec![0u8; 100];
            let _encoded = hex::encode(&_data);
            Ok(())
        });

        assert!(result.is_ok());
    }

    #[test]
    fn test_memory_safety_tester_default_passes_memory_safety_succeeds() {
        let tester = UtilityMemorySafetyTester;
        assert!(tester.test_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_leak_detector_default_monitor_leaks_succeeds() {
        let detector = UtilityLeakDetector;
        let result = detector.monitor_leaks(|| Ok(()));
        assert!(result.is_ok());
    }

    #[test]
    fn test_leak_detector_with_failing_operation_fails() {
        let detector = UtilityLeakDetector::new();
        // Always-failing operation must surface the first error and
        // preserve the variant — a `bool`-shaped pass/fail would be a
        // weaker contract than this assertion expects.
        let result = detector.monitor_leaks(|| {
            Err(LatticeArcError::InvalidInput("intentional failure".to_string()))
        });
        assert!(result.is_err());
        match result {
            Err(LatticeArcError::InvalidInput(msg)) => {
                assert!(msg.contains("intentional failure"));
            }
            Err(other) => panic!("unexpected error variant: {other:?}"),
            Ok(()) => panic!("expected Err, got Ok"),
        }
    }

    #[test]
    fn test_leak_detector_with_intermittent_failures_fails() {
        let detector = UtilityLeakDetector::new();
        let counter = std::sync::atomic::AtomicUsize::new(0);
        // Single intermittent failure in the 1000-iteration sample is
        // enough to flip the return: `monitor_leaks` is Ok ↔ every
        // iteration succeeded.
        let result = detector.monitor_leaks(|| {
            let val = counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            if val.is_multiple_of(3) {
                Err(LatticeArcError::InvalidInput("every third fails".to_string()))
            } else {
                Ok(())
            }
        });
        assert!(result.is_err());
    }

    #[test]
    fn test_memory_safety_tester_new_and_default_are_equivalent_succeeds() {
        let t1 = UtilityMemorySafetyTester::new();
        let t2 = UtilityMemorySafetyTester;
        // Both should work identically
        assert!(t1.test_hex_memory_safety_succeeds().is_ok());
        assert!(t2.test_hex_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_leak_detector_new_and_default_are_equivalent_succeeds() {
        let d1 = UtilityLeakDetector::new();
        let d2 = UtilityLeakDetector;
        assert!(d1.monitor_leaks(|| Ok(())).is_ok());
        assert!(d2.monitor_leaks(|| Ok(())).is_ok());
    }

    #[test]
    fn test_uuid_memory_safety_standalone_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_uuid_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_error_memory_safety_standalone_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_error_memory_safety_fails().is_ok());
    }

    #[test]
    fn test_hex_roundtrip_large_data_roundtrip() {
        let tester = UtilityMemorySafetyTester::new();
        // Ensure large data hex encode/decode works
        let data = vec![0xABu8; 4096];
        let encoded = hex::encode(&data);
        let decoded = hex::decode(&encoded);
        assert!(decoded.is_ok());
        if let Ok(ref bytes) = decoded {
            assert_eq!(bytes, &data);
        }
        // Basic tester should still pass
        assert!(tester.test_hex_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_concurrent_safety_produces_no_errors_fails() {
        let tester = UtilityMemorySafetyTester::new();
        // Run concurrent safety test multiple times to increase confidence
        for _ in 0..3 {
            assert!(tester.test_concurrent_safety_succeeds().is_ok());
        }
    }

    #[test]
    fn test_leak_detector_monitor_many_successes_runs_1000_iterations_succeeds() {
        let detector = UtilityLeakDetector::new();
        let counter = std::sync::atomic::AtomicUsize::new(0);
        let result = detector.monitor_leaks(|| {
            counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            Ok(())
        });
        assert!(result.is_ok());
        // Should have run 1000 iterations
        assert_eq!(counter.load(std::sync::atomic::Ordering::SeqCst), 1000);
    }

    // ---- Coverage: monitor_leaks edge cases ----

    #[test]
    fn test_monitor_leaks_with_allocating_operation_succeeds() {
        let detector = UtilityLeakDetector::new();
        let result = detector.monitor_leaks(|| {
            // Allocate and free memory within the operation
            let data = vec![0xABu8; 256];
            let encoded = hex::encode(&data);
            let _decoded = hex::decode(&encoded);
            Ok(())
        });
        assert!(result.is_ok());
    }

    #[test]
    fn test_monitor_leaks_sequential_runs_all_succeed_succeeds() {
        let detector = UtilityLeakDetector::new();
        // Run monitor_leaks multiple times sequentially
        for _ in 0..3 {
            let result = detector.monitor_leaks(|| Ok(()));
            assert!(result.is_ok());
        }
    }

    #[test]
    fn test_utility_memory_safety_tester_default_passes_memory_safety_succeeds() {
        let tester = UtilityMemorySafetyTester::new();
        assert!(tester.test_memory_safety_succeeds().is_ok());
    }

    #[test]
    fn test_hex_roundtrip_empty_and_single_byte_roundtrip() {
        // Empty data
        let empty: Vec<u8> = vec![];
        let encoded = hex::encode(&empty);
        assert_eq!(encoded, "");
        let decoded = hex::decode(&encoded);
        assert!(decoded.is_ok());
        if let Ok(ref bytes) = decoded {
            assert!(bytes.is_empty());
        }

        // Single byte
        let single = vec![0xFFu8];
        let encoded = hex::encode(&single);
        assert_eq!(encoded, "ff");
        let decoded = hex::decode(&encoded);
        assert!(decoded.is_ok());
        if let Ok(ref bytes) = decoded {
            assert_eq!(bytes, &single);
        }
    }
}
