use crate::trend_value::TrendValue;
use rust_decimal::Decimal;
use std::time::{Duration, Instant};

#[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 crossover_last_occurrence: Option<Instant>,
    pub last_crossover_type: CrossOverType,
    pub crossover_hold_period: Duration,
    pub trend: TrendType,
}

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

impl CrossOverType {
    #[allow(dead_code)]
    pub fn to_numeric(&self) -> Decimal {
        match self {
            CrossOverType::GoldenCross => Decimal::new(7, 1),
            CrossOverType::BuyCross => Decimal::new(6, 1),
            CrossOverType::None => Decimal::new(5, 1),
            CrossOverType::SellCross => Decimal::new(4, 1),
            CrossOverType::DeadCross => Decimal::new(3, 1),
        }
    }
}

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

impl TrendType {
    pub fn to_numeric(&self) -> Decimal {
        match self {
            TrendType::Up => Decimal::new(9, 1),
            TrendType::Down => Decimal::new(7, 1),
            TrendType::Unknown => Decimal::new(5, 1),
            TrendType::StrongUp => Decimal::new(3, 1),
            TrendType::StrongDown => Decimal::new(1, 1),
        }
    }
}

impl TrendMetrics {
    pub fn new(
        short_period: usize,
        long_period: usize,
        trading_period: usize,
        crossover_hold_seconds: i64,
    ) -> Self {
        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,
            crossover_last_occurrence: None,
            last_crossover_type: CrossOverType::None,
            crossover_hold_period: Duration::from_secs(crossover_hold_seconds.try_into().unwrap()),
            trend: TrendType::Unknown,
        }
    }

    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::StrongUp,
            CrossOverType::DeadCross => self.trend = TrendType::StrongDown,
            CrossOverType::BuyCross => self.trend = TrendType::Up,
            CrossOverType::SellCross => self.trend = TrendType::Down,
            CrossOverType::None => {}
        }
    }

    pub fn get(&self) -> Option<(Decimal, Decimal, Decimal)> {
        if self.val.current_ema().is_none() {
            return None;
        }
        if self.short_term_val.current_ema().is_none() {
            return None;
        }
        if self.long_term_val.current_ema().is_none() {
            return None;
        }
        Some((
            self.val.current_ema().unwrap(),
            self.short_term_val.current_ema().unwrap(),
            self.long_term_val.current_ema().unwrap(),
        ))
    }

    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
        {
            if current_signal > Decimal::ZERO {
                CrossOverType::GoldenCross
            } else {
                CrossOverType::BuyCross
            }
        } else if current_signal < current_macd && previous_signal >= previous_macd {
            if current_signal < Decimal::ZERO {
                CrossOverType::DeadCross
            } else {
                CrossOverType::SellCross
            }
        } 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()
    }

    pub fn init_crossover_state(&mut self) {
        self.crossover_last_occurrence = None;
        self.last_crossover_type = CrossOverType::None;
    }
}
