//! Atomic operations and utilities for lock-free programming
//!
//! This module provides high-performance atomic primitives optimized for
//! high-frequency trading systems with proper memory ordering guarantees.

use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};

/// Sequence generator for monotonic ordering of operations
#[repr(align(64))] // Cache line alignment to prevent false sharing
/// SequenceGenerator
///
/// Auto-generated documentation placeholder - enhance with specifics
#[derive(Debug)]
pub struct SequenceGenerator {
    current: AtomicU64,
}

impl SequenceGenerator {
    /// Create a new sequence generator starting at 1
    #[must_use]
    pub const fn new() -> Self {
        Self {
            current: AtomicU64::new(1),
        }
    }

    /// Create a new sequence generator with custom starting value
    #[must_use]
    pub const fn new_with_start(start: u64) -> Self {
        Self {
            current: AtomicU64::new(start),
        }
    }

    /// Get the next sequence number (atomic increment)
    #[inline(always)]
    pub fn next(&self) -> u64 {
        self.current.fetch_add(1, Ordering::Relaxed)
    }

    /// Get current sequence number without incrementing
    #[inline(always)]
    pub fn current(&self) -> u64 {
        self.current.load(Ordering::Relaxed)
    }

    /// Reset sequence to specific value
    #[inline(always)]
    pub fn reset(&self, value: u64) {
        self.current.store(value, Ordering::Relaxed);
    }
}

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

/// High-performance atomic flag for signaling
#[repr(align(64))] // Cache line alignment
/// AtomicFlag
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct AtomicFlag {
    flag: AtomicBool,
}

impl AtomicFlag {
    /// Create a new atomic flag (initially `false`)
    #[must_use]
    pub const fn new() -> Self {
        Self {
            flag: AtomicBool::new(false),
        }
    }

    /// Create a new atomic flag with initial value
    #[must_use]
    pub const fn new_with(initial: bool) -> Self {
        Self {
            flag: AtomicBool::new(initial),
        }
    }

    /// Set the flag to `true`
    #[inline(always)]
    pub fn set(&self) {
        self.flag.store(true, Ordering::Release);
    }

    /// Clear the flag (set to `false`)
    #[inline(always)]
    pub fn clear(&self) {
        self.flag.store(false, Ordering::Release);
    }

    /// Check if flag is set (non-blocking)
    #[inline(always)]
    pub fn is_set(&self) -> bool {
        self.flag.load(Ordering::Acquire)
    }

    /// Test and set the flag atomically
    /// Returns the previous value
    #[inline(always)]
    pub fn test_and_set(&self) -> bool {
        self.flag.swap(true, Ordering::AcqRel)
    }

    /// Compare and swap the flag value
    #[inline(always)]
    pub fn compare_and_swap(&self, current: bool, new: bool) -> bool {
        self.flag
            .compare_exchange(current, new, Ordering::AcqRel, Ordering::Acquire)
            .unwrap_or_else(|x| x)
    }
}

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

impl fmt::Debug for AtomicFlag {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("AtomicFlag")
            .field("flag", &self.is_set())
            .finish()
    }
}

/// Atomic metrics collector for performance monitoring
#[repr(align(64))] // Cache line alignment
/// AtomicMetrics
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct AtomicMetrics {
    operations_count: AtomicU64,
    total_latency_ns: AtomicU64,
    min_latency_ns: AtomicU64,
    max_latency_ns: AtomicU64,
    errors_count: AtomicU64,
    bytes_processed: AtomicU64,
    start_time_ns: AtomicU64,
}

impl AtomicMetrics {
    /// Create new atomic metrics collector
    pub fn new() -> Self {
        Self {
            operations_count: AtomicU64::new(0),
            total_latency_ns: AtomicU64::new(0),
            min_latency_ns: AtomicU64::new(u64::MAX),
            max_latency_ns: AtomicU64::new(0),
            errors_count: AtomicU64::new(0),
            bytes_processed: AtomicU64::new(0),
            start_time_ns: AtomicU64::new(
                SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap_or_default()
                    .as_nanos() as u64,
            ),
        }
    }

    /// Record operation time (alias for record_operation for API compatibility)
    #[inline(always)]
    pub fn record_operation_time(&self, elapsed_ns: u64) {
        self.record_operation(elapsed_ns);
    }

