use crate::{
    args,
    proof,
    reqres,
    utils,
};
use clap::Parser;
use diqwest::WithDigestAuth;
use log::{
    debug,
    error,
    info,
};
use std::process::Command;

/// Current xmr ring size updated here.
const RING_SIZE: u32 = 0x10;

struct RpcLogin {
    username: String,
    credential: String,
}

enum RpcFields {
    Address,
    Balance,
    CheckTxProof,
    Close,
    CreateAddress,
    CreateWallet,
    ExchangeMultisigKeys,
    Export,
    GetTxProof,
    GetTxById,
    GetVersion,
    Id,
    Import,
    JsonRpcVersion,
    Make,
    Open,
    Prepare,
    SignMultisig,
    SweepAll,
    Transfer,
    ValidateAddress,
    Verify,
}

impl RpcFields {
    pub fn value(&self) -> String {
        match *self {
            RpcFields::Address => String::from("get_address"),
            RpcFields::Balance => String::from("get_balance"),
            RpcFields::CheckTxProof => String::from("check_tx_proof"),
            RpcFields::Close => String::from("close_wallet"),
            RpcFields::CreateAddress => String::from("create_address"),
            RpcFields::CreateWallet => String::from("create_wallet"),
            RpcFields::ExchangeMultisigKeys => String::from("exchange_multisig_keys"),
            RpcFields::Export => String::from("export_multisig_info"),
            RpcFields::GetTxProof => String::from("get_tx_proof"),
            RpcFields::GetTxById => String::from("get_transfer_by_txid"),
            RpcFields::GetVersion => String::from("get_version"),
            RpcFields::Id => String::from("0"),
            RpcFields::Import => String::from("import_multisig_info"),
            RpcFields::JsonRpcVersion => String::from("2.0"),
            RpcFields::Make => String::from("make_multisig"),
            RpcFields::Open => String::from("open_wallet"),
            RpcFields::Prepare => String::from("prepare_multisig"),
            RpcFields::SignMultisig => String::from("sign_multisig"),
            RpcFields::SweepAll => String::from("sweep_all"),
            RpcFields::Transfer => String::from("transfer"),
            RpcFields::ValidateAddress => String::from("validate_address"),
            RpcFields::Verify => String::from("verify"),
        }
    }
}

enum DaemonFields {
    GetBlock,
    GetHeight,
    GetInfo,
    GetTransactions,
    Id,
    Version,
}

impl DaemonFields {
    pub fn value(&self) -> String {
        match *self {
            DaemonFields::GetBlock => String::from("get_block"),
            DaemonFields::GetHeight => String::from("get_height"),
            DaemonFields::GetInfo => String::from("get_info"),
            DaemonFields::GetTransactions => String::from("get_transactions"),
            DaemonFields::Id => String::from("0"),
            DaemonFields::Version => String::from("2.0"),
        }
    }
}

pub enum LockTimeLimit {
    Blocks,
}

impl LockTimeLimit {
    pub fn value(&self) -> u64 {
        match *self {
            LockTimeLimit::Blocks => 20,
        }
    }
}

/// Start monerod from the -`-monero-location` flag
///
/// default: /home/$USER/monero-xxx-xxx
pub fn start_daemon() {
    info!("starting monerod");
    let blockchain_dir = get_blockchain_dir();
    let bin_dir = get_monero_location();
    let release_env = utils::get_release_env();
    if release_env == utils::ReleaseEnvironment::Development {
        let args = ["--data-dir", &blockchain_dir, "--stagenet", "--detach"];
        let output = Command::new(format!("{}/monerod", bin_dir))
            .args(args)
            .spawn()
            .expect("monerod failed to start");
        debug!("{:?}", output.stdout);
    } else {
        let args = ["--data-dir", &blockchain_dir, "--detach"];
        let output = Command::new(format!("{}/monerod", bin_dir))
            .args(args)
            .spawn()
            .expect("monerod failed to start");
        debug!("{:?}", output.stdout);
    }
}

