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

use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::sma::{SmaData, sma};
use crate::strategy_team::FilteredData;
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: &Vec<(String, Vec<RealtimePriceData>)>,
    filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<BollingerBandData>)>, Box<dyn std::error::Error + Send + Sync>> {
    if filtered_symbols.is_empty() {
        Err(("Err"))?;
    }

    let mut sma_data_vec: Vec<(String, Vec<SmaData>)> = sma(period, input_rt_data, filtered_symbols).await?;

    let mut bb_data_wrapper: Vec<(String, Vec<BollingerBandData>)> = Vec::new();
    let mut bb_data_wrapper_arc = Arc::new(Mutex::new(bb_data_wrapper));
    let mut task_vec = Vec::new();
    for filtered_symbol in filtered_symbols {
        let filtered_symbol_c = filtered_symbol.clone();
        let rt_data_vec_c: Vec<(String, Vec<RealtimePriceData>)> = input_rt_data.clone();
        let sma_data_vec_c = sma_data_vec.clone();
        let bb_data_wrapper_arc_c = Arc::clone(&bb_data_wrapper_arc);
        task_vec.push(tokio::spawn(async move {
            let search_result1 = rt_data_vec_c.clone().iter().position(|x| x.0 == *filtered_symbol_c.symbol);
            let search_result2 = sma_data_vec_c.iter().position(|x| x.0 == *filtered_symbol_c.symbol);

            if search_result1.is_some() && search_result2.is_some() {
                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[search_result2.unwrap()].1.len() > period {
                    let result = rt_data_vec_c[search_result1.unwrap()].1.binary_search_by_key(
                        &sma_data_vec_c[search_result2.unwrap()].1.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[search_result2.unwrap()].1.iter();
                                for _ in T..period - 1 {
                                    read_data_iter.next();
                                }
                                let window_iter =
                                rt_data_vec_c[search_result1.unwrap()].1.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
                        .push((filtered_symbol_c.symbol.clone(), bb_data_vec.clone()));
                }
            }
        }));
    }
    try_join_all(task_vec).await?;
    let a = bb_data_wrapper_arc.lock().await.to_owned();
    Ok(a)
}