    /// Get average operation time in nanoseconds
    #[inline(always)]
    pub fn avg_operation_time_ns(&self) -> u64 {
        let ops = self.operations_count.load(Ordering::Relaxed);
        if ops > 0 {
            self.total_latency_ns.load(Ordering::Relaxed) / ops
        } else {
            0
        }
    }

    /// Get current operations per second based on elapsed time since creation
    #[inline(always)]
    pub fn operations_per_second(&self) -> f64 {
        let ops = self.operations_count.load(Ordering::Relaxed);
        let start_ns = self.start_time_ns.load(Ordering::Relaxed);

        let now_ns = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_nanos() as u64;

        let elapsed_ns = now_ns.saturating_sub(start_ns);
        let elapsed_secs = {
            let divisor = 1_000_000_000.0_f64;
            if !divisor.is_finite() {
                return 0.0;
            }
            elapsed_ns as f64 / divisor
        };

        if elapsed_secs > 0.0 && elapsed_secs.is_finite() {
            let result = ops as f64 / elapsed_secs;
            if result.is_finite() {
                result
            } else {
                0.0
            }
        } else {
            0.0
        }
    }

    /// Get total number of operations recorded
    #[inline(always)]
    pub fn total_operations(&self) -> u64 {
        self.operations_count.load(Ordering::Relaxed)
    }

    /// Record a successful operation with latency
    #[inline(always)]
    pub fn record_operation(&self, latency_ns: u64) {
        self.operations_count.fetch_add(1, Ordering::Relaxed);
        self.total_latency_ns
            .fetch_add(latency_ns, Ordering::Relaxed);

        // Update min latency
        #[allow(clippy::infinite_loop)] // CAS retry loop: exits via break on successful compare_exchange or condition
        loop {
            let current_min = self.min_latency_ns.load(Ordering::Relaxed);
            if latency_ns >= current_min {
                break;
            }
            if self
                .min_latency_ns
                .compare_exchange_weak(
                    current_min,
                    latency_ns,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                )
                .is_ok()
            {
                break;
            }
        }

        // Update max latency
        #[allow(clippy::infinite_loop)] // CAS retry loop: exits via break on successful compare_exchange or condition
        loop {
            let current_max = self.max_latency_ns.load(Ordering::Relaxed);
            if latency_ns <= current_max {
                break;
            }
            if self
                .max_latency_ns
                .compare_exchange_weak(
                    current_max,
                    latency_ns,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                )
                .is_ok()
            {
                break;
            }
        }
    }

    /// Record an error
    #[inline(always)]
    pub fn record_error(&self) {
        self.errors_count.fetch_add(1, Ordering::Relaxed);
    }

    /// Record bytes processed
    #[inline(always)]
    pub fn record_bytes(&self, bytes: u64) {
        self.bytes_processed.fetch_add(bytes, Ordering::Relaxed);
    }

    /// Get current metrics snapshot
    pub fn snapshot(&self) -> MetricsSnapshot {
        let ops = self.operations_count.load(Ordering::Relaxed);
        let total_lat = self.total_latency_ns.load(Ordering::Relaxed);

        MetricsSnapshot {
            operations_count: ops,
            avg_latency_ns: if ops > 0 { total_lat / ops } else { 0 },
            min_latency_ns: self.min_latency_ns.load(Ordering::Relaxed),
            max_latency_ns: self.max_latency_ns.load(Ordering::Relaxed),
            errors_count: self.errors_count.load(Ordering::Relaxed),
            bytes_processed: self.bytes_processed.load(Ordering::Relaxed),
            operations_per_second: 0.0, // Calculated externally with time delta
        }
    }

    /// Reset all metrics
    pub fn reset(&self) {
        self.operations_count.store(0, Ordering::Relaxed);
        self.total_latency_ns.store(0, Ordering::Relaxed);
        self.min_latency_ns.store(u64::MAX, Ordering::Relaxed);
        self.max_latency_ns.store(0, Ordering::Relaxed);
        self.errors_count.store(0, Ordering::Relaxed);
        self.bytes_processed.store(0, Ordering::Relaxed);
        // Reset start time to current time
        self.start_time_ns.store(
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_nanos() as u64,
            Ordering::Relaxed,
        );
    }
}

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

