//! Async Trigger Executor for Post-Write Triggers
//!
//! Provides asynchronous execution of post-write triggers to avoid blocking
//! the main write path. Uses WriteQueue pattern from storage/concurrent.

use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};

use parking_lot::RwLock;
use tokio::sync::mpsc;

use super::types::{TriggerContext, TriggerPhase, DeferredAction};
use super::registry::RustTrigger;
use crate::types::MemoryNode;

/// Configuration for async trigger executor
#[derive(Debug, Clone)]
pub struct AsyncTriggerConfig {
    /// Maximum number of pending trigger requests
    pub queue_capacity: usize,
    
    /// Batch size for processing triggers
    pub batch_size: usize,
    
    /// Maximum time to wait before flushing a partial batch (ms)
    pub flush_interval_ms: u64,
    
    /// Timeout for sending to queue (ms), 0 = no timeout
    pub send_timeout_ms: u64,
    
    /// Number of worker tasks
    pub worker_count: usize,
    
    /// Queue fill ratio at which slowdown begins (0.0 - 1.0)
    pub queue_low_watermark: f64,
    
    /// Queue fill ratio at which severe slowdown applies (0.0 - 1.0)  
    pub queue_high_watermark: f64,
    
    /// Queue fill ratio at which requests are rejected (0.0 - 1.0)
    pub reject_threshold: f64,
}

impl Default for AsyncTriggerConfig {
    fn default() -> Self {
        Self {
            queue_capacity: 10_000,
            batch_size: 100,
            flush_interval_ms: 50,
            send_timeout_ms: 1000,
            worker_count: 2,
            queue_low_watermark: 0.5,
            queue_high_watermark: 0.8,
            reject_threshold: 0.95,
        }
    }
}

impl AsyncTriggerConfig {
    /// Create config optimized for high throughput
    pub fn high_throughput() -> Self {
        Self {
            queue_capacity: 50_000,
            batch_size: 500,
            flush_interval_ms: 100,
            send_timeout_ms: 5000,
            worker_count: 4,
            queue_low_watermark: 0.6,
            queue_high_watermark: 0.85,
            reject_threshold: 0.95,
        }
    }
    
    /// Create config optimized for low latency
    pub fn low_latency() -> Self {
        Self {
            queue_capacity: 5_000,
            batch_size: 50,
            flush_interval_ms: 10,
            send_timeout_ms: 500,
            worker_count: 2,
            queue_low_watermark: 0.4,
            queue_high_watermark: 0.7,
            reject_threshold: 0.9,
        }
    }
}

/// A trigger execution request
pub struct TriggerRequest {
    /// The memory node that was written
    pub node: MemoryNode,
    
    /// Deferred actions from pre/on-write phases
    pub deferred_actions: Vec<DeferredAction>,
    
    /// Timestamp when request was created
    pub created_at: Instant,
    
    /// Sequence number for ordering
    pub sequence: u64,
}

impl TriggerRequest {
    /// Create a new trigger request
    pub fn new(node: MemoryNode, deferred_actions: Vec<DeferredAction>, sequence: u64) -> Self {
        Self {
            node,
            deferred_actions,
            created_at: Instant::now(),
            sequence,
        }
    }
    
    /// Get time spent waiting in queue
    pub fn queue_time(&self) -> Duration {
        self.created_at.elapsed()
    }
}

/// Metrics for async trigger execution
#[derive(Debug, Default)]
pub struct AsyncTriggerMetrics {
    /// Total requests submitted
    pub total_requests: AtomicU64,
    
    /// Successfully processed requests
    pub processed_requests: AtomicU64,
    
    /// Failed requests
    pub failed_requests: AtomicU64,
    
    /// Rejected requests (due to backpressure)
    pub rejected_requests: AtomicU64,
    
    /// Total deferred actions processed
    pub deferred_actions_processed: AtomicU64,
    
    /// Total processing time in microseconds
    pub total_processing_time_us: AtomicU64,
    
    /// Current queue size
    pub current_queue_size: AtomicU64,
}

impl AsyncTriggerMetrics {
    /// Create new metrics instance
    pub fn new() -> Self {
        Self::default()
    }
    
    /// Record a submitted request
    pub fn record_request(&self) {
        self.total_requests.fetch_add(1, Ordering::Relaxed);
    }
    
    /// Record a processed request
    pub fn record_processed(&self, processing_time_us: u64, deferred_count: u64) {
        self.processed_requests.fetch_add(1, Ordering::Relaxed);
        self.deferred_actions_processed.fetch_add(deferred_count, Ordering::Relaxed);
        self.total_processing_time_us.fetch_add(processing_time_us, Ordering::Relaxed);
    }
    
