use lazy_static::lazy_static;
use rust_decimal::Decimal;
use std::{collections::VecDeque, env};

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

const MIN_PERIOD: usize = 10;

#[derive(Debug, Clone, Default)]
pub struct TrendValue {
    alpha: Decimal,
    value: VecDeque<Decimal>,
    ema: VecDeque<Decimal>,
    trend_period: usize,
}

impl TrendValue {
    pub fn new(period: usize) -> Self {
        let alpha = Decimal::from(2) / (Decimal::from(period) + Decimal::from(1));
        let trend_period = period * *TREND_PREIOD_MULTIPLER;
        TrendValue {
            alpha,
            value: VecDeque::with_capacity(trend_period),
            ema: VecDeque::with_capacity(trend_period),
            trend_period,
        }
    }

    pub fn update_value(&mut self, new_value: Decimal) {
        if self.value.len() > self.trend_period {
            self.value.pop_front();
        }
        self.value.push_back(new_value);

        if self.ema.len() > self.trend_period {
            self.ema.pop_front();
        }
        let ema = new_value * self.alpha
            + self.current_ema().unwrap_or(new_value) * (Decimal::from(1) - self.alpha);
        self.ema.push_back(ema);
    }

    pub fn has_sufficient_data(&self) -> bool {
        self.value.len() >= MIN_PERIOD
    }

    pub fn current_ema(&self) -> Option<Decimal> {
        self.ema.back().copied()
    }

    pub fn previous_ema(&self) -> Option<Decimal> {
        if self.ema.len() > 1 {
            self.ema.get(self.ema.len() - 2).copied()
        } else {
            None
        }
    }

    pub fn current_value(&self) -> Option<Decimal> {
        self.value.back().copied()
    }

    pub fn previous_value(&self) -> Option<Decimal> {
        if self.value.len() > 1 {
            self.value.get(self.value.len() - 2).copied()
        } else {
            None
        }
    }
}