/// Start monero-wallet-rpc
pub fn start_rpc() {
    info!("starting monero-wallet-rpc");
    let bin_dir = get_monero_location();
    let port = get_rpc_port();
    let login = get_rpc_creds();
    let daemon_address = get_rpc_daemon();
    let rpc_login = format!("{}:{}", &login.username, &login.credential);
    let mut wallet_dir = format!(
        "/home/{}/.neveko/stagenet/wallet/",
        std::env::var("USER").unwrap_or(String::from("user")),
    );
    let release_env = utils::get_release_env();
    if release_env == utils::ReleaseEnvironment::Development {
        let args = [
            "--rpc-bind-port",
            &port,
            "--wallet-dir",
            &wallet_dir,
            "--rpc-login",
            &rpc_login,
            "--daemon-address",
            &daemon_address,
            "--stagenet",
        ];
        let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir))
            .args(args)
            .spawn()
            .expect("monero-wallet-rpc failed to start");
        debug!("{:?}", output.stdout);
    } else {
        wallet_dir = format!(
            "/home/{}/.neveko/wallet/",
            std::env::var("USER").unwrap_or(String::from("user")),
        );
        let args = [
            "--rpc-bind-port",
            &port,
            "--wallet-dir",
            &wallet_dir,
            "--rpc-login",
            &rpc_login,
            "--daemon-address",
            &daemon_address,
        ];
        let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir))
            .args(args)
            .spawn()
            .expect("monero-wallet-rpc failed to start");
        debug!("{:?}", output.stdout);
    }
}

fn get_rpc_port() -> String {
    let args = args::Args::parse();
    let rpc = String::from(args.monero_rpc_host);
    let values = rpc.split(":");
    let mut v: Vec<String> = values.map(|s| String::from(s)).collect();
    let port = v.remove(2);
    debug!("monero-wallet-rpc port: {}", port);
    port
}

/// Get monero rpc host from command line argument
fn get_blockchain_dir() -> String {
    let args = args::Args::parse();
    String::from(args.monero_blockchain_dir)
}

/// Get monero download location
fn get_monero_location() -> String {
    let args = args::Args::parse();
    String::from(args.monero_location)
}

/// Get monero rpc host from the `--monero-rpc-host` cli arg
fn get_rpc_host() -> String {
    let args = args::Args::parse();
    let rpc = String::from(args.monero_rpc_host);
    format!("{}/json_rpc", rpc)
}

/// Get creds from the `--monero-rpc-daemon` cli arg
fn get_rpc_creds() -> RpcLogin {
    let args = args::Args::parse();
    let username = String::from(args.monero_rpc_username);
    let credential = String::from(args.monero_rpc_cred);
    RpcLogin {
        username,
        credential,
    }
}

fn get_rpc_daemon() -> String {
    let args = args::Args::parse();
    let daemon = String::from(args.monero_rpc_daemon);
    format!("{}/json_rpc", daemon)
}

/// Performs rpc 'get_version' method
pub async fn get_version() -> reqres::XmrRpcVersionResponse {
    info!("executing {}", RpcFields::GetVersion.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let req = reqres::XmrRpcRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::GetVersion.value(),
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcVersionResponse>().await;
            debug!("{} response: {:?}", RpcFields::GetVersion.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_e) => Default::default(),
    }
}

/// Helper function for checking xmr rpc online during app startup
pub async fn check_rpc_connection() -> () {
    let res: reqres::XmrRpcVersionResponse = get_version().await;
    if res.result.version == 0 {
        error!("failed to connect to monero-wallet-rpc");
    }
}

/// Performs the xmr rpc 'verify' method
pub async fn verify(address: String, data: String, signature: String) -> String {
    info!("executing {}", RpcFields::Verify.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcVerifyParams {
        address,
        data,
        signature,
    };
    let req = reqres::XmrRpcVerifyRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Verify.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcVerifyResponse>().await;
            debug!("{} response: {:?}", RpcFields::Verify.value(), res);
            match res {
                Ok(res) => {
                    if res.result.good {
                        req.params.address
                    } else {
                        utils::ApplicationErrors::LoginError.value()
                    }
                }
                _ => utils::ApplicationErrors::LoginError.value(),
            }
        }
        Err(_e) => utils::ApplicationErrors::LoginError.value(),
    }
}