    /// Record a failure
    pub fn record_failure(&self) {
        self.failed_requests.fetch_add(1, Ordering::Relaxed);
    }
    
    /// Record a rejection
    pub fn record_rejection(&self) {
        self.rejected_requests.fetch_add(1, Ordering::Relaxed);
    }
    
    /// Update queue size
    pub fn update_queue_size(&self, size: usize) {
        self.current_queue_size.store(size as u64, Ordering::Relaxed);
    }
    
    /// Get a snapshot of current metrics
    pub fn snapshot(&self) -> MetricsSnapshot {
        MetricsSnapshot {
            total_requests: self.total_requests.load(Ordering::Relaxed),
            processed_requests: self.processed_requests.load(Ordering::Relaxed),
            failed_requests: self.failed_requests.load(Ordering::Relaxed),
            rejected_requests: self.rejected_requests.load(Ordering::Relaxed),
            deferred_actions_processed: self.deferred_actions_processed.load(Ordering::Relaxed),
            total_processing_time_us: self.total_processing_time_us.load(Ordering::Relaxed),
            current_queue_size: self.current_queue_size.load(Ordering::Relaxed),
        }
    }
}

/// A snapshot of metrics at a point in time
#[derive(Debug, Clone)]
pub struct MetricsSnapshot {
    pub total_requests: u64,
    pub processed_requests: u64,
    pub failed_requests: u64,
    pub rejected_requests: u64,
    pub deferred_actions_processed: u64,
    pub total_processing_time_us: u64,
    pub current_queue_size: u64,
}

impl MetricsSnapshot {
    /// Calculate average processing time in microseconds
    pub fn avg_processing_time_us(&self) -> f64 {
        if self.processed_requests == 0 {
            0.0
        } else {
            self.total_processing_time_us as f64 / self.processed_requests as f64
        }
    }
    
    /// Calculate throughput (requests per second) given elapsed time
    pub fn throughput(&self, elapsed: Duration) -> f64 {
        if elapsed.is_zero() {
            0.0
        } else {
            self.processed_requests as f64 / elapsed.as_secs_f64()
        }
    }
}

/// Backpressure action
#[derive(Debug, Clone, PartialEq)]
pub enum BackpressureAction {
    /// Accept the request immediately
    Accept,
    /// Accept after a delay
    Slowdown(Duration),
    /// Reject the request
    Reject,
}

/// Async Trigger Executor
/// 
/// Executes post-write triggers asynchronously to avoid blocking the main write path.
/// 
/// # Architecture
/// 
/// ```text
/// Write Operation
///      │
///      ▼
/// ┌─────────────┐     ┌─────────────────┐     ┌─────────────────┐
/// │ Commit Data │ ──► │ Submit Trigger  │ ──► │ Async Queue     │
/// └─────────────┘     │ Request         │     │ (non-blocking)  │
///      │              └─────────────────┘     └────────┬────────┘
///      │                                               │
///      ▼                                               ▼
/// [Return to caller]                     ┌─────────────────────┐
/// (not blocked)                          │ Worker Tasks        │
///                                        │ - Execute triggers  │
///                                        │ - Process deferred  │
///                                        └─────────────────────┘
/// ```
pub struct AsyncTriggerExecutor {
    /// Sender for enqueueing requests
    sender: mpsc::Sender<TriggerRequest>,
    
    /// Receiver (moved to worker)
    receiver: parking_lot::Mutex<Option<mpsc::Receiver<TriggerRequest>>>,
    
    /// Post-write triggers to execute
    triggers: RwLock<Vec<Arc<dyn RustTrigger>>>,
    
    /// Configuration
    config: AsyncTriggerConfig,
    
    /// Metrics
    metrics: Arc<AsyncTriggerMetrics>,
    
    /// Sequence counter
    sequence: AtomicU64,
    
    /// Whether the executor is running
    running: AtomicBool,
}

impl AsyncTriggerExecutor {
    /// Create a new async trigger executor
    pub fn new(config: AsyncTriggerConfig) -> Self {
        let (sender, receiver) = mpsc::channel(config.queue_capacity);
        
        Self {
            sender,
            receiver: parking_lot::Mutex::new(Some(receiver)),
            triggers: RwLock::new(Vec::new()),
            config,
            metrics: Arc::new(AsyncTriggerMetrics::new()),
            sequence: AtomicU64::new(0),
            running: AtomicBool::new(false),
        }
    }
    
