//! Momentum indicators: RSI, MACD, Stochastic.

use crate::core::error::RaptorError;
use crate::core::Result;

/// Relative Strength Index (RSI).
///
/// # Arguments
/// * `data` - Price data (typically close prices)
/// * `period` - Lookback period (default: 14)
///
/// # Returns
/// Vector of RSI values (0-100 scale, NaN for warmup period)
pub fn rsi(data: &[f64], period: usize) -> Result<Vec<f64>> {
    if period == 0 {
        return Err(RaptorError::invalid_parameter("RSI period must be > 0"));
    }
    Ok(ferro_ta_core::momentum::rsi(data, period))
}

/// MACD result structure.
#[derive(Debug, Clone)]
pub struct MacdResult {
    /// MACD line (fast EMA - slow EMA).
    pub macd_line: Vec<f64>,
    /// Signal line (EMA of MACD line).
    pub signal_line: Vec<f64>,
    /// Histogram (MACD line - signal line).
    pub histogram: Vec<f64>,
}

/// Moving Average Convergence Divergence (MACD).
///
/// # Arguments
/// * `data` - Price data (typically close prices)
/// * `fast_period` - Fast EMA period (default: 12)
/// * `slow_period` - Slow EMA period (default: 26)
/// * `signal_period` - Signal line EMA period (default: 9)
///
/// # Returns
/// MacdResult with MACD line, signal line, and histogram
pub fn macd(
    data: &[f64],
    fast_period: usize,
    slow_period: usize,
    signal_period: usize,
) -> Result<MacdResult> {
    if fast_period == 0 || slow_period == 0 || signal_period == 0 {
        return Err(RaptorError::invalid_parameter("MACD periods must be > 0"));
    }
    if fast_period >= slow_period {
        return Err(RaptorError::invalid_parameter("MACD fast period must be < slow period"));
    }
    let (macd_line, signal_line, histogram) =
        ferro_ta_core::overlap::macd(data, fast_period, slow_period, signal_period);
    Ok(MacdResult { macd_line, signal_line, histogram })
}

/// Stochastic oscillator result.
#[derive(Debug, Clone)]
pub struct StochasticResult {
    /// %K line (fast stochastic).
    pub k: Vec<f64>,
    /// %D line (slow stochastic, SMA of %K).
    pub d: Vec<f64>,
}

/// Stochastic Oscillator.
///
/// # Arguments
/// * `high` - High prices
/// * `low` - Low prices
/// * `close` - Close prices
/// * `k_period` - %K lookback period (default: 14)
/// * `d_period` - %D smoothing period (default: 3)
///
/// # Returns
/// StochasticResult with %K and %D lines (0-100 scale)
pub fn stochastic(
    high: &[f64],
    low: &[f64],
    close: &[f64],
    k_period: usize,
    d_period: usize,
) -> Result<StochasticResult> {
    let n = close.len();
    if n != high.len() || n != low.len() {
        return Err(RaptorError::length_mismatch(n, high.len()));
    }
    if k_period == 0 || d_period == 0 {
        return Err(RaptorError::invalid_parameter("Stochastic periods must be > 0"));
    }
    let (k, d) = ferro_ta_core::momentum::stoch(high, low, close, k_period, d_period, d_period);
    Ok(StochasticResult { k, d })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_rsi() {
        // Test with simple increasing data
        let data = vec![
            44.0, 44.25, 44.5, 43.75, 44.5, 44.25, 44.0, 44.0, 44.25, 45.0, 45.5, 46.0, 46.5, 47.0,
            47.5,
        ];
        let result = rsi(&data, 14).unwrap();

        // RSI should be valid from index 14
        assert!(result[13].is_nan());
        assert!(!result[14].is_nan());
        assert!(result[14] >= 0.0 && result[14] <= 100.0);
    }

    #[test]
    fn test_macd() {
        let data: Vec<f64> = (1..=50).map(|x| x as f64).collect();
        let result = macd(&data, 12, 26, 9).unwrap();

        // MACD line should be valid from index 25 (slow_period - 1)
        assert!(result.macd_line[24].is_nan());
        assert!(!result.macd_line[25].is_nan());

        // Signal line starts at index slow_period-1 + signal_period-1 = 25+8 = 33
        assert!(result.signal_line[32].is_nan());
        assert!(!result.signal_line[33].is_nan());
    }

    #[test]
    fn test_stochastic() {
        let high = vec![50.0, 51.0, 52.0, 51.5, 50.5, 51.0, 52.0, 53.0, 52.5, 51.5];
        let low = vec![48.0, 49.0, 50.0, 49.5, 48.5, 49.0, 50.0, 51.0, 50.5, 49.5];
        let close = vec![49.0, 50.0, 51.0, 50.0, 49.0, 50.0, 51.0, 52.0, 51.0, 50.0];

        let result = stochastic(&high, &low, &close, 5, 3).unwrap();

        // %K should be valid from index 4
        assert!(result.k[3].is_nan());
        assert!(!result.k[4].is_nan());
        assert!(result.k[4] >= 0.0 && result.k[4] <= 100.0);

        // %D should be valid from index 6
        assert!(result.d[5].is_nan());
        assert!(!result.d[6].is_nan());
    }
}