impl fmt::Debug for AtomicMetrics {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let snapshot = self.snapshot();
        f.debug_struct("AtomicMetrics")
            .field("operations_count", &snapshot.operations_count)
            .field("avg_latency_ns", &snapshot.avg_latency_ns)
            .field("min_latency_ns", &snapshot.min_latency_ns)
            .field("max_latency_ns", &snapshot.max_latency_ns)
            .field("errors_count", &snapshot.errors_count)
            .field("bytes_processed", &snapshot.bytes_processed)
            .finish()
    }
}

/// Snapshot of metrics at a point in time
#[derive(Debug, Clone)]
/// MetricsSnapshot
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct MetricsSnapshot {
    /// Operations Count
    pub operations_count: u64,
    /// Avg Latency Ns
    pub avg_latency_ns: u64,
    /// Min Latency Ns
    pub min_latency_ns: u64,
    /// Max Latency Ns
    pub max_latency_ns: u64,
    /// Errors Count
    pub errors_count: u64,
    /// Bytes Processed
    pub bytes_processed: u64,
    /// Operations Per Second
    pub operations_per_second: f64,
}

impl MetricsSnapshot {
    /// Calculate operations per second given time duration
    #[must_use]
    pub fn with_duration(mut self, duration_secs: f64) -> Self {
        self.operations_per_second = if duration_secs > 0.0 {
            let result = self.operations_count as f64 / duration_secs;
            if result.is_finite() {
                result
            } else {
                0.0
            }
        } else {
            0.0
        };
        self
    }

    /// Calculate throughput in MB/s
    #[must_use]
    pub fn throughput_mbps(&self, duration_secs: f64) -> f64 {
        if duration_secs > 0.0 {
            let mb = self.bytes_processed as f64 / (1024.0 * 1024.0);
            let result = mb / duration_secs;
            if result.is_finite() {
                result
            } else {
                0.0
            }
        } else {
            0.0
        }
    }

    /// Calculate error rate as percentage
    #[must_use]
    pub fn error_rate(&self) -> f64 {
        if self.operations_count > 0 {
            let rate = self.errors_count as f64 / self.operations_count as f64;
            let result = rate * 100.0;
            if result.is_finite() {
                result
            } else {
                0.0
            }
        } else {
            0.0
        }
    }
}

/// Memory fence operations for explicit ordering control
pub mod memory_fence {
    use std::sync::atomic::{fence, Ordering};

    /// Full memory barrier (acquire + release)
    #[inline(always)]
    pub fn full() {
        fence(Ordering::SeqCst);
    }

    /// Acquire memory barrier
    #[inline(always)]
    pub fn acquire() {
        fence(Ordering::Acquire);
    }

    /// Release memory barrier
    #[inline(always)]
    pub fn release() {
        fence(Ordering::Release);
    }

    /// Acquire-Release memory barrier
    #[inline(always)]
    pub fn acq_rel() {
        fence(Ordering::AcqRel);
    }
}

// Removed pub use - use memory_fence::full directly where needed

#[cfg(test)]
#[allow(clippy::indexing_slicing)]
mod tests {
    use super::*;
    use std::sync::Arc;
    use std::thread;
    use std::time::{Duration, Instant};

    #[test]
    fn test_sequence_generator() {
        let gen = SequenceGenerator::new();

        assert_eq!(gen.current(), 1);
        assert_eq!(gen.next(), 1);
        assert_eq!(gen.next(), 2);
        assert_eq!(gen.current(), 3);

        gen.reset(100);
        assert_eq!(gen.current(), 100);
        assert_eq!(gen.next(), 100);
    }

    #[test]
    fn test_sequence_generator_concurrent() {
        let gen = Arc::new(SequenceGenerator::new());
        let num_threads = 8;
        let increments_per_thread = 1000;

        let mut handles = Vec::new();

        for _ in 0..num_threads {
            let gen_clone = Arc::clone(&gen);
            let handle = thread::spawn(move || {
                let mut sequences = Vec::new();
                for _ in 0..increments_per_thread {
                    sequences.push(gen_clone.next());
                }
                sequences
            });
            handles.push(handle);
        }

        let mut all_sequences = Vec::new();
        for handle in handles {
            let sequences = handle.join().expect("Thread failed");
            all_sequences.extend(sequences);
        }

        // Verify all sequences are unique
        all_sequences.sort_unstable();
        for window in all_sequences.windows(2) {
            assert_ne!(window[0], window[1], "Duplicate sequence found");
        }

        assert_eq!(all_sequences.len(), num_threads * increments_per_thread);
    }

