use crate::coex::exchange_team::*;
use crate::coex::order_team::*;
use crate::coin_health_check_team::request_others::CoinPriceData;
use csv::{DeserializeRecordsIter, StringRecord};
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;
use serde::Deserialize;

use super::strategy_test;
use super::{
    exists_record, insert_one_record, try_join_all, try_select_record, AllData, ExchangeInfo,
    FilteredData, FromRow, RealtimePriceData, TradeFee,
};
use crate::signal_association::signal_decision::*;
use tokio::time::{sleep, Duration, Instant};

#[derive(Debug, FromRow)]
struct ServerEpoch {
    server_epoch: u64,
}

#[derive(Debug, FromRow)]
struct Record {
    symbol: String,
    suggested_price: Decimal,
    close_time: u64,
    registered_server_epoch: u64,
    registerer: u16,
    is_long: u8,
}

pub async fn execute_list_up_for_buy(
    all_data: &AllData,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let mut task_vec = Vec::new();
    // let all_data_c3 = all_data.clone();
    let all_data_c4 = all_data.clone();
    let all_data_c5 = all_data.clone();
    let all_data_c6 = all_data.clone();
    
    // strategist_001(all_data).await?;
    // strategist_002(all_data).await?;
    // task_vec.push(tokio::spawn(async move {
    //     crate::strategy_team::strategy_003::list_up_for_buy(all_data_c3).await;
    // }));
    task_vec.push(tokio::spawn(async move {
        crate::strategy_team::strategy_004::list_up_for_buy(all_data_c4).await;
    }));
    task_vec.push(tokio::spawn(async move {
        crate::strategy_team::strategy_005::list_up_for_buy(all_data_c5).await;
    }));
    task_vec.push(tokio::spawn(async move {
        crate::strategy_team::strategy_006::list_up_for_buy(all_data_c6).await;
    }));
    // let all_data_ct = all_data.clone();
    // task_vec.push(tokio::spawn(async move {
    //     crate::strategy_team::strategy_test::strategist_test(all_data_ct).await;
    // }));

    try_join_all(task_vec).await?;
    Ok(())
}

pub async fn execute_list_up_for_sell(
    all_data: &AllData,
    exchange_info_vec: &Vec<ExchangeInfo>,
    trade_fee_vec: &Vec<TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    crate::strategy_team::strategy_004::list_up_for_sell(
        all_data,
        exchange_info_vec,
        trade_fee_vec,
    )
    .await?;

    crate::strategy_team::strategy_005::list_up_for_sell(
        all_data,
        exchange_info_vec,
        trade_fee_vec,
    )
    .await?;

    crate::strategy_team::strategy_006::list_up_for_sell(
        all_data,
        exchange_info_vec,
        trade_fee_vec,
    )
    .await?;

    Ok(())
}

// useful functions for strategists
pub async fn get_current_price(
    symbol: &String,
    rt_price_vec: &Vec<(String, Vec<RealtimePriceData>)>,
) -> Option<f64> {
    let index_result = rt_price_vec.iter().position(|x| *x.0 == *symbol);
    match index_result {
        Some(T) => {
            if rt_price_vec[T].1.last().is_some() {
                Some(rt_price_vec[T].1.last().unwrap().close_price)
            } else {
                None
            }
        }
        None => None,
    }
}

