#![allow(unused)]
#![allow(warnings)]
use super::ema::{ema, EmaData};
use super::FilteredDataValue;
use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all;
use serde::Deserialize;
use sqlx::FromRow;
use std::collections::HashMap;
use std::{iter::zip, sync::Arc};
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};

#[derive(Clone, Debug)]
pub struct TemaData {
    pub tema_value: f64,
    pub close_time: i64,
}
impl TemaData {
    fn new() -> TemaData {
        let a = TemaData {
            tema_value: 0.0,
            close_time: 0,
        };
        a
    }
}

// Binance TEMA (closeprice)
pub async fn tema(
    moving_number: usize,
    input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
    filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<TemaData>>, Box<dyn std::error::Error + Send + Sync>> {
    if filtered_symbols.is_empty() {
        Err("Err")?;
    }
    let e1 = ema(moving_number, input_rt_data, filtered_symbols).await?;

    // calculate EMA(EMA(n))
    let alpha: f64 = 2.0 / (moving_number as f64 + 1.0);
    let mut ema_t: f64 = 0.0;
    let mut ema_prev: f64 = 0.0;

    let mut ema_data_wrapper: HashMap<String, Vec<EmaData>> = HashMap::new();
    let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
    let mut task_vec = Vec::new();
    for (symbol, e1_ema_vec) in &e1 {
        let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
        let mut ema_data = EmaData::new();
        let mut ema_data_vec: Vec<EmaData> = Vec::new();
        let symbol_c = symbol.clone();
        let e1_ema_vec_c = e1_ema_vec.clone();
        task_vec.push(tokio::spawn(async move {
            if e1_ema_vec_c.len() > moving_number {
                let partial_vec1 = e1_ema_vec_c.get(..moving_number).unwrap();
                let partial_vec2 = e1_ema_vec_c.get(moving_number..).unwrap();

                let mut sma_for_initial_value = 0.0;
                for element in partial_vec1 {
                    sma_for_initial_value += element.ema_value;
                }
                sma_for_initial_value /= moving_number as f64;

                ema_data.ema_value = sma_for_initial_value;
                ema_data.close_time = partial_vec1.last().unwrap().close_time;
                ema_data_vec.push(ema_data.clone());

                ema_prev = sma_for_initial_value;

                for element in partial_vec2 {
                    ema_t = (1.0 - alpha) * ema_prev + alpha * element.ema_value;

                    ema_data.ema_value = ema_t;
                    ema_data.close_time = element.close_time;
                    ema_data_vec.push(ema_data.clone());

                    ema_prev = ema_t;
                }
                let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
                ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
            }
        }));
    }
    try_join_all(task_vec).await?;
    let e2 = ema_data_wrapper_arc.lock().await.to_owned();

    // calculate EMA(EMA(EMA(n)))
    let mut ema_data_wrapper: HashMap<String, Vec<EmaData>> = HashMap::new();
    let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
    let mut task_vec = Vec::new();
    for (symbol, e2_ema_vec) in &e2 {
        let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
        let mut ema_data = EmaData::new();
        let mut ema_data_vec: Vec<EmaData> = Vec::new();
        let symbol_c = symbol.clone();
        let e2_ema_vec_c = e2_ema_vec.clone();
        task_vec.push(tokio::spawn(async move {
            if e2_ema_vec_c.len() > moving_number {
                let partial_vec1 = e2_ema_vec_c.get(..moving_number).unwrap();
                let partial_vec2 = e2_ema_vec_c.get(moving_number..).unwrap();

                let mut sma_for_initial_value = 0.0;
                for element in partial_vec1 {
                    sma_for_initial_value += element.ema_value;
                }
                sma_for_initial_value /= moving_number as f64;

                ema_data.ema_value = sma_for_initial_value;
                ema_data.close_time = partial_vec1.last().unwrap().close_time;
                ema_data_vec.push(ema_data.clone());

                ema_prev = sma_for_initial_value;

                for element in partial_vec2 {
                    ema_t = (1.0 - alpha) * ema_prev + alpha * element.ema_value;

                    ema_data.ema_value = ema_t;
                    ema_data.close_time = element.close_time;
                    ema_data_vec.push(ema_data.clone());

                    ema_prev = ema_t;
                }
                let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
                ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
            }
        }));
    }
    try_join_all(task_vec).await?;
    let e3 = ema_data_wrapper_arc.lock().await.to_owned();

    let mut tema_data_wrapper: HashMap<String, Vec<TemaData>> = HashMap::new();
    let mut tema_data_wrapper_arc = Arc::new(Mutex::new(tema_data_wrapper));
    let mut task_vec = Vec::new();
    for (symbol, e3_vec) in e3 {
        if let (Some(e1_vec), Some(e2_vec)) = (e1.get(&symbol), e2.get(&symbol)) {
            let tema_data_wrapper_arc_c: Arc<Mutex<HashMap<String, Vec<TemaData>>>> =
                Arc::clone(&tema_data_wrapper_arc);
            let symbol_c = symbol.clone();
            let e1_vec_c = e1_vec.clone();
            let e2_vec_c = e2_vec.clone();
            task_vec.push(tokio::spawn(async move {
                if e3_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time
                    && e3_vec.last().unwrap().close_time == e2_vec_c.last().unwrap().close_time
                    && e3_vec.len() < e1_vec_c.len()
                    && e3_vec.len() < e2_vec_c.len()
                    && e2_vec_c.len() < e1_vec_c.len()
                {
                    let mut tema_data_vec: Vec<TemaData> = Vec::new();
                    let e1_vec_part = e1_vec_c.get(e1_vec_c.len() - e3_vec.len()..).unwrap();
                    let e2_vec_part = e2_vec_c.get(e2_vec_c.len() - e3_vec.len()..).unwrap();
                    let zipped_e1_e2 = e1_vec_part.iter().zip(e2_vec_part.iter());
                    let zipped_e1_e2_e3 = zipped_e1_e2.zip(e3_vec.iter());
                    for element in zipped_e1_e2_e3 {
                        let mut tema_data = TemaData::new();
                        tema_data.close_time = element.0 .0.close_time;
                        tema_data.tema_value = (3.0
                            * (element.0 .0.ema_value - element.0 .1.ema_value))
                            + element.1.ema_value;
                        tema_data_vec.push(tema_data);
                    }
                    let mut dema_data_wrapper_lock = tema_data_wrapper_arc_c.lock().await;
                    dema_data_wrapper_lock.insert(symbol_c, tema_data_vec.clone());
                }
            }));
        }
    }
    try_join_all(task_vec).await?;
    let a = tema_data_wrapper_arc.lock().await.to_owned();

    Ok(a)
}
