#![allow(unused)]
#![allow(warnings)]
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::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};

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

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

    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, filtered_data) in filtered_symbols {
        if let Some(rt_data_vec) = input_rt_data.get(symbol) {
            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 rt_data_vec_c = rt_data_vec.clone();
            task_vec.push(tokio::spawn(async move {
                if rt_data_vec_c.len() > moving_number {
                    let partial_vec1 = rt_data_vec_c.get(..moving_number).unwrap();
                    let partial_vec2 = rt_data_vec_c.get(moving_number..).unwrap();

                    let mut sma_for_initial_value = 0.0;
                    for element in partial_vec1 {
                        sma_for_initial_value += element.close_price;
                    }
                    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.close_price;

                        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 a = ema_data_wrapper_arc.lock().await.to_owned();
    Ok(a)
}

// Binance EMA ((open+close)/2)
pub async fn ema_opclo(
    moving_number: usize,
    input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
    filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<EmaData>>, Box<dyn std::error::Error + Send + Sync>> {
    if filtered_symbols.is_empty() {
        Err("Err")?;
    }

    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, filtered_data) in filtered_symbols {
        if let Some(rt_data_vec) = input_rt_data.get(symbol) {
            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 rt_data_vec_c = rt_data_vec.clone();
            task_vec.push(tokio::spawn(async move {
                if rt_data_vec_c.len() < moving_number {
                    ema_data.ema_value = 0.0;
                    ema_data.close_time = 0;
                    ema_data_vec.push(ema_data.clone());
                } else {
                    let partial_vec1 = rt_data_vec_c.get(..moving_number).unwrap();
                    let partial_vec2 = rt_data_vec_c.get(moving_number..).unwrap();

                    let mut sma_for_initial_value = 0.0;
                    for element in partial_vec1 {
                        sma_for_initial_value += element.opclo_price;
                    }
                    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.close_price;

                        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 a = ema_data_wrapper_arc.lock().await.to_owned();
    Ok(a)
}