/// Performs the xmr rpc 'create_wallet' method
pub async fn create_wallet(filename: &String, password: &String) -> bool {
    info!("executing {}", RpcFields::CreateWallet.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcCreateWalletParams {
        filename: String::from(filename),
        language: String::from("English"),
        password: String::from(password),
    };
    let req = reqres::XmrRpcCreateRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::CreateWallet.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            // The result from wallet creation is empty
            let res = response.text().await;
            debug!("{} response: {:?}", RpcFields::CreateWallet.value(), res);
            match res {
                Ok(r) => {
                    if r.contains("-1") {
                        return false;
                    }
                    true
                }
                _ => false,
            }
        }
        Err(_) => false,
    }
}

/// Performs the xmr rpc 'open_wallet' method
pub async fn open_wallet(filename: &String, password: &String) -> bool {
    info!("executing {}", RpcFields::Open.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcOpenWalletParams {
        filename: String::from(filename),
        password: String::from(password),
    };
    let req = reqres::XmrRpcOpenRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Open.value(),
        params,
    };
    debug!("open request: {:?}", req);
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            // The result from wallet operation is empty
            let res = response.text().await;
            debug!("{} response: {:?}", RpcFields::Open.value(), res);
            match res {
                Ok(r) => {
                    if r.contains("-1") {
                        return false;
                    }
                    return true;
                }
                _ => false,
            }
        }
        Err(_) => false,
    }
}

/// Performs the xmr rpc 'close_wallet' method
pub async fn close_wallet(filename: &String, password: &String) -> bool {
    info!("executing {}", RpcFields::Close.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcOpenWalletParams {
        filename: String::from(filename),
        password: String::from(password),
    };
    let req = reqres::XmrRpcOpenRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Close.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            // The result from wallet operation is empty
            let res = response.text().await;
            debug!("{} response: {:?}", RpcFields::Close.value(), res);
            match res {
                Ok(_) => true,
                _ => false,
            }
        }
        Err(_) => false,
    }
}

