//! Mining commands.

use anyhow::Result;

use crate::client::RpcClient;
use crate::output::{self, OutputFormat};
use crate::MiningCommands;

/// Handle mining commands.
pub async fn handle(client: &RpcClient, cmd: MiningCommands, format: OutputFormat) -> Result<()> {
    match cmd {
        MiningCommands::Info => info(client, format).await,
        MiningCommands::Template { address } => template(client, &address, format).await,
        MiningCommands::Submit { block } => submit(client, &block, format).await,
        MiningCommands::Hashrate => hashrate(client, format).await,
    }
}

/// Get mining info.
async fn info(client: &RpcClient, format: OutputFormat) -> Result<()> {
    let info = client.get_mining_info().await?;

    match format {
        OutputFormat::Json => {
            output::print_value(&info, format);
        }
        OutputFormat::Text => {
            output::print_header("Mining Information");
            output::print_kv("Blocks", &info.blocks.to_string());
            output::print_kv("Difficulty", &format!("{:.6}", info.difficulty));
            output::print_kv(
                "Network Hashrate",
                &output::format_hashrate(info.network_hashrate),
            );
            if let Some(pool_hr) = info.pool_hashrate {
                output::print_kv("Pool Hashrate", &output::format_hashrate(pool_hr));
            }
        }
    }

    Ok(())
}

/// Get block template.
async fn template(client: &RpcClient, address: &str, format: OutputFormat) -> Result<()> {
    let template = client.get_block_template(address).await?;

    match format {
        OutputFormat::Json => {
            output::print_value(&template, format);
        }
        OutputFormat::Text => {
            output::print_header("Block Template");
            output::print_kv("Version", &template.header.version.to_string());
            output::print_kv("Parents", &template.header.parents.len().to_string());
            output::print_kv(
                "Timestamp",
                &output::format_timestamp(template.header.timestamp),
            );
            output::print_kv("Bits", &format!("0x{:08x}", template.header.bits));
            output::print_kv("Blue Score", &template.header.blue_score.to_string());
            output::print_kv("Target", &output::format_hash(&template.target));
            output::print_kv("Transactions", &template.transactions.len().to_string());
            output::print_kv("Synced", if template.is_synced { "Yes" } else { "No" });

            println!("\nParent hashes:");
            for parent in &template.header.parents {
                println!("  {}", parent);
            }
        }
    }

    Ok(())
}

/// Submit a mined block.
async fn submit(client: &RpcClient, block_hex: &str, format: OutputFormat) -> Result<()> {
    let result = client.submit_block(block_hex).await?;

    match format {
        OutputFormat::Json => {
            let json = serde_json::json!({
                "success": true,
                "hash": result,
            });
            println!("{}", serde_json::to_string_pretty(&json)?);
        }
        OutputFormat::Text => {
            output::print_success("Block submitted successfully!");
            output::print_kv("Hash", &result);
        }
    }

    Ok(())
}

/// Get network hashrate estimate.
async fn hashrate(client: &RpcClient, format: OutputFormat) -> Result<()> {
    let info = client.get_mining_info().await?;

    match format {
        OutputFormat::Json => {
            let json = serde_json::json!({
                "hashrate": info.network_hashrate,
                "difficulty": info.difficulty,
            });
            println!("{}", serde_json::to_string_pretty(&json)?);
        }
        OutputFormat::Text => {
            output::print_kv(
                "Network Hashrate",
                &output::format_hashrate(info.network_hashrate),
            );
            output::print_kv("Difficulty", &format!("{:.6}", info.difficulty));
        }
    }

    Ok(())
}
