use std::{collections::VecDeque, env};

use lazy_static::lazy_static;

lazy_static! {
    static ref TREND_PREIOD_MULTIPLER: usize = env::var("TREND_PREIOD_MULTIPLER")
        .unwrap_or_else(|_| "10".to_string())
        .parse()
        .unwrap_or(10);
}

#[derive(Debug, Clone, Default)]
pub struct TrendValue {
    alpha: f64,
    current_ema: Option<f64>,
    previous_emas: VecDeque<f64>,
    trend_period: usize,
}

impl TrendValue {
    pub fn new(period: usize) -> Self {
        let alpha = 2.0 / (period as f64 + 1.0);
        let trend_period = period * *TREND_PREIOD_MULTIPLER;
        TrendValue {
            alpha,
            current_ema: None,
            previous_emas: VecDeque::with_capacity(trend_period),
            trend_period,
        }
    }

    pub fn update_value(&mut self, new_value: f64) {
        if let Some(current_ema) = self.current_ema {
            self.previous_emas.push_back(current_ema);
            if self.previous_emas.len() > self.trend_period {
                self.previous_emas.pop_front();
            }
        }
        self.current_ema = Some(
            new_value * self.alpha + self.current_ema.unwrap_or(new_value) * (1.0 - self.alpha),
        );
    }

    pub fn ema_change_rate(&self) -> f64 {
        if self.previous_emas.is_empty() {
            return 0.0;
        }

        let sum: f64 = self
            .previous_emas
            .iter()
            .zip(self.previous_emas.iter().skip(1))
            .map(|(&prev, &current)| (current - prev) / prev.abs())
            .sum();

        sum / self.previous_emas.len() as f64
    }

    pub fn current_ema(&self) -> f64 {
        self.current_ema.unwrap_or_default()
    }

    pub fn previous_ema(&self) -> f64 {
        *self.previous_emas.back().unwrap_or(&self.current_ema())
    }
}
