pub mod order;
pub mod table_mgmt;

use hmac_sha256::HMAC;
use crate::RunningMode::*;
use crate::{API_KEY, API_KEY_TESTNET, RUNNING_MODE, SECRET_KEY, SECRET_KEY_TESTNET, FUTURES_URL, FUTURES_URL_TEST};
use crate::strategy_team::{AllData, TimeData};
use crate::database_control::*;
use rust_decimal::Decimal;
use sqlx::FromRow;
use hex::ToHex;
use crate::future::table_mgmt::select_listuped_positions;

#[derive(Debug, PartialEq, Clone, sqlx::Type)]
pub enum Position {
    Long,
    Short
}

#[derive(Debug, PartialEq, Clone, sqlx::Type)]
pub enum Status {
    #[sqlx(rename = "listup")]
    Listup,
    #[sqlx(rename = "filled")]
    Filled,
    #[sqlx(rename = "partially_filled")]
    PartiallyFilled
}

impl std::fmt::Display for Position {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub struct EntryCoinInfo {
    pub symbol: String,
    pub close_time: i64,
    pub registered_server_epoch: i64,
}

impl EntryCoinInfo {
    fn new() -> EntryCoinInfo {
        let a = EntryCoinInfo {
            symbol: String::new(),
            close_time: 0,
            registered_server_epoch: 0
        };
        a
    }
}

#[derive(Debug, Clone)]
pub enum ContractType {
    Perpetual,      // this is the target
    CurrentMonth,
    NextMonth,
    CurrentQuarter,
    NextQuarter,
    PerpetualDelivering,
}

#[derive(Debug, Clone)]
pub enum ContractStatus {
    PendingTrading, // this is the target
    Trading,
    PreDelivering,
    Delivering,
    Delivered,
    PreSettle,
    Settling,
    Close,
}

#[derive(Debug, FromRow, Clone)]
pub struct FuturesExchangeInfo {
    pub stepsize: Decimal,
    pub ticksize: Decimal,
    pub contract_type: ContractType,
    pub contract_status: ContractStatus,
    pub base_asset_precision: u32,
    pub quote_precision: u32,
    pub notional: Decimal
}

impl FuturesExchangeInfo {
    fn new() -> FuturesExchangeInfo {
        let a = FuturesExchangeInfo {
            stepsize: Decimal::new(0, 8),
            ticksize: Decimal::new(0, 8),
            contract_type: ContractType::Perpetual,
            contract_status: ContractStatus::Trading,
            base_asset_precision: 0,
            quote_precision: 0,
            notional: Decimal::new(0, 8),
        };
        a
    }
}

#[derive(Clone, Debug)]
pub struct FuturesTradeFee {
    user_level: Option<i32>,
    taker_fee_percent: Decimal,
    maker_fee_percent: Decimal,
}

impl FuturesTradeFee {
    pub fn new() -> FuturesTradeFee {
        let a = FuturesTradeFee {
            user_level: None,
            taker_fee_percent: Decimal::new(0, 8),
            maker_fee_percent: Decimal::new(0, 8),
        };
        a
    }
}
#[derive(Debug, FromRow, Clone)]
pub struct PositionCoinList {
    pub id: u64,
    pub order_type: String,
    pub status: String,
    pub symbol: String,
    pub order_id: u64,
    pub position: String,
    pub registered_server_epoch: i64,
    pub transact_time: i64,
    pub close_time: i64,
    pub used_usdt: Decimal,
    pub expected_get_usdt: f64,
    pub pnl: f64,
    pub entry_price: Decimal,
    pub current_price: Decimal,
    pub base_qty_ordered: Decimal,
    pub pure_profit_percent: f64,
    pub minimum_profit_percent: f64,
    pub maximum_profit_percent: f64,
}

impl PositionCoinList {
    fn new() -> PositionCoinList {
        let a = PositionCoinList{
            id: 0,
            order_type: String::new(),
            status: String::new(),
            symbol: String::new(),
            order_id: 0,
            position: String::new(),
            registered_server_epoch: 0,
            transact_time: 0,
            close_time: 0,
            used_usdt: Decimal::new(0, 8),
            expected_get_usdt: 0.0,
            pnl: 0.0,
            entry_price: Decimal::new(0, 8),
            current_price: Decimal::new(0, 8),
            base_qty_ordered: Decimal::new(0, 8),
            pure_profit_percent: 0.0,
            minimum_profit_percent: 0.0,
            maximum_profit_percent: 0.0,
        };
        a
    }
}

async fn hmac_signature(query: &mut String) {
    // fetch time information from [time] table
    let table_name = String::from("time");
    let columns = String::from("*");
    let condition = None;
    let mut time_info = TimeData {
        server_epoch: 0,
        local_epoch: 0,
        epoch_difference: 0,
        server_ymdhs: String::new(),
        local_ymdhs: String::new(),
        last_server_epoch: 0,
        last_server_ymdhs: String::new(),
    };

    let select_result = select_record(&table_name, &columns, &condition, &time_info)
        .await
        .unwrap();
    let difference_epoch = select_result.first().unwrap().epoch_difference;
    let server_epoch = select_result.first().unwrap().server_epoch;
    
    // build query message
    // local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤
    // 이를 timestamp로 사용한다.
    let mut timestamp;
    if difference_epoch >= 0 {
        timestamp = (server_epoch as u128 + difference_epoch as u128).to_string();
    } else if difference_epoch < 0 {
        timestamp = (server_epoch as u128 + (difference_epoch * -1) as u128).to_string();
    } else {
        timestamp = server_epoch.to_string();
    }
    let recv_window_size = "30000".to_string(); // default: 5,000ms, Max: 60,000ms

    let mut query_build = String::from("&timestamp=");
    query_build.push_str(&timestamp);
    query_build.push_str("&recvWindow=");
    query_build.push_str(&recv_window_size);

    let mut secret_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            secret_key.push_str(SECRET_KEY_TESTNET);
        } else {
            secret_key.push_str(SECRET_KEY);
        }
    }

    query.push_str(&query_build);
    let signature = HMAC::mac(&query.as_bytes(), secret_key.as_bytes());

    query.push_str("&signature=");
    query.push_str(signature.encode_hex::<String>().as_str());
}