    /// Register a post-write trigger
    pub fn register_trigger(&self, trigger: Arc<dyn RustTrigger>) {
        if trigger.phase() == TriggerPhase::PostWrite {
            self.triggers.write().push(trigger);
        }
    }
    
    /// Unregister a trigger by ID
    pub fn unregister_trigger(&self, id: &str) {
        self.triggers.write().retain(|t| t.id() != id);
    }
    
    /// Get the next sequence number
    fn next_sequence(&self) -> u64 {
        self.sequence.fetch_add(1, Ordering::SeqCst)
    }
    
    /// Get current queue length (approximate)
    pub fn queue_len(&self) -> usize {
        self.config.queue_capacity - self.sender.capacity()
    }
    
    /// Get queue fill ratio (0.0 - 1.0)
    pub fn queue_fill_ratio(&self) -> f64 {
        self.queue_len() as f64 / self.config.queue_capacity as f64
    }
    
    /// Evaluate backpressure action
    fn evaluate_backpressure(&self) -> BackpressureAction {
        let fill_ratio = self.queue_fill_ratio();
        
        if fill_ratio >= self.config.reject_threshold {
            BackpressureAction::Reject
        } else if fill_ratio >= self.config.queue_high_watermark {
            // Severe slowdown
            let ratio = (fill_ratio - self.config.queue_high_watermark) / 
                (self.config.reject_threshold - self.config.queue_high_watermark);
            let delay_ms = (10.0 * (2.0_f64).powf(ratio * 4.0)).min(100.0) as u64;
            BackpressureAction::Slowdown(Duration::from_millis(delay_ms))
        } else if fill_ratio >= self.config.queue_low_watermark {
            // Mild slowdown
            let ratio = (fill_ratio - self.config.queue_low_watermark) / 
                (self.config.queue_high_watermark - self.config.queue_low_watermark);
            let delay_ms = (1.0 * (2.0_f64).powf(ratio * 2.0)).min(10.0) as u64;
            BackpressureAction::Slowdown(Duration::from_millis(delay_ms))
        } else {
            BackpressureAction::Accept
        }
    }
    
    /// Submit a trigger request (async, may block if queue is full)
    /// 
    /// This is called after a write operation commits successfully.
    /// The request will be processed asynchronously by worker tasks.
    pub async fn submit(
        &self,
        node: MemoryNode,
        deferred_actions: Vec<DeferredAction>,
    ) -> Result<u64, AsyncTriggerError> {
        // Check backpressure
        match self.evaluate_backpressure() {
            BackpressureAction::Accept => {}
            BackpressureAction::Slowdown(duration) => {
                tokio::time::sleep(duration).await;
            }
            BackpressureAction::Reject => {
                self.metrics.record_rejection();
                return Err(AsyncTriggerError::QueueFull);
            }
        }
        
        let sequence = self.next_sequence();
        let request = TriggerRequest::new(node, deferred_actions, sequence);
        
        self.metrics.record_request();
        self.metrics.update_queue_size(self.queue_len());
        
        if self.config.send_timeout_ms > 0 {
            match tokio::time::timeout(
                Duration::from_millis(self.config.send_timeout_ms),
                self.sender.send(request)
            ).await {
                Ok(Ok(())) => Ok(sequence),
                Ok(Err(_)) => {
                    self.metrics.record_failure();
                    Err(AsyncTriggerError::ExecutorClosed)
                }
                Err(_) => {
                    self.metrics.record_rejection();
                    Err(AsyncTriggerError::Timeout)
                }
            }
        } else {
            self.sender.send(request).await
                .map(|_| sequence)
                .map_err(|_| {
                    self.metrics.record_failure();
                    AsyncTriggerError::ExecutorClosed
                })
        }
    }
    
    /// Submit without blocking (fire-and-forget)
    pub fn submit_sync(&self, node: MemoryNode, deferred_actions: Vec<DeferredAction>) -> Result<u64, AsyncTriggerError> {
        let sequence = self.next_sequence();
        let request = TriggerRequest::new(node, deferred_actions, sequence);
        
        self.metrics.record_request();
        
        match self.sender.try_send(request) {
            Ok(()) => {
                self.metrics.update_queue_size(self.queue_len());
                Ok(sequence)
            }
            Err(mpsc::error::TrySendError::Full(_)) => {
                self.metrics.record_rejection();
                Err(AsyncTriggerError::QueueFull)
            }
            Err(mpsc::error::TrySendError::Closed(_)) => {
                self.metrics.record_failure();
                Err(AsyncTriggerError::ExecutorClosed)
            }
        }
    }
    
