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

/// Stochastic Oscillator Indicator
/// 
/// %K = 100 × (Close - Lowest Low) / (Highest High - Lowest Low)
/// %D = SMA of %K
pub struct Stochastic {
    k_period: usize,
    d_period: usize,
    smooth_k: usize,
    high_window: RollingWindow<f64>,
    low_window: RollingWindow<f64>,
    close_window: RollingWindow<f64>,
    k_sma: SMA,
    d_sma: SMA,
    k_values: RollingWindow<f64>,
}

impl Stochastic {
    pub fn new(k_period: usize, d_period: usize, smooth_k: usize) -> Result<Self, IndicatorError> {
        if k_period == 0 || d_period == 0 {
            return Err(IndicatorError::InvalidParameter(
                "K and D periods must be greater than 0".to_string()
            ));
        }
        
        Ok(Self {
            k_period,
            d_period,
            smooth_k,
            high_window: RollingWindow::new(k_period),
            low_window: RollingWindow::new(k_period),
            close_window: RollingWindow::new(k_period),
            k_sma: SMA::new(smooth_k.max(1))?,
            d_sma: SMA::new(d_period)?,
            k_values: RollingWindow::new(d_period),
        })
    }
    
    /// Standard Fast Stochastic (14, 3)
    pub fn fast() -> Result<Self, IndicatorError> {
        Self::new(14, 3, 1)
    }
    
    /// Standard Slow Stochastic (14, 3, 3)
    pub fn slow() -> Result<Self, IndicatorError> {
        Self::new(14, 3, 3)
    }
    
    /// Calculate raw %K value
    fn calculate_raw_k(high: &[f64], low: &[f64], close: f64) -> Option<f64> {
        if high.is_empty() || low.is_empty() {
            return None;
        }
        
        let highest = high.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
        let lowest = low.iter().cloned().fold(f64::INFINITY, f64::min);
        
        let range = highest - lowest;
        if range > 0.0 {
            Some(100.0 * (close - lowest) / range)
        } else {
            Some(50.0) // If no range, return middle value
        }
    }
    
    /// Calculate Stochastic for a series of price data
    pub fn calculate_series(
        high: &[f64],
        low: &[f64],
        close: &[f64],
        k_period: usize,
        d_period: usize,
        smooth_k: usize,
    ) -> Result<(Vec<f64>, Vec<f64>), IndicatorError> {
        if high.len() != low.len() || high.len() != close.len() {
            return Err(IndicatorError::InvalidParameter(
                "High, low, and close arrays must have the same length".to_string()
            ));
        }
        
        if high.len() < k_period {
            return Err(IndicatorError::InsufficientData {
                required: k_period,
                actual: high.len(),
            });
        }
        
        // Calculate raw %K values
        let mut raw_k_values = Vec::with_capacity(high.len() - k_period + 1);
        for i in k_period - 1..high.len() {
            let start = i + 1 - k_period;
            if let Some(k) = Self::calculate_raw_k(
                &high[start..=i],
                &low[start..=i],
                close[i]
            ) {
                raw_k_values.push(k);
            }
        }
        
        // Smooth %K if requested
        let k_values = if smooth_k > 1 {
            SMA::calculate_series(&raw_k_values, smooth_k)?
        } else {
            raw_k_values
        };
        
        // Calculate %D (SMA of %K)
        let d_values = if k_values.len() >= d_period {
            SMA::calculate_series(&k_values, d_period)?
        } else {
            vec![]
        };
        
        Ok((k_values, d_values))
    }
}

impl Indicator for Stochastic {
    fn calculate(&mut self, data: &PriceData) -> Result<IndicatorResult, IndicatorError> {
        if data.high.len() != data.low.len() || data.high.len() != data.close.len() {
            return Err(IndicatorError::InvalidParameter(
                "Price data arrays must have the same length".to_string()
            ));
        }
        
        let (k_values, d_values) = Self::calculate_series(
            &data.high,
            &data.low,
            &data.close,
            self.k_period,
            self.d_period,
            self.smooth_k,
        )?;
        
        Ok(IndicatorResult::Stochastic {
            k: k_values,
            d: d_values,
        })
    }
    
    fn reset(&mut self) {
        self.high_window.clear();
        self.low_window.clear();
        self.close_window.clear();
        self.k_sma.reset();
        self.d_sma.reset();
        self.k_values.clear();
    }
    
