use std::{borrow::Borrow, cell::RefCell, env};

use crate::trend_value::TrendValue;
use lazy_static::lazy_static;

lazy_static! {
    static ref CROSS_EFFECTIVE_DURATION_PERIOD: u64 = env::var("CROSS_EFFECTIVE_DURATION_SECS")
        .unwrap_or_else(|_| "0".to_string())
        .parse()
        .unwrap_or(0);
    static ref TREND_CHANGE_COUNTER_THRESHOLD: u64 = env::var("TREND_CHANGE_COUNTER_THRESHOLD")
        .unwrap_or_else(|_| "0".to_string())
        .parse()
        .unwrap_or(0);
}

#[derive(Debug, Clone)]
pub struct TrendMetrics {
    pub short_term_val: TrendValue,
    pub long_term_val: TrendValue,
    pub current_value: Option<f64>,
    last_crossover_state: CrossOverType,
    cross_duration_period_counter: u64,
    cross_duration_period_max: u64,
    last_trend_type: RefCell<TrendType>,
    trend_change_counter: RefCell<u64>,
}

#[derive(Debug, Clone, PartialEq, Default)]
pub enum TrendType {
    StrongUp,
    WeakUp,
    WeakDown,
    StrongDown,
    #[default]
    NoTrend,
}

impl TrendType {
    pub fn to_numeric(&self) -> f64 {
        match self {
            TrendType::StrongUp => 0.1,
            TrendType::WeakUp => 0.05,
            TrendType::NoTrend => 0.0,
            TrendType::WeakDown => -0.05,
            TrendType::StrongDown => -0.1,
        }
    }
}

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

impl CrossOverType {
    pub fn to_numeric(&self) -> f64 {
        match self {
            CrossOverType::GoldenCross => 0.6,
            CrossOverType::DeadCross => 0.4,
            CrossOverType::None => 0.5,
        }
    }
}

impl TrendMetrics {
    pub fn new(short_period: usize, long_period: usize) -> Self {
        Self {
            short_term_val: TrendValue::new(short_period),
            long_term_val: TrendValue::new(long_period),
            current_value: None,
            last_crossover_state: CrossOverType::None,
            cross_duration_period_counter: 0,
            cross_duration_period_max: *CROSS_EFFECTIVE_DURATION_PERIOD,
            last_trend_type: TrendType::NoTrend.into(),
            trend_change_counter: 0.into(),
        }
    }

    pub fn update(&mut self, new_value: f64, short_value: f64, long_value: f64) {
        self.current_value = Some(new_value);
        self.short_term_val.update_value(short_value);
        self.long_term_val.update_value(long_value);
    }

    pub fn ema_difference_rate(&self) -> Option<f64> {
        let short_term_ema = match self.short_term_val.current_ema() {
            Some(v) => v,
            None => return None,
        };
        let long_term_ema = match self.long_term_val.current_ema() {
            Some(v) => v,
            None => return None,
        };
        let ema_difference = (short_term_ema - long_term_ema).abs();

        Some(ema_difference / long_term_ema.abs())
    }

    pub fn trend_type(&self) -> Option<TrendType> {
        let current_value = self.current_value.unwrap_or_default();
        let short_term_ema = match self.short_term_val.current_ema() {
            Some(v) => v,
            None => return None,
        };
        let long_term_ema = match self.long_term_val.current_ema() {
            Some(v) => v,
            None => return None,
        };

        let new_trend_type = match (
            current_value > short_term_ema,
            short_term_ema > long_term_ema,
        ) {
            (true, true) => TrendType::StrongUp,
            (false, true) => TrendType::WeakUp,
            (false, false) => TrendType::StrongDown,
            (true, false) => TrendType::WeakDown,
        };

        if new_trend_type != *self.last_trend_type.borrow() {
            *self.trend_change_counter.borrow_mut() += 1;
            if *self.trend_change_counter.borrow() > *TREND_CHANGE_COUNTER_THRESHOLD {
                *self.last_trend_type.borrow_mut() = new_trend_type.borrow().clone();
                *self.trend_change_counter.borrow_mut() = 0;
                return Some(new_trend_type);
            }
        } else {
            *self.trend_change_counter.borrow_mut() = 0;
        }

        Some(self.last_trend_type.borrow().clone())
    }

    pub fn is_crossover(&mut self) -> Result<CrossOverType, ()> {
        let short_term_current_ema = self.short_term_val.current_ema().ok_or(())?;
        let long_term_current_ema = self.long_term_val.current_ema().ok_or(())?;
        let short_term_previous_ema = self.short_term_val.previous_ema().ok_or(())?;
        let long_term_previous_ema = self.long_term_val.previous_ema().ok_or(())?;

        let current_state = if short_term_current_ema > long_term_current_ema
            && short_term_previous_ema <= long_term_previous_ema
        {
            CrossOverType::GoldenCross
        } else if short_term_current_ema < long_term_current_ema
            && short_term_previous_ema >= long_term_previous_ema
        {
            CrossOverType::DeadCross
        } else {
            CrossOverType::None
        };

        if self.cross_duration_period_counter > 0 {
            if self.cross_duration_period_counter > self.cross_duration_period_max {
                self.cross_duration_period_counter = 0;
            } else {
                self.cross_duration_period_counter += 1;
                return Ok(self.last_crossover_state.clone());
            }
        }

        if current_state != CrossOverType::None {
            self.cross_duration_period_counter = 1;
            self.last_crossover_state = current_state.clone();
        }

        Ok(current_state)
    }
}