/// Performs the xmr rpc 'get_balance' method
pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
    info!("executing {}", RpcFields::Balance.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcBalanceParams = reqres::XmrRpcBalanceParams {
        account_index: 0,
        address_indices: vec![0],
        all_accounts: false,
        strict: false,
    };
    let req = reqres::XmrRpcBalanceRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Balance.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcBalanceResponse>().await;
            debug!("{} response: {:?}", RpcFields::Balance.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'get_address' method
pub async fn get_address() -> reqres::XmrRpcAddressResponse {
    info!("executing {}", RpcFields::Address.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcAddressParams = reqres::XmrRpcAddressParams { account_index: 0 };
    let req = reqres::XmrRpcAddressRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Address.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcAddressResponse>().await;
            debug!("{} response: {:?}", RpcFields::Address.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'get_address' method
pub async fn validate_address(address: &String) -> reqres::XmrRpcValidateAddressResponse {
    info!("executing {}", RpcFields::ValidateAddress.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcValidateAddressParams = reqres::XmrRpcValidateAddressParams {
        address: String::from(address),
        any_net_type: false,
        allow_openalias: true,
    };
    let req = reqres::XmrRpcValidateAddressRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::ValidateAddress.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response
                .json::<reqres::XmrRpcValidateAddressResponse>()
                .await;
            debug!("{} response: {:?}", RpcFields::ValidateAddress.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}
// START Multisig

/// Performs the xmr rpc 'prepare_multisig' method
pub async fn prepare_wallet() -> reqres::XmrRpcPrepareResponse {
    info!("executing {}", RpcFields::Prepare.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let req = reqres::XmrRpcRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Prepare.value(),
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcPrepareResponse>().await;
            debug!("{} response: {:?}", RpcFields::Prepare.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'make_multisig' method
pub async fn make_wallet(info: Vec<String>) -> reqres::XmrRpcMakeResponse {
    info!("executing {}", RpcFields::Make.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcMakeParams {
        multisig_info: info,
        threshold: 2,
    };
    let req = reqres::XmrRpcMakeRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Make.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcMakeResponse>().await;
            debug!("{} response: {:?}", RpcFields::Make.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'export_multisig_info' method
pub async fn export_multisig_info() -> reqres::XmrRpcExportResponse {
    info!("executing {}", RpcFields::Export.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let req = reqres::XmrRpcRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Export.value(),
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcExportResponse>().await;
            debug!("{} response: {:?}", RpcFields::Export.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'import_multisig_info' method
pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResponse {
    info!("executing {}", RpcFields::Import.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcImportParams { info };
    let req = reqres::XmrRpcImportRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Import.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcImportResponse>().await;
            debug!("{} response: {:?}", RpcFields::Import.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'sign_multisig' method
pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigResponse {
    info!("executing {}", RpcFields::SignMultisig.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcSignMultisigParams { tx_data_hex };
    let req = reqres::XmrRpcSignMultisigRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::SignMultisig.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcSignMultisigResponse>().await;
            debug!("{} response: {:?}", RpcFields::SignMultisig.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'exchange_multisig_keys' method
pub async fn exchange_multisig_keys(
    force_update_use_with_caution: bool,
    multisig_info: Vec<String>,
    password: &String,
) -> reqres::XmrRpcExchangeMultisigKeysResponse {
    info!("executing: {}", RpcFields::ExchangeMultisigKeys.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params = reqres::XmrRpcExchangeMultisigKeysParams {
        force_update_use_with_caution,
        password: String::from(password),
        multisig_info,
    };
    let req = reqres::XmrRpcExchangeMultisigKeysRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::ExchangeMultisigKeys.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response
                .json::<reqres::XmrRpcExchangeMultisigKeysResponse>()
                .await;
            debug!(
                "{} response: {:?}",
                RpcFields::ExchangeMultisigKeys.value(),
                res
            );
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}
// END Multisig

/// Performs the xmr rpc 'check_tx_proof' method
pub async fn check_tx_proof(txp: &proof::TxProof) -> reqres::XmrRpcCheckTxProofResponse {
    info!("executing {}", RpcFields::CheckTxProof.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcCheckTxProofParams = reqres::XmrRpcCheckTxProofParams {
        address: String::from(&txp.subaddress),
        message: String::from(&txp.message),
        signature: String::from(&txp.signature),
        txid: String::from(&txp.hash),
    };
    let req = reqres::XmrRpcCheckTxProofRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::CheckTxProof.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcCheckTxProofResponse>().await;
            debug!("{} response: {:?}", RpcFields::CheckTxProof.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'get_tx_proof' method
pub async fn get_tx_proof(ptxp: proof::TxProof) -> reqres::XmrRpcGetTxProofResponse {
    info!("executing {}", RpcFields::GetTxProof.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcGetTxProofParams = reqres::XmrRpcGetTxProofParams {
        address: String::from(&ptxp.subaddress),
        message: String::from(&ptxp.message),
        txid: String::from(&ptxp.hash),
    };
    let req = reqres::XmrRpcGetTxProofRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::GetTxProof.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcGetTxProofResponse>().await;
            debug!("{} response: {:?}", RpcFields::GetTxProof.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'get_transfer_by_txid' method
pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse {
    info!("executing: {}", RpcFields::GetTxById.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcGetTxByIdParams = reqres::XmrRpcGetTxByIdParams {
        txid: String::from(txid),
    };
    let req = reqres::XmrRpcGetTxByIdRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::GetTxById.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcGetTxByIdResponse>().await;
            debug!("{} response: {:?}", RpcFields::GetTxById.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'transfer' method
pub async fn transfer(d: reqres::Destination) -> reqres::XmrRpcTransferResponse {
    info!("executing {}", RpcFields::Transfer.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let mut destinations: Vec<reqres::Destination> = Vec::new();
    destinations.push(d);
    let params: reqres::XmrRpcTransferParams = reqres::XmrRpcTransferParams {
        account_index: 0,
        destinations,
        get_tx_key: false,
        priority: 0,
        ring_size: RING_SIZE,
        subaddr_indices: vec![0],
    };
    let req = reqres::XmrRpcTransfrerRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::Transfer.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcTransferResponse>().await;
            debug!("{} response: {:?}", RpcFields::Transfer.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'sweep_all' method
pub async fn sweep_all(address: String) -> reqres::XmrRpcSweepAllResponse {
    info!("executing {}", RpcFields::SweepAll.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcSweepAllParams = reqres::XmrRpcSweepAllParams { address };
    let req = reqres::XmrRpcSweepAllRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::SweepAll.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcSweepAllResponse>().await;
            debug!("{} response: {:?}", RpcFields::SweepAll.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr rpc 'create_address' method
pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
    info!("executing {}", RpcFields::CreateAddress.value());
    let client = reqwest::Client::new();
    let host = get_rpc_host();
    let params: reqres::XmrRpcCreateAddressParams =
        reqres::XmrRpcCreateAddressParams { account_index: 0 };
    let req = reqres::XmrRpcCreateAddressRequest {
        jsonrpc: RpcFields::JsonRpcVersion.value(),
        id: RpcFields::Id.value(),
        method: RpcFields::CreateAddress.value(),
        params,
    };
    let login: RpcLogin = get_rpc_creds();
    match client
        .post(host)
        .json(&req)
        .send_with_digest_auth(&login.username, &login.credential)
        .await
    {
        Ok(response) => {
            let res = response.json::<reqres::XmrRpcCreateAddressResponse>().await;
            debug!("{} response: {:?}", RpcFields::CreateAddress.value(), res);
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

// Daemon requests
//-------------------------------------------------------------------
/// Performs the xmr daemon 'get_info' method
pub async fn get_info() -> reqres::XmrDaemonGetInfoResponse {
    info!("fetching daemon info");
    let client = reqwest::Client::new();
    let host = get_rpc_daemon();
    let req = reqres::XmrRpcRequest {
        jsonrpc: DaemonFields::Version.value(),
        id: DaemonFields::Id.value(),
        method: DaemonFields::GetInfo.value(),
    };
    match client.post(host).json(&req).send().await {
        Ok(response) => {
            let res = response.json::<reqres::XmrDaemonGetInfoResponse>().await;
            // add debug log here if needed for adding more info to home screen in gui
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr daemon 'get_height' method
pub async fn get_height() -> reqres::XmrDaemonGetHeightResponse {
    info!("fetching daemon height");
    let client = reqwest::Client::new();
    let args = args::Args::parse();
    let daemon = String::from(args.monero_rpc_daemon);
    let req = format!("{}/{}", daemon, DaemonFields::GetHeight.value());
    match client.post(req).send().await {
        Ok(response) => {
            let res = response.json::<reqres::XmrDaemonGetHeightResponse>().await;
            // don't log this one. The fee estimator blows up logs (T_T)
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr daemon 'get_block' method
pub async fn get_block(height: u64) -> reqres::XmrDaemonGetBlockResponse {
    info!("fetching block at height: {}", height);
    let client = reqwest::Client::new();
    let host = get_rpc_daemon();
    let params: reqres::XmrDaemonGetBlockParams = reqres::XmrDaemonGetBlockParams { height };
    let req = reqres::XmrDaemonGetBlockRequest {
        jsonrpc: DaemonFields::Version.value(),
        id: DaemonFields::Id.value(),
        method: DaemonFields::GetBlock.value(),
        params,
    };
    match client.post(host).json(&req).send().await {
        Ok(response) => {
            let res = response.json::<reqres::XmrDaemonGetBlockResponse>().await;
            // don't log this one. The fee estimator blows up logs (T_T)
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}

/// Performs the xmr daemon 'get_transactions' method
pub async fn get_transactions(txs_hashes: Vec<String>) -> reqres::XmrDaemonGetTransactionsResponse {
    info!("fetching {} transactions", txs_hashes.len());
    let client = reqwest::Client::new();
    let args = args::Args::parse();
    let daemon = String::from(args.monero_rpc_daemon);
    let url = format!("{}/{}", daemon, DaemonFields::GetTransactions.value());
    let req = reqres::XmrDaemonGetTransactionsRequest {
        txs_hashes,
        decode_as_json: true,
    };
    match client.post(url).json(&req).send().await {
        Ok(response) => {
            let res = response
                .json::<reqres::XmrDaemonGetTransactionsResponse>()
                .await;
            // don't log this one. The fee estimator blows up logs (T_T)
            match res {
                Ok(res) => res,
                _ => Default::default(),
            }
        }
        Err(_) => Default::default(),
    }
}