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

use crate::database_control::*;
use crate::time_checking_team::UserTime;
use log;
use rand::*;
use reqwest::{Client, ClientBuilder, Response};
use tokio::join;
use tokio::time::*;

#[derive(Debug)]
pub struct ServerHealth {
    pub server_on: bool, // the total status of server. Its value is decided by AND operation of ping_on and wallet_system_on
    // through is_server_on method.
    pub ping_on: bool,          // ping test to server.
    pub wallet_system_on: bool, // check wallet system of server.
    pub waiting_maximum: u32, // maximum time for timer to wait until server works on again. the value will be null when the server works on.
}
impl ServerHealth {
    pub fn new() -> ServerHealth {
        // make an instance and initialize fields
        ServerHealth {
            server_on: false,
            ping_on: false,
            wallet_system_on: false,
            waiting_maximum: 0,
        }
    }
}

// to check the current server health and store data into database.
pub async fn execute_server_health_check(
    usertime: &mut UserTime,
    serverhealth: &mut ServerHealth,
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let mut rng = rand::rngs::OsRng;
    let mut waiting_time: u32;
    loop {
        update(serverhealth, usertime, &client).await?;

        if serverhealth.server_on == true {
            serverhealth.waiting_maximum = 0;
            // update_database(&serverhealth).await;
            break;
        } else {
            usertime.update_last_server().await;
            // whenever server is off, waiting_maximum increases a second
            // and thread sleeps for uniform distribution random number between 1sec and waiting_maximum.
            serverhealth.waiting_maximum += 1;
            waiting_time = rng.gen_range(1..=serverhealth.waiting_maximum);

            // update_database(&serverhealth).await;
            if waiting_time > 2 {
                log::warn!(">>> retry connection after {} second(s).", waiting_time);
            }

            sleep(Duration::from_secs(waiting_time as u64)).await;
        }
    }
    // println!("server health 완료");
    Ok(())
}

// check the server on or off
async fn update(
    serverHealth: &mut ServerHealth,
    usertime: &mut UserTime,
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let (r1, r2) = join!(check_ping(usertime, &client), check_wallet_system(&client));
    serverHealth.ping_on = r1?;
    serverHealth.wallet_system_on = r2?;
    serverHealth.server_on = serverHealth.ping_on && serverHealth.wallet_system_on;

    Ok(())
}

// test ping to server and record the last running time of server (/api, Weight 1)
async fn check_ping(
    usertime: &mut UserTime,
    client: &Client,
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
    let response = client
        .get("https://api.binance.com/api/v3/ping")
        .send()
        .await;

    match response {
        Ok(T) => {
            let body = T.text_with_charset("utf-8").await?;
            if body.contains("{}") {
                Ok(true)
            } else {
                Ok(false)
            }
        }
        Err(e) => Ok(false),
    }
}

// check wallet system status (/sapi, Weight 1)
async fn check_wallet_system(
    client: &Client,
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
    let response = client
        .get("https://api.binance.com/sapi/v1/system/status")
        .send()
        .await;

    match response {
        Ok(T) => {
            let body = T.text_with_charset("utf-8").await?;
            if body.contains("0") {
                Ok(true)
            } else {
                Ok(false)
            }
        }
        Err(E) => Ok(false),
    }
}

async fn update_database(serverhealth: &ServerHealth) {
    let table_name = String::from("serverhealth");
    let mut record: Vec<(&str, &str)> = Vec::new();
    let condition = vec![("id", "1")];

    let mut server_on = serverhealth.server_on.to_string();
    let mut ping_on = serverhealth.ping_on.to_string();
    let mut wallet_system_on = serverhealth.wallet_system_on.to_string();
    let mut waiting_maximum = serverhealth.waiting_maximum.to_string();

    record.push(("server_on", server_on.as_str()));
    record.push(("ping_on", ping_on.as_str()));
    record.push(("wallet_system_on", wallet_system_on.as_str()));
    record.push(("waiting_maximum", waiting_maximum.as_str()));

    let mut delete_query_result = update_record(&table_name, &record, &condition).await;
    if delete_query_result.is_err() {
        loop {
            delete_query_result = update_record(&table_name, &record, &condition).await;
            if delete_query_result.is_ok() {
                break;
            }
            sleep(Duration::from_millis(10)).await;
        }
    }
}