pub async fn insert_pre_suggested_coins(
    registerer: i32,
    is_long: bool,
    filtered_coins: &Vec<FilteredData>,
    alldata: &AllData,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // Check the existance of record that is registered by this strategist
    let mut dest_table_name = String::from("suggested_coin_list");
    let mut dest2_table_name = String::from("buy_ordered_coin_list");
    let mut dest3_table_name = String::from("pre_suggested_coin_list");
    let mut insert_table_name = String::from("pre_suggested_coin_list");
    let mut exists_condition_build = String::from("registerer=");
    exists_condition_build.push_str(registerer.to_string().as_str());
    let mut exists_condition = Some(exists_condition_build);
    let exists_result1 = exists_record(&dest_table_name, &exists_condition).await;
    let exists_result2 = exists_record(&dest2_table_name, &exists_condition).await;
    let exists_result3 = exists_record(&dest3_table_name, &exists_condition).await;
    let insert_columns = vec![
        "symbol",
        "close_time",
        "suggested_price",
        "current_price",
        "stoploss",
        "target_price",
        "registered_server_epoch",
        "profit_percent",
        "minimum_profit_percent",
        "maximum_profit_percent",
        "registerer",
        "is_long",
    ];

    if exists_result1 == true && exists_result2 == true && exists_result3 == true {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        #[derive(FromRow)]
        struct SelectData2 {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let mut select_columns2 = String::from("symbol, close_time");
        let select_data_structure2 = SelectData2 {
            symbol: String::new(),
            close_time: 0,
        };
        let suggested_coin_list = try_select_record(
            &dest_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;
        let ordered_coin_list = try_select_record(
            &dest2_table_name,
            &select_columns2,
            &select_condition,
            &select_data_structure2,
        )
        .await?;
        let pre_suggested_coin_list = try_select_record(
            &dest3_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;

        // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            for list_element in &ordered_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            for list_element in &pre_suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else if exists_result1 == true && exists_result2 == true && exists_result3 == false {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        #[derive(FromRow)]
        struct SelectData2 {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let mut select_columns2 = String::from("symbol, close_time");
        let select_data_structure2 = SelectData2 {
            symbol: String::new(),
            close_time: 0,
        };
        let suggested_coin_list = try_select_record(
            &dest_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;
        let ordered_coin_list = try_select_record(
            &dest2_table_name,
            &select_columns2,
            &select_condition,
            &select_data_structure2,
        )
        .await?;

        // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            for list_element in &ordered_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else if exists_result1 == true && exists_result2 == false && exists_result3 == true {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let suggested_coin_list = try_select_record(
            &dest_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;
        let pre_suggested_coin_list = try_select_record(
            &dest3_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;

        // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            for list_element in &pre_suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else if exists_result1 == true && exists_result2 == false && exists_result3 == false {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let suggested_coin_list = try_select_record(
            &dest_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;

        // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else if exists_result1 == false && exists_result2 == true && exists_result3 == true {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        #[derive(FromRow)]
        struct SelectData2 {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let mut select_columns2 = String::from("symbol, close_time");
        let select_data_structure2 = SelectData2 {
            symbol: String::new(),
            close_time: 0,
        };
        let ordered_coin_list = try_select_record(
            &dest2_table_name,
            &select_columns2,
            &select_condition,
            &select_data_structure2,
        )
        .await?;
        let pre_suggested_coin_list = try_select_record(
            &dest3_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;

        // insert record without duplicate(registerer, close_time, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &ordered_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            for list_element in &pre_suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else if exists_result1 == false && exists_result2 == true && exists_result3 == false {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        #[derive(FromRow)]
        struct SelectData2 {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let mut select_columns2 = String::from("symbol, close_time");
        let select_data_structure2 = SelectData2 {
            symbol: String::new(),
            close_time: 0,
        };
        let ordered_coin_list = try_select_record(
            &dest2_table_name,
            &select_columns2,
            &select_condition,
            &select_data_structure2,
        )
        .await?;

        // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &ordered_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else if exists_result1 == false && exists_result2 == false && exists_result3 == true {
        // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list]
        #[derive(FromRow)]
        struct SelectData {
            symbol: String,
            close_time: i64,
        }
        let mut select_columns = String::from("symbol, close_time");
        let mut condition_build = String::from("WHERE registerer=");
        condition_build.push_str(registerer.to_string().as_str());
        let select_condition = Some(condition_build);
        let select_data_structure = SelectData {
            symbol: String::new(),
            close_time: 0,
        };
        let pre_suggested_coin_list = try_select_record(
            &dest3_table_name,
            &select_columns,
            &select_condition,
            &select_data_structure,
        )
        .await?;

        // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
        for filtered_coin in filtered_coins {
            let mut is_dupe = false; // initialize

            for list_element in &pre_suggested_coin_list {
                if (filtered_coin.symbol == list_element.symbol)
                    && (filtered_coin.closetime == list_element.close_time)
                {
                    is_dupe = true;
                    break;
                }
            }

            if is_dupe == false {
                let mut insert_values = vec![
                    filtered_coin.symbol.clone(),            // symbol
                    filtered_coin.closetime.to_string(),     // close_time
                    filtered_coin.current_price.to_string(), // suggested_price
                    filtered_coin.current_price.to_string(), // current_price
                    filtered_coin.stoploss.to_string(),      // stoploss
                    filtered_coin.target_price.to_string(),  // target_price
                    server_epoch().await.to_string(),        // registered_server_epoch
                    0.0.to_string(),                         // profit_percent
                    0.0.to_string(),                         // minimum_profit_percent
                    0.0.to_string(),                         // maximum_profit_percent
                    registerer.to_string(),                  // registerer
                ];

                if is_long == true {
                    insert_values.push(1.to_string()); // is_long
                } else {
                    insert_values.push(0.to_string()); // is_long
                }

                insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
            }
        }
    } else {
        for filtered_coin in filtered_coins {
            let mut insert_values = vec![
                filtered_coin.symbol.clone(),            // symbol
                filtered_coin.closetime.to_string(),     // close_time
                filtered_coin.current_price.to_string(), // suggested_price
                filtered_coin.current_price.to_string(), // current_price
                filtered_coin.stoploss.to_string(),      // stoploss
                filtered_coin.target_price.to_string(),  // target_price
                server_epoch().await.to_string(),        // registered_server_epoch
                0.0.to_string(),                         // profit_percent
                0.0.to_string(),                         // minimum_profit_percent
                0.0.to_string(),                         // maximum_profit_percent
                registerer.to_string(),                  // registerer
            ];

            if is_long == true {
                insert_values.push(1.to_string()); // is_long
            } else {
                insert_values.push(0.to_string()); // is_long
            }

            insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
        }
    }

    Ok(())
}
