use crate::database_control::*;
use chrono::naive::NaiveDate;
use reqwest::{Client, ClientBuilder};
use serde_json::Value;
use sqlx::{Error, FromRow};
use tokio::{join, time::Duration, time::*};

#[derive(Debug, FromRow, Clone)]
pub struct DollarIndex {
    pub date: NaiveDate,
    pub value: String,
}

#[derive(Debug, FromRow)]
pub struct SignalAssociaionOpinion {
    pub id: i64,
    pub name: String,
    pub is_working: bool,
    pub opinion: String,
    pub weight: f64,
}

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("dollar_index"),
        String::from("0"),
        String::from("-"),
        String::from("0.0"),
    ];
    insert_one_record(&table_name, &initial_columns, &initial_values).await;
}

pub async fn initialize_record() {
    let mut dollar_index_vec: Vec<DollarIndex> = Vec::new();

    loop {
        let result = reqeust_dollar_index().await;
        match result {
            Ok(T) => {
                dollar_index_vec = T;
                break;
            }
            Err(E) => {
                println!("{}", E);
                let association_update_table_name = String::from("signal_association_opinion");
                let association_update_condition =
                    vec![(String::from("name"), String::from("dollar_index"))];
                let association_update_values: Vec<(String, String)> = vec![
                    (String::from("is_working"), 0.to_string()),
                    (String::from("opinion"), String::from("-")),
                ];

                update_record2(
                    &association_update_table_name,
                    &association_update_values,
                    &association_update_condition,
                )
                .await;
                sleep(Duration::from_secs(5)).await;
            }
        }
    }

    let insert_table_name = String::from("dollar_index");
    let insert_columns = vec!["date", "value"];
    let mut initial_values: Vec<String> = Vec::new();
    let mut insert_value_container: Vec<Vec<String>> = Vec::new();
    for element in dollar_index_vec {
        let mut insert_value_build: Vec<String> = Vec::new();

        insert_value_build.push(element.date.to_string());
        insert_value_build.push(element.value);
        insert_value_container.push(insert_value_build.clone());
    }

    insert_records(&insert_table_name, &insert_columns, &insert_value_container).await;
}

pub async fn reqeust_dollar_index(
) -> Result<Vec<DollarIndex>, Box<dyn std::error::Error + Send + Sync>> {
    let client = ClientBuilder::new()
        .timeout(tokio::time::Duration::from_secs(30))
        .build()?;
    let mut url = String::from("https://api.stlouisfed.org/fred/series/observations?series_id=DTWEXAFEGS&api_key=d4af0a1b811bd0324c4210ca59db99fb&file_type=json&limit=90&sort_order=desc");

    let res = client.get(&url).send().await?;

    let body = res.text_with_charset("utf-8").await?;

    let v = serde_json::from_str::<Value>(body.as_str())?;

    let mut a = v.get("observations").unwrap().as_array().unwrap();

    let mut vec: Vec<DollarIndex> = Vec::new();
    let mut dollar_index_info = DollarIndex {
        date: chrono::naive::MAX_DATE,
        value: String::new(),
    };
    let parse_from_str = NaiveDate::parse_from_str;

    for element in a {
        dollar_index_info.date =
            parse_from_str(element.get("date").unwrap().as_str().unwrap(), "%Y-%m-%d").unwrap();
        dollar_index_info.value = element.get("value").unwrap().as_str().unwrap().to_string();
        vec.push(dollar_index_info.clone());
    }

    Ok(vec)
}

// Nominal Advanced Foreign Economies U.S. Dollar Index
// dollar index from: https://fred.stlouisfed.org/series/DTWEXAFEGS
pub async fn monitoring_dollar_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let table_name = String::from("dollar_index");
    delete_all_rows(&table_name)
        .await
        .expect("Failed to delete rows!");
    let association_update_table_name = String::from("signal_association_opinion");
    let mut avg = 0.0;
    let mut cnt = 0;
    let mut opinion = String::new();

    loop {
        initialize_record().await;
        let mut dollar_index_vec = select_dollar_index().await;

        for element in &dollar_index_vec {
            if element.value.as_str() != "." {
                avg += element.value.parse::<f64>().unwrap();
                cnt += 1;
            }
        }
        if cnt != 0 {
            avg /= cnt as f64;

            for element in dollar_index_vec {
                if element.value.as_str() != "." {
                    if avg * 1.005 <= element.value.parse::<f64>().unwrap() {
                        opinion = String::from("UP");
                    } else if avg * 1.005 > element.value.parse::<f64>().unwrap()
                        && avg * 0.997 < element.value.parse::<f64>().unwrap()
                    {
                        opinion = String::from("KEEP");
                    } else {
                        opinion = String::from("DOWN");
                    }
                    break;
                }
            }
            break;
        } else {
            println!("cnt is 0!");
            let association_update_condition =
                vec![(String::from("name"), String::from("dollar_index"))];
            let association_update_values: Vec<(String, String)> = vec![
                (String::from("is_working"), 0.to_string()),
                (String::from("opinion"), String::from("-")),
            ];

            update_record2(
                &association_update_table_name,
                &association_update_values,
                &association_update_condition,
            )
            .await;
            sleep(Duration::from_secs(5)).await;
        }
    }

    // update signal_association_opinion
    let association_update_condition = vec![(String::from("name"), String::from("dollar_index"))];
    let association_update_values: Vec<(String, String)> = vec![
        (String::from("is_working"), 1.to_string()),
        (String::from("opinion"), opinion),
    ];

    update_record2(
        &association_update_table_name,
        &association_update_values,
        &association_update_condition,
    )
    .await;

    Ok(())
}

async fn select_dollar_index() -> Vec<DollarIndex> {
    let select_table_name = String::from("dollar_index");
    let select_columns = String::from("*");
    let select_condition = None;
    let select_struct = DollarIndex {
        date: chrono::naive::MAX_DATE,
        value: String::new(),
    };

    let dollar_index_vec = select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &select_struct,
    )
    .await
    .unwrap();

    dollar_index_vec
}
