use std::{
    env,
    time::{Duration, Instant},
};

use crate::trend_value::TrendValue;
use rust_decimal::Decimal;

#[derive(Debug, Clone)]
pub struct TrendMetrics {
    pub short_term_val: TrendValue,
    pub long_term_val: TrendValue,
    pub macd: TrendValue,
    pub val: TrendValue,
    pub max_val: Decimal,
    pub min_val: Decimal,
    pub trend: TrendType,
    pub crossover_last_occurrence: Option<Instant>,
    pub last_crossover_type: CrossOverType,
    pub crossover_hold_period: Duration,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum CrossOverType {
    GoldenCross,
    DeadCross,
    None,
}

impl CrossOverType {
    pub fn to_numeric(&self) -> Decimal {
        match self {
            CrossOverType::GoldenCross => Decimal::new(6, 1),
            CrossOverType::DeadCross => Decimal::new(4, 1),
            CrossOverType::None => Decimal::new(5, 1),
        }
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TrendType {
    Up,
    Down,
    Unknown,
}

impl TrendType {
    pub fn to_numeric(&self) -> Decimal {
        match self {
            TrendType::Up => Decimal::new(6, 1),
            TrendType::Down => Decimal::new(4, 1),
            TrendType::Unknown => Decimal::new(5, 1),
        }
    }
}

impl TrendMetrics {
    pub fn new(short_period: usize, long_period: usize, trading_period: usize) -> Self {
        let crossover_hold_seconds = env::var("CROSSOVER_HOLD_SECONDS")
            .unwrap_or_else(|_| "60".to_string())
            .parse::<u64>()
            .unwrap_or(60);

        Self {
            short_term_val: TrendValue::new(short_period),
            long_term_val: TrendValue::new(long_period),
            val: TrendValue::new(trading_period),
            macd: TrendValue::new(trading_period),
            max_val: Decimal::ZERO,
            min_val: Decimal::MAX,
            trend: TrendType::Unknown,
            crossover_last_occurrence: None,
            last_crossover_type: CrossOverType::None,
            crossover_hold_period: Duration::from_secs(crossover_hold_seconds),
        }
    }

    pub fn update(&mut self, new_value: Decimal, short_value: Decimal, long_value: Decimal) {
        self.short_term_val.update_value(short_value);
        self.long_term_val.update_value(long_value);
        self.val.update_value(new_value);

        let ema_diff =
            self.short_term_val.current_ema().unwrap() - self.long_term_val.current_ema().unwrap();
        self.macd.update_value(ema_diff);

        self.max_val = std::cmp::max(self.max_val, new_value);
        self.min_val = std::cmp::min(self.min_val, new_value);

        match self.is_crossover() {
            CrossOverType::GoldenCross => self.trend = TrendType::Up,
            CrossOverType::DeadCross => self.trend = TrendType::Down,
            CrossOverType::None => {}
        }
    }

    pub fn is_crossover(&mut self) -> CrossOverType {
        if !self.macd.has_sufficient_data() {
            return CrossOverType::None;
        }

        let current_macd = match self.macd.current_ema() {
            Some(value) => value,
            None => return CrossOverType::None,
        };
        let previous_macd = match self.macd.previous_ema() {
            Some(value) => value,
            None => return CrossOverType::None,
        };
        let current_signal = match self.macd.current_value() {
            Some(value) => value,
            None => return CrossOverType::None,
        };
        let previous_signal = match self.macd.previous_value() {
            Some(value) => value,
            None => return CrossOverType::None,
        };

        let current_crossover = if current_signal > current_macd
            && previous_signal <= previous_macd
            && current_signal < Decimal::ZERO
        {
            CrossOverType::GoldenCross
        } else if current_signal < current_macd
            && previous_signal >= previous_macd
            && current_signal > Decimal::ZERO
        {
            CrossOverType::DeadCross
        } else {
            CrossOverType::None
        };

        if current_crossover != CrossOverType::None {
            self.crossover_last_occurrence = Some(Instant::now());
            self.last_crossover_type = current_crossover;
        } else if let Some(last_occurrence) = self.crossover_last_occurrence {
            if last_occurrence.elapsed() > self.crossover_hold_period {
                self.last_crossover_type = CrossOverType::None;
            }
        }

        self.last_crossover_type.clone()
    }
}
