#![allow(unused)]
#![allow(warnings)]

use super::HashMap;
use crate::database_control::*;
use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::sma::{sma, SmaData};
use futures::future::try_join_all;
use serde::Deserialize;
use sqlx::FromRow;
use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};

#[derive(Clone, Debug)]
pub struct BollingerBandData {
    pub sma: f64,
    pub upperband: f64,
    pub lowerband: f64,
    pub close_time: i64,
}

impl BollingerBandData {
    fn new() -> BollingerBandData {
        let a = BollingerBandData {
            sma: 0.0,
            upperband: 0.0,
            lowerband: 0.0,
            close_time: 0,
        };
        a
    }
}

// Binance Bollingerband (SMA)
pub async fn bollingerband(
    period: usize,
    sd_factor: f64,
    input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
    filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<BollingerBandData>>, Box<dyn std::error::Error + Send + Sync>> {
    if filtered_symbols.is_empty() {
        Err(("Err"))?;
    }

    let mut sma_data_map: HashMap<String, Vec<SmaData>> =
        sma(period, input_rt_data, filtered_symbols).await?;
    let mut bb_data_wrapper: HashMap<String, Vec<BollingerBandData>> = HashMap::new();
    let mut bb_data_wrapper_arc = Arc::new(Mutex::new(bb_data_wrapper));
    let mut task_vec = Vec::new();
    for (symbol, filtered_data) in filtered_symbols {
        if input_rt_data.contains_key(symbol) && sma_data_map.contains_key(symbol) {
            let symbol_c = symbol.clone();
            let rt_data_vec_c = input_rt_data.get(symbol).unwrap().clone();
            let sma_data_vec_c = sma_data_map.get(symbol).unwrap().clone();
            let bb_data_wrapper_arc_c = Arc::clone(&bb_data_wrapper_arc);
            task_vec.push(tokio::spawn(async move {
                let mut bb_data = BollingerBandData::new();
                let mut bb_data_vec: Vec<BollingerBandData> = Vec::new();

                // if the data has shorter than buffer
                if sma_data_vec_c.len() > period {
                    let result = rt_data_vec_c.binary_search_by_key(
                        &sma_data_vec_c.first().unwrap().close_time,
                        |RealtimePriceData {
                             opclo_price,
                             open_price,
                             close_price,
                             high_price,
                             low_price,
                             close_time,
                             quote_asset_volume,
                             candle_type,
                         }| *close_time,
                    );

                    match result {
                        Ok(T) => {
                            if T <= period - 1 {
                                let mut read_data_iter = sma_data_vec_c.iter();
                                for _ in T..period - 1 {
                                    read_data_iter.next();
                                }
                                let window_iter = rt_data_vec_c.windows(period);
                                for buffer in window_iter {
                                    let mut sd_mean = 0.0;
                                    let mut standard_deviation = 0.0;
                                    for element in buffer {
                                        sd_mean += element.close_price;
                                    }
                                    sd_mean /= (period as f64);
                                    for element in buffer {
                                        standard_deviation +=
                                            (element.close_price - sd_mean).powi(2);
                                    }
                                    standard_deviation =
                                        sd_factor * ((standard_deviation / period as f64).sqrt());

                                    match read_data_iter.next() {
                                        Some(T) => {
                                            bb_data.sma = T.sma_value;
                                            bb_data.upperband = T.sma_value + standard_deviation;
                                            bb_data.lowerband = T.sma_value - standard_deviation;
                                            bb_data.close_time = T.close_time;
                                            bb_data_vec.push(bb_data.clone());
                                        }
                                        None => {}
                                    }
                                }
                            }
                        }
                        Err(E) => {}
                    }
                    let mut bb_data_wrapper_lock = bb_data_wrapper_arc_c.lock().await;
                    bb_data_wrapper_lock.insert(symbol_c, bb_data_vec.clone());
                }
            }));
        }
    }
    try_join_all(task_vec).await?;
    let a = bb_data_wrapper_arc.lock().await.to_owned();
    Ok(a)
}