    /// Take the receiver (for worker thread)
    pub fn take_receiver(&self) -> Option<mpsc::Receiver<TriggerRequest>> {
        self.receiver.lock().take()
    }
    
    /// Get a clone of triggers for worker
    pub fn get_triggers(&self) -> Vec<Arc<dyn RustTrigger>> {
        self.triggers.read().clone()
    }
    
    /// Get metrics
    pub fn metrics(&self) -> &Arc<AsyncTriggerMetrics> {
        &self.metrics
    }
    
    /// Get configuration
    pub fn config(&self) -> &AsyncTriggerConfig {
        &self.config
    }
    
    /// Check if executor is running
    pub fn is_running(&self) -> bool {
        self.running.load(Ordering::Relaxed)
    }
    
    /// Set running state
    pub fn set_running(&self, running: bool) {
        self.running.store(running, Ordering::Relaxed);
    }
}

/// Process a batch of trigger requests
pub fn process_trigger_batch(
    batch: Vec<TriggerRequest>,
    triggers: &[Arc<dyn RustTrigger>],
    metrics: &AsyncTriggerMetrics,
) {
    for request in batch {
        let start = Instant::now();
        let deferred_count = request.deferred_actions.len() as u64;
        
        // Create context
        let mut ctx = TriggerContext::new(TriggerPhase::PostWrite, request.node);
        
        // Execute all post-write triggers
        for trigger in triggers {
            if trigger.should_execute(&ctx) {
                let result = trigger.execute(&ctx);
                
                // Handle result (post-write can modify context but shouldn't abort)
                if let super::types::TriggerResult::Modify(node) = result {
                    ctx.node = node;
                }
            }
        }
        
        // Process deferred actions
        for _action in request.deferred_actions {
            // Deferred actions are handled by the caller or logged
            // The actual processing depends on the action type
        }
        
        let elapsed_us = start.elapsed().as_micros() as u64;
        metrics.record_processed(elapsed_us, deferred_count);
    }
}

/// Errors from async trigger execution
#[derive(Debug, Clone, thiserror::Error)]
pub enum AsyncTriggerError {
    #[error("Trigger queue is full")]
    QueueFull,
    
    #[error("Executor is closed")]
    ExecutorClosed,
    
    #[error("Request timed out")]
    Timeout,
    
    #[error("Execution failed: {0}")]
    ExecutionFailed(String),
}

