use super::{Indicator, IncrementalIndicator, IndicatorResult, IndicatorError, PriceData};
use super::ema::EMA;

/// Moving Average Convergence Divergence (MACD) Indicator
/// 
/// MACD = Fast EMA - Slow EMA
/// Signal = EMA of MACD
/// Histogram = MACD - Signal
pub struct MACD {
    fast_period: usize,
    slow_period: usize,
    signal_period: usize,
    fast_ema: EMA,
    slow_ema: EMA,
    signal_ema: EMA,
    macd_value: Option<f64>,
}

impl MACD {
    pub fn new(fast_period: usize, slow_period: usize, signal_period: usize) -> Result<Self, IndicatorError> {
        if fast_period == 0 || slow_period == 0 || signal_period == 0 {
            return Err(IndicatorError::InvalidParameter(
                "All periods must be greater than 0".to_string()
            ));
        }
        
        if fast_period >= slow_period {
            return Err(IndicatorError::InvalidParameter(
                "Fast period must be less than slow period".to_string()
            ));
        }
        
        Ok(Self {
            fast_period,
            slow_period,
            signal_period,
            fast_ema: EMA::new(fast_period)?,
            slow_ema: EMA::new(slow_period)?,
            signal_ema: EMA::new(signal_period)?,
            macd_value: None,
        })
    }
    
    /// Standard MACD with 12, 26, 9 periods
    pub fn standard() -> Result<Self, IndicatorError> {
        Self::new(12, 26, 9)
    }
    
    /// Calculate MACD for a series of values
    pub fn calculate_series(
        values: &[f64], 
        fast_period: usize, 
        slow_period: usize, 
        signal_period: usize
    ) -> Result<(Vec<f64>, Vec<f64>, Vec<f64>), IndicatorError> {
        if fast_period >= slow_period {
            return Err(IndicatorError::InvalidParameter(
                "Fast period must be less than slow period".to_string()
            ));
        }
        
        if values.len() < slow_period {
            return Err(IndicatorError::InsufficientData {
                required: slow_period,
                actual: values.len(),
            });
        }
        
        // Calculate EMAs
        let fast_ema = EMA::calculate_series(values, fast_period)?;
        let slow_ema = EMA::calculate_series(values, slow_period)?;
        
        // Calculate MACD line
        let mut macd_line = Vec::with_capacity(slow_ema.len());
        for i in 0..slow_ema.len() {
            // Align indices - slow EMA starts later
            let fast_idx = i + (slow_period - fast_period);
            macd_line.push(fast_ema[fast_idx] - slow_ema[i]);
        }
        
        // Calculate signal line (EMA of MACD)
        let signal_line = if macd_line.len() >= signal_period {
            EMA::calculate_series(&macd_line, signal_period)?
        } else {
            vec![]
        };
        
        // Calculate histogram
        let mut histogram = Vec::with_capacity(signal_line.len());
        for i in 0..signal_line.len() {
            // Align indices
            let macd_idx = i + (macd_line.len() - signal_line.len());
            histogram.push(macd_line[macd_idx] - signal_line[i]);
        }
        
        Ok((macd_line, signal_line, histogram))
    }
}

impl Indicator for MACD {
    fn calculate(&mut self, data: &PriceData) -> Result<IndicatorResult, IndicatorError> {
        let values = &data.close;
        
        let (macd, signal, histogram) = Self::calculate_series(
            values, 
            self.fast_period, 
            self.slow_period, 
            self.signal_period
        )?;
        
        Ok(IndicatorResult::MACD {
            macd,
            signal,
            histogram,
        })
    }
    
    fn reset(&mut self) {
        self.fast_ema.reset();
        self.slow_ema.reset();
        self.signal_ema.reset();
        self.macd_value = None;
    }
    
    fn is_ready(&self) -> bool {
        self.fast_ema.is_ready() && self.slow_ema.is_ready() && self.signal_ema.is_ready()
    }
}

impl IncrementalIndicator for MACD {
    fn update(&mut self, value: f64) -> Result<Option<f64>, IndicatorError> {
        // Update both EMAs
        let fast_result = self.fast_ema.update(value)?;
        let slow_result = self.slow_ema.update(value)?;
        
        // Calculate MACD if both EMAs are ready
        if let (Some(fast), Some(slow)) = (fast_result, slow_result) {
            let macd = fast - slow;
            self.macd_value = Some(macd);
            
            // Update signal line
            if let Some(signal) = self.signal_ema.update(macd)? {
                // Return histogram value
                Ok(Some(macd - signal))
            } else {
                Ok(None)
            }
        } else {
            Ok(None)
        }
    }
    
    fn current(&self) -> Option<f64> {
        // Return current histogram value
        if let (Some(fast), Some(slow), Some(signal)) = (
            self.fast_ema.current(),
            self.slow_ema.current(),
            self.signal_ema.current()
        ) {
            let macd = fast - slow;
            Some(macd - signal)
        } else {
            None
        }
    }
}

/// Structure to hold all MACD values for incremental updates
pub struct MACDValues {
    pub macd: f64,
    pub signal: f64,
    pub histogram: f64,
}

impl MACD {
    /// Get all current MACD values
    pub fn current_values(&self) -> Option<MACDValues> {
        if let (Some(fast), Some(slow), Some(signal)) = (
            self.fast_ema.current(),
            self.slow_ema.current(),
            self.signal_ema.current()
        ) {
            let macd = fast - slow;
            Some(MACDValues {
                macd,
                signal,
                histogram: macd - signal,
            })
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_macd_calculation() {
        let values = vec![
            10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5,
            14.0, 14.5, 15.0, 14.5, 14.0, 13.5, 13.0, 12.5,
            12.0, 11.5, 11.0, 10.5, 10.0, 10.5, 11.0, 11.5,
            12.0, 12.5, 13.0, 13.5, 14.0, 14.5
        ];
        
        let (macd, signal, histogram) = MACD::calculate_series(&values, 12, 26, 9).unwrap();
        
        // Should have values after slow period
        assert!(!macd.is_empty());
        assert!(!signal.is_empty());
        assert!(!histogram.is_empty());
        
        // Histogram should equal MACD - Signal
        for i in 0..histogram.len() {
            let expected = macd[macd.len() - histogram.len() + i] - signal[i];
            assert!((histogram[i] - expected).abs() < 1e-10);
        }
    }
    
    #[test]
    fn test_standard_macd() {
        let macd = MACD::standard().unwrap();
        assert_eq!(macd.fast_period, 12);
        assert_eq!(macd.slow_period, 26);
        assert_eq!(macd.signal_period, 9);
    }
}