    fn is_ready(&self) -> bool {
        self.high_window.is_full() && self.d_sma.is_ready()
    }
}

impl IncrementalIndicator for Stochastic {
    fn update(&mut self, value: f64) -> Result<Option<f64>, IndicatorError> {
        // For incremental updates, we assume value is close price
        // In real usage, you'd need to provide high/low/close separately
        self.high_window.push(value);
        self.low_window.push(value);
        self.close_window.push(value);
        
        if self.high_window.is_full() {
            // Calculate raw %K
            if let Some(raw_k) = Self::calculate_raw_k(
                &self.high_window.as_slice(),
                &self.low_window.as_slice(),
                value
            ) {
                // Smooth %K if needed
                let k_value = if self.smooth_k > 1 {
                    self.k_sma.update(raw_k)?
                } else {
                    Some(raw_k)
                };
                
                if let Some(k) = k_value {
                    self.k_values.push(k);
                    
                    // Calculate %D
                    if let Some(d) = self.d_sma.update(k)? {
                        return Ok(Some(d));
                    }
                }
            }
        }
        
        Ok(None)
    }
    
    fn current(&self) -> Option<f64> {
        self.d_sma.current()
    }
}

/// Structure to hold Stochastic values
pub struct StochasticValues {
    pub k: f64,
    pub d: f64,
}

impl Stochastic {
    /// Get current %K and %D values
    pub fn current_values(&self) -> Option<StochasticValues> {
        if let (Some(&k), Some(d)) = (self.k_values.iter().last(), self.d_sma.current()) {
            Some(StochasticValues { k, d })
        } else {
            None
        }
    }
    
    /// Update with separate high, low, close values
    pub fn update_hlc(&mut self, high: f64, low: f64, close: f64) -> Result<Option<StochasticValues>, IndicatorError> {
        self.high_window.push(high);
        self.low_window.push(low);
        self.close_window.push(close);
        
        if self.high_window.is_full() {
            if let Some(raw_k) = Self::calculate_raw_k(
                &self.high_window.as_slice(),
                &self.low_window.as_slice(),
                close
            ) {
                let k_value = if self.smooth_k > 1 {
                    self.k_sma.update(raw_k)?
                } else {
                    Some(raw_k)
                };
                
                if let Some(k) = k_value {
                    self.k_values.push(k);
                    
                    if let Some(d) = self.d_sma.update(k)? {
                        return Ok(Some(StochasticValues { k, d }));
                    }
                }
            }
        }
        
        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_stochastic_calculation() {
        let high = vec![
            127.01, 127.62, 126.59, 127.35, 128.17,
            128.43, 127.37, 126.42, 126.90, 126.85,
            125.65, 125.72, 127.16, 127.72, 127.69
        ];
        let low = vec![
            125.36, 126.16, 124.93, 126.09, 126.82,
            126.48, 126.03, 124.83, 126.39, 125.72,
            124.56, 124.57, 125.07, 126.86, 126.63
        ];
        let close = vec![
            125.36, 126.16, 124.93, 126.09, 126.82,
            126.48, 126.03, 124.83, 126.39, 125.72,
            124.56, 124.57, 125.07, 126.86, 126.63
        ];
        
        let (k_values, d_values) = Stochastic::calculate_series(&high, &low, &close, 14, 3, 1).unwrap();
        
        assert!(!k_values.is_empty());
        assert!(!d_values.is_empty());
        
        // %K should be between 0 and 100
        for k in &k_values {
            assert!(*k >= 0.0 && *k <= 100.0);
        }
    }
    
    #[test]
    fn test_stochastic_extremes() {
        // When close is at highest high, %K should be 100
        let high = vec![10.0; 14];
        let low = vec![5.0; 14];
        let mut close = vec![7.5; 14];
        close[13] = 10.0; // Last close at high
        
        let (k_values, _) = Stochastic::calculate_series(&high, &low, &close, 14, 3, 1).unwrap();
        assert!((k_values[0] - 100.0).abs() < 1e-10);
        
        // When close is at lowest low, %K should be 0
        close[13] = 5.0; // Last close at low
        let (k_values, _) = Stochastic::calculate_series(&high, &low, &close, 14, 3, 1).unwrap();
        assert!(k_values[0].abs() < 1e-10);
    }
}