/// Start async trigger worker tasks
/// 
/// This function spawns worker tasks that process trigger requests from the queue.
/// Call this once after creating the AsyncTriggerExecutor.
pub async fn start_async_trigger_workers(
    executor: Arc<AsyncTriggerExecutor>,
) -> Result<(), AsyncTriggerError> {
    let receiver = executor.take_receiver()
        .ok_or(AsyncTriggerError::ExecutorClosed)?;
    
    executor.set_running(true);
    
    let config = executor.config().clone();
    let metrics = executor.metrics().clone();
    let triggers = executor.get_triggers();
    
    // Single worker that processes batches
    let executor_clone = executor.clone();
    tokio::spawn(async move {
        let mut receiver = receiver;
        let mut batch = Vec::with_capacity(config.batch_size);
        let mut last_flush = Instant::now();
        let flush_interval = Duration::from_millis(config.flush_interval_ms);
        
        loop {
            // Try to receive with timeout for batch flushing
            match tokio::time::timeout(flush_interval, receiver.recv()).await {
                Ok(Some(request)) => {
                    batch.push(request);
                    
                    // Process batch if full
                    if batch.len() >= config.batch_size {
                        let batch_to_process = std::mem::take(&mut batch);
                        process_trigger_batch(batch_to_process, &triggers, &metrics);
                        last_flush = Instant::now();
                    }
                }
                Ok(None) => {
                    // Channel closed, process remaining
                    if !batch.is_empty() {
                        process_trigger_batch(std::mem::take(&mut batch), &triggers, &metrics);
                    }
                    executor_clone.set_running(false);
                    break;
                }
                Err(_) => {
                    // Timeout - flush partial batch if we have items and time elapsed
                    if !batch.is_empty() && last_flush.elapsed() >= flush_interval {
                        let batch_to_process = std::mem::take(&mut batch);
                        process_trigger_batch(batch_to_process, &triggers, &metrics);
                        last_flush = Instant::now();
                    }
                }
            }
            
            metrics.update_queue_size(executor_clone.queue_len());
        }
    });
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::types::TriggerResult;
    use std::sync::atomic::AtomicUsize;
    
    struct TestPostWriteTrigger {
        id: String,
        execute_count: AtomicUsize,
    }
    
    impl TestPostWriteTrigger {
        fn new(id: &str) -> Self {
            Self {
                id: id.to_string(),
                execute_count: AtomicUsize::new(0),
            }
        }
        
        fn count(&self) -> usize {
            self.execute_count.load(Ordering::Relaxed)
        }
    }
    
    impl RustTrigger for TestPostWriteTrigger {
        fn id(&self) -> &str { &self.id }
        fn name(&self) -> &str { "Test Post-Write Trigger" }
        fn phase(&self) -> TriggerPhase { TriggerPhase::PostWrite }
        
        fn execute(&self, _ctx: &TriggerContext) -> TriggerResult {
            self.execute_count.fetch_add(1, Ordering::Relaxed);
            TriggerResult::Continue
        }
    }
    
    #[test]
    fn test_config_defaults() {
        let config = AsyncTriggerConfig::default();
        assert_eq!(config.queue_capacity, 10_000);
        assert_eq!(config.batch_size, 100);
        assert_eq!(config.worker_count, 2);
    }
    
    #[test]
    fn test_config_high_throughput() {
        let config = AsyncTriggerConfig::high_throughput();
        assert_eq!(config.queue_capacity, 50_000);
        assert_eq!(config.batch_size, 500);
        assert_eq!(config.worker_count, 4);
    }
    
    #[test]
    fn test_config_low_latency() {
        let config = AsyncTriggerConfig::low_latency();
        assert_eq!(config.queue_capacity, 5_000);
        assert_eq!(config.batch_size, 50);
    }
    
    #[test]
    fn test_executor_creation() {
        let config = AsyncTriggerConfig::default();
        let executor = AsyncTriggerExecutor::new(config);
        
        assert_eq!(executor.queue_len(), 0);
        assert!(!executor.is_running());
    }
    
    #[test]
    fn test_register_trigger() {
        let executor = AsyncTriggerExecutor::new(AsyncTriggerConfig::default());
        let trigger = Arc::new(TestPostWriteTrigger::new("test-1"));
        
        executor.register_trigger(trigger);
        
        let triggers = executor.get_triggers();
        assert_eq!(triggers.len(), 1);
        assert_eq!(triggers[0].id(), "test-1");
    }
    
    #[test]
    fn test_unregister_trigger() {
        let executor = AsyncTriggerExecutor::new(AsyncTriggerConfig::default());
        let trigger = Arc::new(TestPostWriteTrigger::new("test-1"));
        
        executor.register_trigger(trigger);
        assert_eq!(executor.get_triggers().len(), 1);
        
        executor.unregister_trigger("test-1");
        assert_eq!(executor.get_triggers().len(), 0);
    }
    
    #[test]
    fn test_submit_sync() {
        let config = AsyncTriggerConfig {
            queue_capacity: 100,
            ..Default::default()
        };
        let executor = AsyncTriggerExecutor::new(config);
        
        let node = MemoryNode::new("test content");
        let result = executor.submit_sync(node, vec![]);
        
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 0);
        assert_eq!(executor.queue_len(), 1);
    }
    
    #[test]
    fn test_submit_sync_multiple() {
        let config = AsyncTriggerConfig {
            queue_capacity: 100,
            ..Default::default()
        };
        let executor = AsyncTriggerExecutor::new(config);
        
        for i in 0..10 {
            let node = MemoryNode::new(&format!("content {}", i));
            let seq = executor.submit_sync(node, vec![]).unwrap();
            assert_eq!(seq, i);
        }
        
        assert_eq!(executor.queue_len(), 10);
        
        let snapshot = executor.metrics().snapshot();
        assert_eq!(snapshot.total_requests, 10);
    }
    
    #[test]
    fn test_submit_sync_queue_full() {
        let config = AsyncTriggerConfig {
            queue_capacity: 2,
            reject_threshold: 0.95,
            ..Default::default()
        };
        let executor = AsyncTriggerExecutor::new(config);
        
        // Fill the queue
        executor.submit_sync(MemoryNode::new("1"), vec![]).unwrap();
        executor.submit_sync(MemoryNode::new("2"), vec![]).unwrap();
        
        // Third should fail
        let result = executor.submit_sync(MemoryNode::new("3"), vec![]);
        assert!(matches!(result, Err(AsyncTriggerError::QueueFull)));
    }
    
    #[test]
    fn test_backpressure_evaluation() {
        let config = AsyncTriggerConfig {
            queue_capacity: 100,
            queue_low_watermark: 0.3,
            queue_high_watermark: 0.6,
            reject_threshold: 0.9,
            ..Default::default()
        };
        let executor = AsyncTriggerExecutor::new(config);
        
        // Empty queue - should accept
        assert_eq!(executor.evaluate_backpressure(), BackpressureAction::Accept);
    }
    
    #[test]
    fn test_metrics_snapshot() {
        let metrics = AsyncTriggerMetrics::new();
        
        metrics.record_request();
        metrics.record_request();
        metrics.record_processed(100, 2);
        metrics.record_failure();
        metrics.record_rejection();
        
        let snapshot = metrics.snapshot();
        assert_eq!(snapshot.total_requests, 2);
        assert_eq!(snapshot.processed_requests, 1);
        assert_eq!(snapshot.failed_requests, 1);
        assert_eq!(snapshot.rejected_requests, 1);
        assert_eq!(snapshot.deferred_actions_processed, 2);
        assert_eq!(snapshot.total_processing_time_us, 100);
    }
    
    #[test]
    fn test_process_batch() {
        let trigger = Arc::new(TestPostWriteTrigger::new("test"));
        let triggers: Vec<Arc<dyn RustTrigger>> = vec![trigger.clone()];
        let metrics = AsyncTriggerMetrics::new();
        
        let batch = vec![
            TriggerRequest::new(MemoryNode::new("content 1"), vec![], 0),
            TriggerRequest::new(MemoryNode::new("content 2"), vec![], 1),
            TriggerRequest::new(MemoryNode::new("content 3"), vec![], 2),
        ];
        
        process_trigger_batch(batch, &triggers, &metrics);
        
        assert_eq!(trigger.count(), 3);
        assert_eq!(metrics.snapshot().processed_requests, 3);
    }
    
    #[tokio::test]
    async fn test_submit_async() {
        let config = AsyncTriggerConfig {
            queue_capacity: 100,
            ..Default::default()
        };
        let executor = AsyncTriggerExecutor::new(config);
        
        let node = MemoryNode::new("test content");
        let result = executor.submit(node, vec![]).await;
        
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 0);
    }
    
    #[tokio::test]
    async fn test_start_workers() {
        let config = AsyncTriggerConfig {
            queue_capacity: 100,
            batch_size: 10,
            flush_interval_ms: 10,
            ..Default::default()
        };
        let executor = Arc::new(AsyncTriggerExecutor::new(config));
        
        let trigger = Arc::new(TestPostWriteTrigger::new("test"));
        executor.register_trigger(trigger.clone());
        
        // Submit some requests before starting workers
        for i in 0..5 {
            let node = MemoryNode::new(&format!("content {}", i));
            executor.submit_sync(node, vec![]).unwrap();
        }
        
        // Start workers
        start_async_trigger_workers(executor.clone()).await.unwrap();
        
        assert!(executor.is_running());
        
        // Wait for processing
        tokio::time::sleep(Duration::from_millis(50)).await;
        
        // Verify triggers were executed
        assert_eq!(trigger.count(), 5);
    }
    
    #[tokio::test]
    async fn test_continuous_submission() {
        let config = AsyncTriggerConfig {
            queue_capacity: 1000,
            batch_size: 50,
            flush_interval_ms: 20,
            ..Default::default()
        };
        let executor = Arc::new(AsyncTriggerExecutor::new(config));
        
        let trigger = Arc::new(TestPostWriteTrigger::new("test"));
        executor.register_trigger(trigger.clone());
        
        // Start workers first
        start_async_trigger_workers(executor.clone()).await.unwrap();
        
        // Submit requests continuously
        let total_requests = 200;
        for i in 0..total_requests {
            let node = MemoryNode::new(&format!("content {}", i));
            executor.submit(node, vec![]).await.unwrap();
        }
        
        // Wait for all to be processed
        tokio::time::sleep(Duration::from_millis(200)).await;
        
        // Verify all triggers were executed
        assert_eq!(trigger.count(), total_requests);
        
        let snapshot = executor.metrics().snapshot();
        assert_eq!(snapshot.total_requests, total_requests as u64);
        assert_eq!(snapshot.processed_requests, total_requests as u64);
    }
}
