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,
    decay_factor: f64,
}

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,
            decay_factor: 0.1,
        }
    }

    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) -> Option<f64> {
        if self.previous_emas.is_empty() || self.decay_factor <= 0.0 || self.decay_factor > 1.0 {
            return None;
        }

        let len = self.previous_emas.len();
        let mut weighted_sum: f64 = 0.0;
        let mut weight: f64 = 1.0;
        let mut total_weight: f64 = 0.0;

        for i in 0..len - 1 {
            let prev = self.previous_emas[len - 2 - i];
            let current = self.previous_emas[len - 1 - i];
            weighted_sum += ((current - prev) / prev.abs()) * weight;
            total_weight += weight;
            weight *= self.decay_factor;
        }

        Some(weighted_sum / total_weight)
    }

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

    pub fn previous_ema(&self) -> Option<f64> {
        self.previous_emas.back().copied()
    }
}
