use super::RpcHandler;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize)]
struct BitcoinInfo {
    block_height: u64,
    sync_progress: f64,
    chain: String,
    difficulty: f64,
    mempool_size: u64,
    mempool_tx_count: u64,
    verification_progress: f64,
}

#[derive(Debug, Deserialize)]
struct BitcoinRpcResponse<T> {
    result: Option<T>,
    error: Option<serde_json::Value>,
}

#[derive(Debug, Deserialize)]
struct BlockchainInfo {
    chain: Option<String>,
    blocks: Option<u64>,
    difficulty: Option<f64>,
    #[serde(rename = "verificationprogress")]
    verification_progress: Option<f64>,
}

#[derive(Debug, Deserialize)]
struct MempoolInfo {
    size: Option<u64>,
    bytes: Option<u64>,
}

impl RpcHandler {
    pub(super) async fn handle_bitcoin_getinfo(&self) -> Result<serde_json::Value> {
        let client = reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(10))
            .build()
            .context("Failed to create HTTP client")?;

        let blockchain_info = self
            .bitcoin_rpc_call::<BlockchainInfo>(&client, "getblockchaininfo", &[])
            .await
            .context("Failed to query getblockchaininfo")?;

        let mempool_info = self
            .bitcoin_rpc_call::<MempoolInfo>(&client, "getmempoolinfo", &[])
            .await
            .unwrap_or(MempoolInfo {
                size: Some(0),
                bytes: Some(0),
            });

        let info = BitcoinInfo {
            block_height: blockchain_info.blocks.unwrap_or(0),
            sync_progress: blockchain_info
                .verification_progress
                .unwrap_or(0.0),
            chain: blockchain_info.chain.unwrap_or_else(|| "unknown".into()),
            difficulty: blockchain_info.difficulty.unwrap_or(0.0),
            mempool_size: mempool_info.bytes.unwrap_or(0),
            mempool_tx_count: mempool_info.size.unwrap_or(0),
            verification_progress: blockchain_info
                .verification_progress
                .unwrap_or(0.0),
        };

        Ok(serde_json::to_value(info)?)
    }

    async fn bitcoin_rpc_call<T: serde::de::DeserializeOwned>(
        &self,
        client: &reqwest::Client,
        method: &str,
        params: &[serde_json::Value],
    ) -> Result<T> {
        let body = serde_json::json!({
            "jsonrpc": "1.0",
            "id": "archy",
            "method": method,
            "params": params,
        });

        let resp = client
            .post("http://127.0.0.1:8332/")
            .basic_auth("archipelago", Some("archipelago123"))
            .json(&body)
            .send()
            .await
            .context("Bitcoin RPC connection failed")?;

        let rpc_resp: BitcoinRpcResponse<T> = resp
            .json()
            .await
            .context("Failed to parse Bitcoin RPC response")?;

        if let Some(err) = rpc_resp.error {
            anyhow::bail!("Bitcoin RPC error: {}", err);
        }

        rpc_resp
            .result
            .ok_or_else(|| anyhow::anyhow!("Bitcoin RPC returned null result"))
    }
}
