//! Main run loop for the IngestWorker.
//!
//! Contains the continuous ingestion loop that tails the WAL.

use super::IngestWorker;
use crate::error::IngestError;
use std::sync::atomic::Ordering;
use std::time::Duration;
use stemedb_storage::KVStore;
use tracing::{debug, error, info, warn};

impl<S: KVStore + 'static> IngestWorker<S> {
    /// Run the continuous ingestion loop, tailing the WAL until shutdown.
    ///
    /// If a flush notification channel is configured (via `with_flush_notify`),
    /// the worker waits for signals from the GroupCommitBuffer instead of
    /// polling. When notified, it refreshes the segment list to see new data
    /// written by a separate Journal instance.
    pub async fn run(&mut self) {
        info!(
            current_offset = self.current_offset,
            has_notify = self.notify.is_some(),
            "Starting ingestion loop"
        );
        loop {
            // Check for shutdown signal
            if self.shutdown.load(Ordering::Relaxed) {
                info!("Shutdown signal received, stopping ingestion loop");
                break;
            }

            match self.step().await {
                Ok(0) => {
                    // No new data - wait for notify signal or timeout
                    if let Some(ref notify) = self.notify {
                        // Wait for signal from GroupCommitBuffer with timeout fallback
                        tokio::select! {
                            _ = notify.notified() => {
                                debug!(
                                    current_offset = self.current_offset,
                                    "Received flush notify signal"
                                );
                                // New data flushed - refresh segment list before next read
                                if let Ok(mut journal) = self.journal.try_lock() {
                                    if let Err(e) = journal.refresh_segments() {
                                        debug!("Failed to refresh segments: {:?}", e);
                                    } else {
                                        debug!("Successfully refreshed segments");
                                    }
                                } else {
                                    warn!("Could not acquire journal lock for refresh");
                                }
                            }
                            _ = tokio::time::sleep(Duration::from_millis(100)) => {
                                // Timeout fallback - still try to refresh in case we missed a signal
                                if let Ok(mut journal) = self.journal.try_lock() {
                                    let _ = journal.refresh_segments();
                                }
                            }
                        }
                    } else {
                        // No notify channel - fall back to polling
                        tokio::time::sleep(Duration::from_millis(10)).await;
                    }
                }
                Ok(_) => {
                    // Processed data, continue immediately
                }
                Err(e) => {
                    // On shutdown, WAL errors are expected (files may be deleted)
                    if self.shutdown.load(Ordering::Relaxed) {
                        debug!("Error during shutdown (expected): {:?}", e);
                        break;
                    }
                    match &e {
                        IngestError::InputValidation(msg) => {
                            warn!("Rejected invalid input: {}", msg);
                        }
                        IngestError::InvalidSignature(msg) => {
                            warn!("Rejected invalid signature: {}", msg);
                        }
                        _ => {
                            error!("Ingestion error: {:?}", e);
                        }
                    }
                    tokio::time::sleep(Duration::from_secs(1)).await;
                }
            }
        }
        info!("Ingestion loop stopped");
    }
}
