use crate::database_control::*;
use serde_json::Value;
use sqlx::{Error, FromRow};
use thirtyfour::{prelude::*, OptionRect};
use tokio::{join, time::*};

pub async fn initialize_association_record() {
    let table_name = String::from("signal_association_opinion");
    let initial_columns = vec!["name", "is_working", "opinion", "weight"];
    let initial_values = vec![
        String::from("coinmarketcap"),
        String::from("0"),
        String::from("-"),
        String::from("0.0"),
    ];
    insert_one_record(&table_name, &initial_columns, &initial_values).await;
}

pub async fn market_cap_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let association_update_table_name = String::from("signal_association_opinion");
    let association_update_condition = vec![(String::from("name"), String::from("coinmarketcap"))];

    let caps = DesiredCapabilities::chrome();
    let driver = WebDriver::new("http://localhost:9515/", &caps).await?;

    // get the current window handle
    let handle = driver.current_window_handle().await?;

    // open browser and url
    driver.get("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=5m&count=60").await?;
    driver.execute_script(r#"window.open("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=5m&count=1440");"#).await?;
    // driver.get("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=30m").await?;
    // driver.execute_script(r#"window.open("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=12h");"#).await?;

    // get window handles and switch to the new tab
    let handles = driver.window_handles().await?;
    // switch the first tab
    let elem = driver.find_element(By::Tag("pre")).await?;
    let text = elem.text().await?;

    // JSON Parsing
    let mut v: Value = serde_json::from_str(text.as_str()).unwrap();

    if v.as_object().unwrap().get("data").is_none() {
        return Err("Err")?;
    }

    let mut into_vec = v
        .as_object()
        .unwrap()
        .get("data")
        .unwrap()
        .as_object()
        .unwrap();

    // initialize variables
    let mut market_caps: Vec<f64> = Vec::new();
    let mut last_updated_time = String::new();
    let mut normalized_values: Vec<f64> = Vec::new();
    let mut degree_values: Vec<f64> = Vec::new();
    let mut weighted_average_degree: f64 = 0.0;

    last_updated_time = into_vec.keys().last().unwrap().clone();
    for element in into_vec {
        market_caps.push(
            element
                .1
                .as_array()
                .unwrap()
                .first()
                .unwrap()
                .as_f64()
                .unwrap(),
        );
    }

    let mut market_cap_min = market_caps
        .iter()
        .copied()
        .min_by(|a, b| a.partial_cmp(b).unwrap())
        .unwrap();
    let mut market_cap_max = market_caps
        .iter()
        .copied()
        .max_by(|a, b| a.partial_cmp(b).unwrap())
        .unwrap();

    for element in &market_caps {
        normalized_values.push((element - market_cap_min) / (market_cap_max - market_cap_min));
    }
    for element in &normalized_values {
        degree_values.push(
            (normalized_values.last().unwrap() - element)
                .atan()
                .to_degrees(),
        );
    }
    degree_values.pop();

    for element in degree_values.iter().copied().enumerate() {
        weighted_average_degree += element.1 * (element.0 + 1) as f64;
    }
    // weighted_average_degree /= 45.0;
    weighted_average_degree /= 1770.0; // summation from 1 to 59

    // initialize variables for Kalman Filtering
    let h = 1.0;
    let a = 1.0;
    let mut q = 0.001;
    let r = 1.0;
    let mut x = weighted_average_degree;
    let mut p = 5000.0;
    let mut xp = 0.0;
    let mut pp = 0.0;
    let mut k = 0.0; // Kalman gain
    let mut z = 0.0; // measurement

    // variables for using database
    #[derive(FromRow)]
    struct SelectData {
        market_cap_index: f64,
        minimum: f64,
        maximum: f64,
        transition_point: f64,
        liquidation_signal: i8,
        negative_buy_signal: i8,
        transition_buy_signal: i8,
    };
    let table_name = String::from("market_cap_index");
    let update_condition = vec![(String::from("id"), String::from("1"))];
    let update_condition2 = vec![(String::from("id"), String::from("2"))];
    let select_columns = String::from("market_cap_index, minimum, maximum, transition_point, liquidation_signal, negative_buy_signal, transition_buy_signal");
    let select_data_structure = SelectData {
        market_cap_index: 0.0,
        minimum: 0.0,
        maximum: 0.0,
        transition_point: 0.0,
        liquidation_signal: 0,
        negative_buy_signal: 0,
        transition_buy_signal: 0,
    };
    let select_condition = None;

    // variables for EMA calculation of 5hr angle
    let mut prev_angle: Option<f64> = None;
    let mut current_angle: Option<f64> = None;

    driver
        .set_window_rect(OptionRect::new().with_size(1, 1))
        .await?;
    // refresh page and save JSON repeatedly
    loop {
        // refresh page in the first tab
        driver.refresh().await?;
        // switch to the second tab and refresh page
        driver.switch_to().window(&handles[1]).await?;
        driver.refresh().await?;
        // switch to the first tab
        driver.switch_to().window(&handle).await?;
        driver.minimize_window().await?;

        // task in the first tab starts
        // save JSON
        let elem = driver.find_element(By::Tag("pre")).await;
        let mut text;
        match elem {
            Ok(T) => {
                text = T.text().await?;
                v = serde_json::from_str(text.as_str()).unwrap();

                if v.as_object().is_none() {
                    return Err("Err")?;
                }

                if v.as_object().unwrap().get("data").is_none() {
                    return Err("Err")?;
                }

                into_vec = v
                    .as_object()
                    .unwrap()
                    .get("data")
                    .unwrap()
                    .as_object()
                    .unwrap();

                if last_updated_time.as_str() != into_vec.keys().last().unwrap().as_str() {
                    // when working
                    let mut association_update_values =
                        vec![(String::from("is_working"), String::from("1"))];
                    let mut update_value_add: (String, String) =
                        (String::from("opinion"), String::from("KEEP"));

                    last_updated_time = into_vec.keys().last().unwrap().clone();

                    market_caps.clear();
                    normalized_values.clear();
                    degree_values.clear();
                    weighted_average_degree = 0.0;

                    for element in into_vec {
                        market_caps.push(
                            element
                                .1
                                .as_array()
                                .unwrap()
                                .first()
                                .unwrap()
                                .as_f64()
                                .unwrap(),
                        );
                    }

                    market_cap_min = market_caps
                        .iter()
                        .copied()
                        .min_by(|a, b| a.partial_cmp(b).unwrap())
                        .unwrap();
                    market_cap_max = market_caps
                        .iter()
                        .copied()
                        .max_by(|a, b| a.partial_cmp(b).unwrap())
                        .unwrap();

                    for element in &market_caps {
                        normalized_values
                            .push((element - market_cap_min) / (market_cap_max - market_cap_min));
                    }

                    for element in &normalized_values {
                        degree_values.push(
                            (normalized_values.last().unwrap() - element)
                                .atan()
                                .to_degrees(),
                        );
                    }
                    degree_values.pop();

                    for element in degree_values.iter().copied().enumerate() {
                        weighted_average_degree += element.1 * (element.0 + 1) as f64;
                    }
                    // weighted_average_degree /= 45.0;
                    weighted_average_degree /= 1770.0; // summation from 1 to 59

                    // using database
                    let mut update_values = vec![(String::from("market_cap_index"), x.to_string())];
                    update_record2(&table_name, &update_values, &update_condition)
                        .await
                        .unwrap();
                    let select_result = select_record(
                        &table_name,
                        &select_columns,
                        &select_condition,
                        &select_data_structure,
                    )
                    .await?;
                    let record = select_result.first().unwrap();
                    let record2 = select_result.last().unwrap();

                    // if record2.market_cap_index > 0.0  { q=0.01; }   // lower q than r means filtering is insensitive to turbulance(input noise) -> meaning more delayed filtering
                    // else { q=0.004; }

                    // 1st order Kalman Filtering for weighted_average_degree
                    z = weighted_average_degree;
                    xp = x;
                    pp = p + q;
                    k = pp / (pp + r);
                    x = xp + k * (z - xp);
                    p = pp - k * pp;

                    if record.market_cap_index > 0.0 {
                        if record.minimum != 0.0 {
                            update_values = vec![
                                (String::from("minimum"), 0.0.to_string()),
                                (String::from("negative_buy_signal"), 0.to_string()),
                                (String::from("transition_point"), 0.0.to_string()),
                            ];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }

                        if record.maximum < record.market_cap_index
                            && record.liquidation_signal == 0
                        {
                            if record.transition_point == 0.0 || record.minimum < 0.0 {
                                update_values = vec![(
                                    String::from("maximum"),
                                    record.market_cap_index.to_string(),
                                )];
                                update_record2(&table_name, &update_values, &update_condition)
                                    .await
                                    .unwrap();
                            } else {
                                if record.market_cap_index >= 5.0
                                    && (record.market_cap_index - record.maximum) / record.maximum
                                        > 0.2
                                {
                                    // 현재 5 이상 지수가 이전 대비 20% 증가하면 추가 매수하기.
                                    update_values = vec![
                                        (
                                            String::from("maximum"),
                                            record.market_cap_index.to_string(),
                                        ),
                                        (String::from("transition_buy_signal"), 1.to_string()),
                                    ];
                                    update_record2(&table_name, &update_values, &update_condition)
                                        .await
                                        .unwrap();
                                } else {
                                    update_values = vec![
                                        (
                                            String::from("maximum"),
                                            record.market_cap_index.to_string(),
                                        ),
                                        (String::from("transition_buy_signal"), 0.to_string()),
                                    ];
                                    update_record2(&table_name, &update_values, &update_condition)
                                        .await
                                        .unwrap();
                                }
                            }
                        }

                        if record.maximum > record.market_cap_index
                            && record.liquidation_signal == 0
                        {
                            update_values = vec![
                                (String::from("liquidation_signal"), 1.to_string()),
                                (
                                    String::from("transition_point"),
                                    record.market_cap_index.to_string(),
                                ),
                                (String::from("transition_buy_signal"), 0.to_string()),
                            ];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }

                        if record.liquidation_signal == 1
                            && record.market_cap_index < record.transition_point
                        {
                            update_values = vec![(
                                String::from("transition_point"),
                                record.market_cap_index.to_string(),
                            )];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }

                        if record.liquidation_signal == 1
                            && record.market_cap_index > record.transition_point
                        {
                            update_values = vec![
                                (String::from("maximum"), record.market_cap_index.to_string()),
                                (String::from("liquidation_signal"), 0.to_string()),
                            ];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }
                    } else {
                        if record.maximum != 0.0 {
                            update_values = vec![
                                (String::from("maximum"), 0.0.to_string()),
                                (String::from("transition_point"), 0.0.to_string()),
                            ];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }

                        if record.market_cap_index < record.minimum
                            && record.negative_buy_signal == 0
                        {
                            update_values = vec![
                                (String::from("minimum"), record.market_cap_index.to_string()),
                                (String::from("liquidation_signal"), 1.to_string()),
                            ];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }
                        if record.market_cap_index > record.minimum
                            && record.liquidation_signal == 1
                            && record.negative_buy_signal == 0
                        {
                            if record2.market_cap_index > 0.0 {
                                // considering the market is going up
                                update_values = vec![
                                    (String::from("negative_buy_signal"), 1.to_string()),
                                    (
                                        String::from("transition_point"),
                                        record.market_cap_index.to_string(),
                                    ),
                                    (String::from("liquidation_signal"), 0.to_string()),
                                ];
                                update_record2(&table_name, &update_values, &update_condition)
                                    .await
                                    .unwrap();
                            } else {
                                // considering the market is doing down
                                if record.market_cap_index < -20.0 {
                                    update_values = vec![
                                        (String::from("negative_buy_signal"), 1.to_string()),
                                        (
                                            String::from("transition_point"),
                                            record.market_cap_index.to_string(),
                                        ),
                                        (String::from("liquidation_signal"), 0.to_string()),
                                    ];
                                    update_record2(&table_name, &update_values, &update_condition)
                                        .await
                                        .unwrap();
                                } else {
                                    update_values = vec![
                                        (
                                            String::from("transition_point"),
                                            record.market_cap_index.to_string(),
                                        ),
                                        (String::from("liquidation_signal"), 0.to_string()),
                                    ];
                                    update_record2(&table_name, &update_values, &update_condition)
                                        .await
                                        .unwrap();
                                }
                            }
                        }
                        if record.negative_buy_signal == 1
                            && record.market_cap_index > record.transition_point
                        {
                            if record.market_cap_index > -5.0 {
                                update_values = vec![
                                    (String::from("minimum"), record.market_cap_index.to_string()),
                                    (String::from("negative_buy_signal"), 0.to_string()),
                                    (String::from("transition_point"), 0.0.to_string()),
                                ];
                                update_record2(&table_name, &update_values, &update_condition)
                                    .await
                                    .unwrap();
                            } else {
                                update_values = vec![(
                                    String::from("transition_point"),
                                    record.market_cap_index.to_string(),
                                )];
                                update_record2(&table_name, &update_values, &update_condition)
                                    .await
                                    .unwrap();
                            }
                        }
                        if record.negative_buy_signal == 1
                            && record.market_cap_index < record.transition_point
                        {
                            update_values = vec![
                                (String::from("negative_buy_signal"), 0.to_string()),
                                (String::from("liquidation_signal"), 1.to_string()),
                                (String::from("minimum"), record.market_cap_index.to_string()),
                            ];
                            update_record2(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
                        }
                    } // task in the first tab ends

                    // task in the second tab starts
                    driver.switch_to().window(&handles[1]).await?;
                    // save JSON
                    let elem = driver.find_element(By::Tag("pre")).await?;
                    let text = elem.text().await?;
                    v = serde_json::from_str(text.as_str()).unwrap();

                    if v.as_object().is_none() {
                        return Err("Err")?;
                    }

                    if v.as_object().unwrap().get("data").is_none() {
                        return Err("Err")?;
                    }

                    into_vec = v
                        .as_object()
                        .unwrap()
                        .get("data")
                        .unwrap()
                        .as_object()
                        .unwrap();

                    market_caps.clear();
                    normalized_values.clear();
                    degree_values.clear();
                    weighted_average_degree = 0.0;

                    for element in into_vec {
                        market_caps.push(
                            element
                                .1
                                .as_array()
                                .unwrap()
                                .first()
                                .unwrap()
                                .as_f64()
                                .unwrap(),
                        );
                    }

                    market_cap_min = market_caps
                        .iter()
                        .copied()
                        .min_by(|a, b| a.partial_cmp(b).unwrap())
                        .unwrap();
                    market_cap_max = market_caps
                        .iter()
                        .copied()
                        .max_by(|a, b| a.partial_cmp(b).unwrap())
                        .unwrap();

                    for element in &market_caps {
                        normalized_values
                            .push((element - market_cap_min) / (market_cap_max - market_cap_min));
                    }

                    for element in &normalized_values {
                        degree_values.push(
                            (normalized_values.last().unwrap() - element)
                                .atan()
                                .to_degrees(),
                        );
                    }
                    degree_values.pop();

                    for element in degree_values.iter().copied().enumerate() {
                        // weighted_average_degree += element.1 * (element.0 + 1) as f64;
                        weighted_average_degree += element.1; // normal average
                    }
                    // weighted_average_degree /= 45.0;
                    // weighted_average_degree /= 1036080.0; // summation from 1 to 1439
                    weighted_average_degree /= degree_values.len() as f64; // normal average

                    if prev_angle.is_some() && current_angle.is_some() {
                        current_angle = Some(
                            ((1.0 / 4.0) * weighted_average_degree)
                                + ((3.0 / 4.0) * prev_angle.unwrap()),
                        );

                        // if current_angle.unwrap() >= 3.0 {
                        //     update_value_add = ((String::from("opinion"), String::from("UP")));
                        //     q=0.01;
                        // } else if current_angle.unwrap() <= -3.0 {
                        //     update_value_add = ((String::from("opinion"), String::from("DOWN")));
                        //     q=0.004;
                        // } else {
                        //     update_value_add = ((String::from("opinion"), String::from("KEEP")));
                        //     q=0.01;
                        // }

                        if current_angle.unwrap() > 3.0 {
                            if current_angle.unwrap() - prev_angle.unwrap() <= -0.15 {
                                update_value_add =
                                    ((String::from("opinion"), String::from("DOWN")));
                                q = 0.004;
                            } else {
                                update_value_add = ((String::from("opinion"), String::from("UP")));
                                q = 0.01;
                            }
                        } else if current_angle.unwrap() < -3.0 {
                            if current_angle.unwrap() - prev_angle.unwrap() >= 0.15 {
                                update_value_add = ((String::from("opinion"), String::from("UP")));
                                q = 0.01;
                            } else {
                                update_value_add =
                                    ((String::from("opinion"), String::from("DOWN")));
                                q = 0.004;
                            }
                        } else {
                            update_value_add = ((String::from("opinion"), String::from("KEEP")));
                            q = 0.01;
                        }

                        prev_angle = current_angle;
                    } else {
                        current_angle = Some(weighted_average_degree);
                        prev_angle = Some(weighted_average_degree);
                    }

                    // using database
                    let update_values = vec![(
                        String::from("market_cap_index"),
                        current_angle.unwrap().to_string(),
                    )];
                    update_record2(&table_name, &update_values, &update_condition2)
                        .await
                        .unwrap();
                    // return to the first tab
                    driver.switch_to().window(&handle).await?;
                    driver.minimize_window().await?;

                    // update record in [signal_association_opinion]
                    association_update_values.push(update_value_add);
                    update_record2(
                        &association_update_table_name,
                        &association_update_values,
                        &association_update_condition,
                    )
                    .await;
                    // println!("5hr_angle:{},5day_angle:{}", x, current_angle.unwrap());
                }
            }
            Err(E) => {
                // when not working
                let association_update_values = vec![
                    (String::from("is_working"), String::from("0")),
                    (String::from("opinion"), String::from("-")),
                    (String::from("weight"), String::from("0.0")),
                ];
                update_record2(
                    &association_update_table_name,
                    &association_update_values,
                    &association_update_condition,
                )
                .await;
            }
        }

        // sleep for 60 seconds
        sleep(Duration::from_nanos(60_000_000_000)).await;

        // println!("market_cap_index 완료");
    }
    Ok(())
}
