//! Progress callback infrastructure

use std::sync::{Arc, Mutex};
use std::time::Duration;

/// Progress update information
#[derive(Debug, Clone)]
pub struct ProgressUpdate {
    /// Current progress percentage (0.0 to 100.0)
    pub percent: f64,
    /// Current phase or operation description
    pub phase: String,
    /// Optional additional message
    pub message: Option<String>,
}

impl ProgressUpdate {
    /// Create new progress update
    pub fn new(percent: f64, phase: &str) -> Self {
        let clamped_percent = percent.clamp(0.0, 100.0);
        if clamped_percent != percent {
            crate::profiling::logging::log_warn(&format!(
                "Progress percentage {} clamped to {}",
                percent, clamped_percent
            ));
        }

        Self {
            percent: clamped_percent,
            phase: phase.to_string(),
            message: None,
        }
    }

    /// Add message to progress update
    pub fn with_message(mut self, message: &str) -> Self {
        self.message = Some(message.to_string());
        self
    }

    /// Validate progress update
    pub fn validate(&self) -> Result<(), crate::profiling::error::ProfilingError> {
        if self.percent < 0.0 || self.percent > 100.0 {
            return Err(crate::profiling::error::ProfilingError::InvalidConfig(
                format!(
                    "Progress percentage {} out of bounds [0, 100]",
                    self.percent
                ),
            ));
        }

        if self.phase.is_empty() {
            return Err(crate::profiling::error::ProfilingError::InvalidConfig(
                "Progress phase cannot be empty".to_string(),
            ));
        }

        Ok(())
    }
}

/// Progress callback trait
pub trait ProgressCallback: Send + Sync {
    /// Called when progress is updated
    fn on_progress(&self, update: &ProgressUpdate);

    /// Check if operation should be cancelled (optional)
    fn should_cancel(&self) -> bool {
        false
    }
}

/// Default progress callback that logs to console
#[derive(Debug, Clone)]
pub struct ConsoleProgressCallback;

impl ProgressCallback for ConsoleProgressCallback {
    fn on_progress(&self, update: &ProgressUpdate) {
        let bar_width = 40;
        let filled = (update.percent / 100.0 * bar_width as f64) as usize;
        let empty = bar_width - filled;

        let bar = format!("{}{}", "=".repeat(filled), " ".repeat(empty));
        println!("[{:3.0}%] [{}] {}", update.percent, bar, update.phase);

        if let Some(ref msg) = update.message {
            println!("      {}", msg);
        }
    }
}

/// No-op progress callback
#[derive(Debug, Clone)]
pub struct NoOpProgressCallback;

impl ProgressCallback for NoOpProgressCallback {
    fn on_progress(&self, _update: &ProgressUpdate) {}
}

/// Progress reporter for tracking benchmark progress
pub struct ProgressReporter {
    callback: Arc<dyn ProgressCallback>,
    interval: Duration,
    last_report: Arc<Mutex<std::time::Instant>>,
}

impl ProgressReporter {
    /// Create new progress reporter
    pub fn new(callback: Arc<dyn ProgressCallback>, interval: Duration) -> Self {
        Self {
            callback,
            interval,
            last_report: Arc::new(Mutex::new(std::time::Instant::now() - interval)),
        }
    }

    /// Create with default interval (1 second)
    pub fn with_default_interval(callback: Arc<dyn ProgressCallback>) -> Self {
        Self::new(callback, Duration::from_secs(1))
    }

    /// Report progress if enough time has elapsed
    pub fn report(
        &self,
        update: ProgressUpdate,
    ) -> Result<(), crate::profiling::error::ProfilingError> {
        // Validate progress update
        update.validate()?;

        // Check for cancellation
        if self.callback.should_cancel() {
            return Err(crate::profiling::error::ProfilingError::Cancelled);
        }

        if let Ok(mut last) = self.last_report.lock() {
            if last.elapsed() >= self.interval {
                self.callback.on_progress(&update);
                *last = std::time::Instant::now();
            }
        } else {
            // Lock poisoned - fallback to immediate reporting
            crate::profiling::logging::log_warn(
                "Progress reporter lock poisoned, using immediate reporting",
            );
            self.callback.on_progress(&update);
        }

        Ok(())
    }

    /// Force report regardless of interval
    pub fn report_now(
        &self,
        update: ProgressUpdate,
    ) -> Result<(), crate::profiling::error::ProfilingError> {
        // Validate progress update
        update.validate()?;

        // Check for cancellation
        if self.callback.should_cancel() {
            return Err(crate::profiling::error::ProfilingError::Cancelled);
        }

        self.callback.on_progress(&update);
        if let Ok(mut last) = self.last_report.lock() {
            *last = std::time::Instant::now();
        } else {
            crate::profiling::logging::log_warn(
                "Progress reporter lock poisoned during force report",
            );
        }

        Ok(())
    }
}

impl Clone for ProgressReporter {
    fn clone(&self) -> Self {
        Self {
            callback: Arc::clone(&self.callback),
            interval: self.interval,
            last_report: Arc::clone(&self.last_report),
        }
    }
}
