use super::{hmac_signature, REAL, SIMUL, TEST, RUNNING_MODE, FUTURES_URL, FUTURES_URL_TEST, API_KEY, API_KEY_TESTNET, FuturesExchangeInfo, PositionCoinList, FuturesTradeFee};
use std::collections::HashMap;
use rust_decimal::{Decimal, RoundingStrategy};
use crate::database_control::*;
use crate::decimal_funcs::*;
use rust_decimal_macros::dec;
use rust_decimal::prelude::ToPrimitive;
use serde_json::Value;
use reqwest::{Client, ClientBuilder};

pub async fn get_tradefee_balance(future_trade_fee: &mut FuturesTradeFee, client: &Client) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(FUTURES_URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else {
            url.push_str(FUTURES_URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/fapi/v2/account?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);
    
    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

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

    // deserialize JSON
    let v = serde_json::from_str::<Value>(body.as_str());
    match v {
        Ok(T) => {
            if let (Some(fee_tier), Some(available_balance)) = (T.get("feeTier"), T.get("availableBalance")) {
                let update_table_name = String::from("future_available_balance");
                let update_values = vec![
                    (String::from("available_usdt"), available_balance.as_str().unwrap().to_string()),
                ];
                let update_condition = vec![(String::from("id"), String::from("1"))];
                update_record2(&update_table_name, &update_values, &update_condition)
                    .await
                    .unwrap();

                match fee_tier.as_number().unwrap().as_i64() {
                    Some(0) => {    // Regular User
                        future_trade_fee.user_level = Some(0);
                        future_trade_fee.maker_fee_percent = dec!(0.0200);
                        future_trade_fee.taker_fee_percent = dec!(0.0500);
                    },
                    Some(1) => { // VIP1
                        future_trade_fee.user_level = Some(1);
                        future_trade_fee.maker_fee_percent = dec!(0.0160);
                        future_trade_fee.taker_fee_percent = dec!(0.0400);
                    },
                    Some(2) => {// VIP 2
                        future_trade_fee.user_level = Some(2);
                        future_trade_fee.maker_fee_percent = dec!(0.0140);
                        future_trade_fee.taker_fee_percent = dec!(0.0350);
                    },
                    Some(3) => {// VIP 3
                        future_trade_fee.user_level = Some(3);
                        future_trade_fee.maker_fee_percent = dec!(0.0120);
                        future_trade_fee.taker_fee_percent = dec!(0.0320);
                    },
                    Some(4) => {// VIP 4
                        future_trade_fee.user_level = Some(4);
                        future_trade_fee.maker_fee_percent = dec!(0.0100);
                        future_trade_fee.taker_fee_percent = dec!(0.0300);
                    },
                    Some(5) => {// VIP 5
                        future_trade_fee.user_level = Some(5);
                        future_trade_fee.maker_fee_percent = dec!(0.0080);
                        future_trade_fee.taker_fee_percent = dec!(0.0270);
                    },
                    Some(6) => {// VIP 6
                        future_trade_fee.user_level = Some(6);
                        future_trade_fee.maker_fee_percent = dec!(0.0060);
                        future_trade_fee.taker_fee_percent = dec!(0.0250);
                    },
                    Some(7) => {// VIP 7
                        future_trade_fee.user_level = Some(7);
                        future_trade_fee.maker_fee_percent = dec!(0.0040);
                        future_trade_fee.taker_fee_percent = dec!(0.0220);
                    },
                    Some(8) => {// VIP 8
                        future_trade_fee.user_level = Some(8);
                        future_trade_fee.maker_fee_percent = dec!(0.0020);
                        future_trade_fee.taker_fee_percent = dec!(0.0200);
                    },
                    Some(9) => {// VIP 9
                        future_trade_fee.user_level = Some(9);
                        future_trade_fee.maker_fee_percent = dec!(0.0000);
                        future_trade_fee.taker_fee_percent = dec!(0.0170);
                    },
                    Some(_) => {},
                    None => {}
                }
                log::info!("fee_tier: {:?}. available_balance: {:?}", fee_tier, available_balance);
            } else {
                log::error!("Endpoint(/fapi/v1/leverage?) output changed!");
                return Err("Err")?;
            }
        }
        Err(e) => {
            log::warn!("account information failed!: {}, {}", body, e);
        }
    }

    Ok(())
}

pub async fn update_price_of_filled_positions(
    coin_price_map: &HashMap<String, f64>,
    exchange_info_map: &HashMap<String, FuturesExchangeInfo>,
    futures_trade_fee: &FuturesTradeFee,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let filled_buy_orders = select_filled_positions().await?;

    if !filled_buy_orders.is_empty() {
        // 심볼들을 5개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다.
        // update real-time current price to each record through chunks
        let chunks: std::slice::Chunks<'_, PositionCoinList> = filled_buy_orders.chunks(5);
        let mut task_vec = Vec::new();

        for chunk in chunks {
            let chunk_vec = chunk.to_vec();
            let coin_price_vec_c = coin_price_map.clone();
            let exchange_info_vec_c = exchange_info_map.clone();
            let futures_trade_fee_map_c = futures_trade_fee.clone();
            task_vec.push(tokio::spawn(async move {
                update_repeat_task(
                    chunk_vec,
                    &coin_price_vec_c,
                    &exchange_info_vec_c,
                    &futures_trade_fee_map_c,
                )
                .await;
            }));
        }
    }
    Ok(())
}

async fn update_repeat_task(
    buy_ordered_coin_vec: Vec<PositionCoinList>,
    coin_price_map: &HashMap<String, f64>,
    exchange_info_map: &HashMap<String, FuturesExchangeInfo>,
    futures_trade_fee: &FuturesTradeFee,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let update_table_name = String::from("future_ordered_coin_list");
    let mut price = Decimal::new(0, 8);
    let mut profit_percent = 0.0;   
    
    // update current_price, expected__get_usdt, pnl, pure_profit_percent, minimum_profit_percent, maximum_profit_percent
    for element in buy_ordered_coin_vec {
        // build update values
        if coin_price_map.contains_key(&element.symbol)
            && exchange_info_map.contains_key(&element.symbol)
            && futures_trade_fee.user_level.is_some()
        {
            price = rust_decimal::prelude::FromPrimitive::from_f64(
                *coin_price_map.get(&element.symbol).unwrap(),
            )
            .unwrap();
            if !price.is_zero() {
                let mut update_values: Vec<(String, String)> = Vec::new();
                // to get quote_commission_precision
                let entry_trade_fee_ratio = decimal_div(futures_trade_fee.maker_fee_percent, dec!(100));
                let exit_trade_fee_ratio = decimal_div(futures_trade_fee.taker_fee_percent, dec!(100));
                let trade_fee = decimal_div(futures_trade_fee.taker_fee_percent, dec!(100));
                let lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize;
                let quote_precision = exchange_info_map
                    .get(&element.symbol)
                    .unwrap()
                    .quote_precision;
                let position_size = element.base_qty_ordered.round_dp_with_strategy(
                    lot_step_size.normalize().scale(),
                    RoundingStrategy::ToZero,
                );
                
                let entry_trade_fee = decimal_mul(decimal_mul(position_size, element.entry_price), entry_trade_fee_ratio);
                let exit_trade_fee = decimal_mul(decimal_mul(position_size, price), exit_trade_fee_ratio);
                let fee_total = decimal_add(entry_trade_fee, exit_trade_fee);
                let initial_margin = decimal_add(decimal_mul(position_size, element.entry_price), entry_trade_fee);
                let mut unrealized_pnl = Decimal::new(0, 8);
                
                if element.position.contains("Long") {
                    unrealized_pnl = decimal_sub(decimal_mul(decimal_sub(price, element.entry_price), position_size), fee_total);
                } else {
                    unrealized_pnl = decimal_sub(decimal_mul(decimal_sub(element.entry_price, price), position_size), fee_total);
                }
                
                let mut pure_profit_percent = (unrealized_pnl.to_f64().unwrap()
                    / decimal_add(initial_margin, unrealized_pnl).to_f64().unwrap())
                    * 100.0;
                
                pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding
                update_values.push((String::from("current_price"), price.to_string()));
                update_values.push((String::from("expected_get_usdt"), decimal_add(unrealized_pnl, element.used_usdt).to_string()));
                update_values.push((String::from("pnl"), unrealized_pnl.to_string()));
                update_values.push((String::from("pure_profit_percent"), pure_profit_percent.to_string()));

                if element.minimum_profit_percent > pure_profit_percent {
                    update_values.push((String::from("minimum_profit_percent"), pure_profit_percent.to_string()));
                // minimum_profit_percent
                } else if pure_profit_percent >= 0.0 {
                    update_values.push((String::from("minimum_profit_percent"), 0.0.to_string()));
                } else {
                    update_values.push((String::from("minimum_profit_percent"), element.minimum_profit_percent.to_string()));
                    // minimum_profit_percent
                }

                if element.maximum_profit_percent < pure_profit_percent {
                    update_values.push((String::from("maximum_profit_percent"), pure_profit_percent.to_string()));
                // maximum_profit_percent
                } else if pure_profit_percent <= 0.0 {
                    update_values.push((String::from("maximum_profit_percent"), 0.0.to_string()));
                } else {
                    update_values.push((String::from("maximum_profit_percent"), element.maximum_profit_percent.to_string()));
                    // maximum_profit_percent
                }
                let update_condition = vec![(String::from("id"), element.id.to_string())];
                update_record2(&update_table_name, &update_values, &update_condition)
                                .await
                                .unwrap();
            }
        }
    }

    Ok(())
}

pub async fn select_filled_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
    let select_table_name = String::from("future_ordered_coin_list");
    let select_columns = String::from("*");
    let mut select_condition_build = String::from("WHERE order_type = 'POSITIONING' and (status = 'FILLED' or status = 'SIMUL')");

    let select_condition = Some(select_condition_build);

    let data_struct = PositionCoinList::new();

    let select_result = try_select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &data_struct,
    )
    .await;

    if select_result.is_ok() {
        Ok(select_result.unwrap())
    } else {
        eprint!("select_filled_positions() error!");
        Err("error")?
    }
}

pub async fn select_listuped_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
    let select_table_name = String::from("future_ordered_coin_list");
    let select_columns = String::from("*");
    let mut select_condition_build = String::from("WHERE status = 'LISTUP'");

    let select_condition = Some(select_condition_build);

    let data_struct = PositionCoinList::new();

    let select_result = try_select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &data_struct,
    )
    .await;

    if select_result.is_ok() {
        Ok(select_result.unwrap())
    } else {
        eprint!("select_listup_positions() error!");
        Err("error")?
    }
}
