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

#[derive(Clone, Debug)]
pub struct EmaData {
    pub ema_value: f64,
    pub close_time: i64,
}
impl EmaData {
    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: &Vec<(String, Vec<RealtimePriceData>)>,
    filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(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: Vec<(String, Vec<EmaData>)> = Vec::new();
    let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
    let mut task_vec = Vec::new();
    for filtered_elem in filtered_symbols {
        let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *filtered_elem.symbol);

        match symbol_search_result {
            Some(T) => {
                let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
                let filtered_elem_c = filtered_elem.clone();
                let mut ema_data = EmaData::new();
                let mut ema_data_vec: Vec<EmaData> = Vec::new();
                let input_rt_data_c = input_rt_data.clone();
                task_vec.push(tokio::spawn(async move {
                    if input_rt_data_c[T].1.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 = input_rt_data_c[T].1.get(..moving_number).unwrap();
                        let partial_vec2 = input_rt_data_c[T].1.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.push((filtered_elem_c.symbol.clone(), ema_data_vec.clone()));
                }));
            }
            None => {}
        }
    }
    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: &Vec<(String, Vec<RealtimePriceData>)>,
    filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(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: Vec<(String, Vec<EmaData>)> = Vec::new();
    let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
    let mut task_vec = Vec::new();
    for filtered_elem in filtered_symbols {
        let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *filtered_elem.symbol);

        match symbol_search_result {
            Some(T) => {
                let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
                let filtered_elem_c = filtered_elem.clone();
                let mut ema_data = EmaData::new();
                let mut ema_data_vec: Vec<EmaData> = Vec::new();
                let input_rt_data_c = input_rt_data.clone();
                task_vec.push(tokio::spawn(async move {
                    if input_rt_data_c[T].1.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 = input_rt_data_c[T].1.get(..moving_number).unwrap();
                        let partial_vec2 = input_rt_data_c[T].1.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.opclo_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.push((filtered_elem_c.symbol.clone(), ema_data_vec.clone()));
                }));
            }
            None => {}
        }
    }
    try_join_all(task_vec).await?;
    let a = ema_data_wrapper_arc.lock().await.to_owned();
    Ok(a)
}