use super::{Indicator, IncrementalIndicator, IndicatorResult, IndicatorError, PriceData};
use super::common::RollingWindow;

/// Simple Moving Average (SMA) Indicator
/// 
/// Calculates the arithmetic mean of the last N periods
pub struct SMA {
    period: usize,
    window: RollingWindow<f64>,
    sum: f64,
}

impl SMA {
    pub fn new(period: usize) -> Result<Self, IndicatorError> {
        if period == 0 {
            return Err(IndicatorError::InvalidParameter(
                "Period must be greater than 0".to_string()
            ));
        }
        
        Ok(Self {
            period,
            window: RollingWindow::new(period),
            sum: 0.0,
        })
    }
    
    /// Calculate SMA for a series of values
    pub fn calculate_series(values: &[f64], period: usize) -> Result<Vec<f64>, IndicatorError> {
        if period == 0 {
            return Err(IndicatorError::InvalidParameter(
                "Period must be greater than 0".to_string()
            ));
        }
        
        if values.len() < period {
            return Err(IndicatorError::InsufficientData {
                required: period,
                actual: values.len(),
            });
        }
        
        let mut result = Vec::with_capacity(values.len() - period + 1);
        
        // Calculate first SMA
        let mut sum: f64 = values[0..period].iter().sum();
        result.push(sum / period as f64);
        
        // Calculate remaining SMAs using sliding window
        for i in period..values.len() {
            sum = sum - values[i - period] + values[i];
            result.push(sum / period as f64);
        }
        
        Ok(result)
    }
}

impl Indicator for SMA {
    fn calculate(&mut self, data: &PriceData) -> Result<IndicatorResult, IndicatorError> {
        let values = &data.close;
        
        if values.len() < self.period {
            return Err(IndicatorError::InsufficientData {
                required: self.period,
                actual: values.len(),
            });
        }
        
        let sma_values = Self::calculate_series(values, self.period)?;
        Ok(IndicatorResult::Series(sma_values))
    }
    
    fn reset(&mut self) {
        self.window.clear();
        self.sum = 0.0;
    }
    
    fn is_ready(&self) -> bool {
        self.window.is_full()
    }
}

impl IncrementalIndicator for SMA {
    fn update(&mut self, value: f64) -> Result<Option<f64>, IndicatorError> {
        // If window is full, subtract the oldest value from sum
        if self.window.is_full() {
            if let Some(oldest) = self.window.iter().next() {
                self.sum -= oldest;
            }
        }
        
        // Add new value
        self.window.push(value);
        self.sum += value;
        
        // Calculate SMA if we have enough data
        if self.window.is_full() {
            Ok(Some(self.sum / self.period as f64))
        } else {
            Ok(None)
        }
    }
    
    fn current(&self) -> Option<f64> {
        if self.window.is_full() {
            Some(self.sum / self.period as f64)
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_sma_calculation() {
        let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
        let result = SMA::calculate_series(&values, 3).unwrap();
        
        assert_eq!(result.len(), 8);
        assert!((result[0] - 2.0).abs() < 1e-10); // (1+2+3)/3 = 2
        assert!((result[1] - 3.0).abs() < 1e-10); // (2+3+4)/3 = 3
        assert!((result[7] - 9.0).abs() < 1e-10); // (8+9+10)/3 = 9
    }
    
    #[test]
    fn test_incremental_sma() {
        let mut sma = SMA::new(3).unwrap();
        
        assert_eq!(sma.update(1.0).unwrap(), None);
        assert_eq!(sma.update(2.0).unwrap(), None);
        assert_eq!(sma.update(3.0).unwrap(), Some(2.0));
        assert_eq!(sma.update(4.0).unwrap(), Some(3.0));
        assert_eq!(sma.update(5.0).unwrap(), Some(4.0));
    }
}