#![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 futures::future::try_join_all;
use serde::Deserialize;
use sqlx::FromRow;
use std::f64::NAN;
use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};

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

// Binance RSI (EMA, Closeprice, Wilder's weight, 150 candles)
pub async fn rsi(
    rsi_number: usize,
    input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
    filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<RsiData>>, Box<dyn std::error::Error + Send + Sync>> {
    let instant = Instant::now();

    let mut rsi_data_wrapper: HashMap<String, Vec<RsiData>> = HashMap::new();
    let mut rsi_data_wrapper_arc = Arc::new(Mutex::new(rsi_data_wrapper));

    let mut task_vec = Vec::new();
    for (symbol, filtered_data) in filtered_symbols {
        if let Some(rt_price_vec) = input_rt_data.get(symbol) {
            let rsi_data_wrapper_arc_c = Arc::clone(&rsi_data_wrapper_arc);
            let symbol_c = symbol.clone();
            let mut input_rt_data_c = rt_price_vec.clone();
            task_vec.push(tokio::spawn(async move {
                let mut rsi_data_vec: Vec<RsiData> = Vec::new();
                let mut rsi_data = RsiData::new();
                let mut read_data_vec: Vec<RealtimePriceData> = Vec::new();
                let mut read_price_buffer: Vec<RealtimePriceData> = Vec::new();
                let mut prev_price: f64 = 0.0;
                let mut current_price: f64 = 0.0;
                let mut sum_increase: f64 = 0.0;
                let mut sum_decrease: f64 = 0.0;
                let mut rsi: f64 = 0.0;
                let mut last_close_time = 0;

                if input_rt_data_c.len() < rsi_number + 1 {
                    rsi_data.rsi_value = 0.0;
                    rsi_data.close_time = 0;
                    rsi_data_vec.push(rsi_data.clone());
                } else {
                    if input_rt_data_c.len() >= (150 + rsi_number) as usize {
                        input_rt_data_c.reverse();
                        input_rt_data_c.truncate((150 + rsi_number) as usize);
                        input_rt_data_c.reverse();
                    }
                    let window_iter = input_rt_data_c.windows(rsi_number + 1);

                    let mut prev_avg_ups: Option<f64> = None;
                    let mut prev_avg_downs: Option<f64> = None;
                    let mut current_avg_ups: Option<f64> = None;
                    let mut current_avg_downs: Option<f64> = None;

                    for buffer in window_iter {
                        let mut up_vec: Vec<f64> = Vec::new();
                        let mut down_vec: Vec<f64> = Vec::new();

                        let buffer_window = buffer.windows(2);
                        for element in buffer_window {
                            if element.last().unwrap().close_price
                                - element.first().unwrap().close_price
                                > 0.0
                            {
                                up_vec.push(
                                    element.last().unwrap().close_price
                                        - element.first().unwrap().close_price,
                                );
                                down_vec.push(0.0);
                            } else if element.last().unwrap().close_price
                                - element.first().unwrap().close_price
                                < 0.0
                            {
                                up_vec.push(0.0);
                                down_vec.push(
                                    (element.last().unwrap().close_price
                                        - element.first().unwrap().close_price)
                                        .abs(),
                                );
                            } else {
                                up_vec.push(0.0);
                                down_vec.push(0.0);
                            }
                        }

                        if current_avg_ups == None
                            && current_avg_downs == None
                            && prev_avg_ups == None
                            && prev_avg_downs == None
                        {
                            // initial averages based on SMA
                            let mut avg_ups: f64 = up_vec.iter().sum();
                            avg_ups /= rsi_number as f64;
                            let mut avg_downs: f64 = down_vec.iter().sum();
                            avg_downs /= rsi_number as f64;

                            current_avg_ups = Some(avg_ups);
                            current_avg_downs = Some(avg_downs);
                        } else {
                            // [EMA]
                            let alpha = 1.0 / (rsi_number as f64); // Wilder's weight
                            current_avg_ups = Some(
                                alpha * up_vec.last().unwrap()
                                    + ((1.0 - alpha) * prev_avg_ups.unwrap()),
                            );
                            current_avg_downs = Some(
                                alpha * down_vec.last().unwrap()
                                    + ((1.0 - alpha) * prev_avg_downs.unwrap()),
                            );
                        }
                        prev_avg_ups = current_avg_ups;
                        prev_avg_downs = current_avg_downs;

                        let rs =
                            current_avg_ups.unwrap() / (current_avg_downs.unwrap() + 0.00000001); // 0.00000001 is used to avoid division by 0

                        let rsi = 100.0 - (100.0 / (1.0 + rs));

                        rsi_data.rsi_value = rsi;
                        rsi_data.close_time = buffer.last().unwrap().close_time;
                        rsi_data_vec.push(rsi_data.clone());
                    }
                    let mut rsi_data_wrapper_lock = rsi_data_wrapper_arc_c.lock().await;
                    rsi_data_wrapper_lock.insert(symbol_c, rsi_data_vec.clone());
                }
            }));
        }
    }
    try_join_all(task_vec).await?;
    let a = rsi_data_wrapper_arc.lock().await.to_owned();
    Ok(a)
}