    #[test]
    fn test_atomic_flag() {
        let flag = AtomicFlag::new();

        assert!(!flag.is_set());

        flag.set();
        assert!(flag.is_set());

        assert!(flag.test_and_set()); // Should return true (was set)
        assert!(flag.is_set()); // Should still be set

        flag.clear();
        assert!(!flag.is_set());

        assert!(!flag.test_and_set()); // Should return false (was clear)
        assert!(flag.is_set()); // Should now be set
    }

    #[test]
    fn test_atomic_flag_concurrent() {
        let flag = Arc::new(AtomicFlag::new());
        let num_threads = 10;

        let mut handles = Vec::new();

        for thread_id in 0..num_threads {
            let flag_clone = Arc::clone(&flag);
            let handle = thread::spawn(move || {
                // Each thread tries to be the first to set the flag
                let was_first = !flag_clone.test_and_set();
                (thread_id, was_first)
            });
            handles.push(handle);
        }

        let results: Vec<_> = handles
            .into_iter()
            .map(|h| h.join().expect("Thread failed"))
            .collect();

        // Exactly one thread should have been first
        let first_count = results.iter().filter(|(_, was_first)| *was_first).count();
        assert_eq!(first_count, 1);

        // Flag should be set
        assert!(flag.is_set());
    }

    #[test]
    fn test_atomic_metrics() {
        let metrics = AtomicMetrics::new();

        // Record some operations
        metrics.record_operation(100);
        metrics.record_operation(200);
        metrics.record_operation(50);
        metrics.record_error();
        metrics.record_bytes(1024);

        let snapshot = metrics.snapshot();

        assert_eq!(snapshot.operations_count, 3);
        assert_eq!(snapshot.avg_latency_ns, (100 + 200 + 50) / 3);
        assert_eq!(snapshot.min_latency_ns, 50);
        assert_eq!(snapshot.max_latency_ns, 200);
        assert_eq!(snapshot.errors_count, 1);
        assert_eq!(snapshot.bytes_processed, 1024);

        // Test error rate calculation
        assert!((snapshot.error_rate() - 33.333).abs() < 0.1);
    }

    #[test]
    fn test_atomic_metrics_concurrent() {
        let metrics = Arc::new(AtomicMetrics::new());
        let num_threads = 8;
        let ops_per_thread = 1000;

        let start_time = Instant::now();
        let mut handles = Vec::new();

        for _ in 0..num_threads {
            let metrics_clone = Arc::clone(&metrics);
            let handle = thread::spawn(move || {
                for i in 0..ops_per_thread {
                    let latency = 100 + (i % 100) as u64; // Varying latency
                    metrics_clone.record_operation(latency);

                    if i % 100 == 0 {
                        metrics_clone.record_error();
                    }

                    metrics_clone.record_bytes(64);
                }
            });
            handles.push(handle);
        }

        for handle in handles {
            handle.join().expect("Thread failed");
        }

        let duration = start_time.elapsed();
        let snapshot = metrics.snapshot().with_duration(duration.as_secs_f64());

        assert_eq!(
            snapshot.operations_count,
            (num_threads * ops_per_thread) as u64
        );
        assert_eq!(
            snapshot.errors_count,
            (num_threads * (ops_per_thread / 100)) as u64
        );
        assert_eq!(
            snapshot.bytes_processed,
            (num_threads * ops_per_thread * 64) as u64
        );

        println!("Performance: {:.0} ops/sec", snapshot.operations_per_second);
        println!(
            "Throughput: {:.2} MB/s",
            snapshot.throughput_mbps(duration.as_secs_f64())
        );
        println!("Error rate: {:.2}%", snapshot.error_rate());

        // Verify reasonable performance
        assert!(snapshot.operations_per_second > 100_000.0);
    }

    #[test]
    fn test_memory_fences() {
        // Memory fences should not panic or cause issues
        memory_fence::acquire();
        memory_fence::release();
        memory_fence::acq_rel();
        memory_fence::full();

        // Test that fences work in concurrent context
        let flag = Arc::new(AtomicFlag::new());
        let flag_clone = Arc::clone(&flag);

        let handle = thread::spawn(move || {
            thread::sleep(Duration::from_millis(10));
            flag_clone.set();
            memory_fence::release(); // Ensure visibility
        });

        // Wait for flag to be set
        while !flag.is_set() {
            memory_fence::acquire(); // Ensure we see updates
            thread::yield_now();
        }

        handle.join().expect("Thread failed");
    }
}
