//! evm_client: EVM RPC client and integration



//! evm_client: Minimal EVM JSON-RPC client abstraction

//! evm_client: Minimal EVM JSON-RPC client abstraction

//! evm_client: Minimal EVM JSON-RPC client abstraction

//! evm_client: Minimal EVM JSON-RPC client abstraction

//! evm_client: Minimal EVM JSON-RPC client abstraction

pub use ethers_core::types::*;
pub mod provider;
pub mod signer;
pub mod rhai_bindings;
pub mod rhai_sync_helpers;
pub mod error;
pub use provider::send_rpc;
pub use error::EvmError;

/// Public EVM client struct for use in bindings and sync helpers
pub struct Provider {
    pub rpc_url: String,
    pub chain_id: u64,
    pub explorer_url: Option<String>,
}

pub struct EvmClient {
    pub provider: Provider,
}

impl EvmClient {
    pub fn new(provider: Provider) -> Self {
        Self { provider }
    }

    /// Initialize logging for the current target (native: env_logger, WASM: console_log)
    /// Call this before using any log macros.
    pub fn init_logging() {
        use std::sync::Once;
        static INIT: Once = Once::new();
        INIT.call_once(|| {
            #[cfg(not(target_arch = "wasm32"))]
            {
                use env_logger;
                let _ = env_logger::builder().is_test(false).try_init();
            }
            #[cfg(target_arch = "wasm32")]
            {
                use console_log;
                let _ = console_log::init_with_level(log::Level::Debug);
            }
        });
    }

    pub async fn get_balance(&self, address: Address) -> Result<U256, EvmError> {
        // TODO: Use provider info
        provider::get_balance(&self.provider.rpc_url, address)
            .await
            .map_err(|e| EvmError::Rpc(e.to_string()))
    }

    pub async fn send_transaction(
        &self,
        mut tx: provider::Transaction,
        signer: &dyn crate::signer::Signer,
    ) -> Result<ethers_core::types::H256, EvmError> {
        use ethers_core::types::{U256, H256, Bytes, Address};
        use std::str::FromStr;
        use serde_json::json;
        use crate::provider::{send_rpc, parse_signature_rs_v};

        // 1. Fill in missing fields via JSON-RPC if needed
        // Parse signer address as H160
        let signer_addr = ethers_core::types::Address::from_str(&signer.address())
            .map_err(|e| EvmError::Rpc(format!("Invalid signer address: {}", e)))?;
        // Nonce
        if tx.nonce.is_none() {
            let body = json!({
                "jsonrpc": "2.0",
                "method": "eth_getTransactionCount",
                "params": [format!("0x{:x}", signer_addr), "pending"],
                "id": 1
            }).to_string();
            let resp = send_rpc(&self.provider.rpc_url, &body).await.map_err(|e| EvmError::Rpc(e.to_string()))?;
            let v: serde_json::Value = serde_json::from_str(&resp).map_err(|e| EvmError::Rpc(e.to_string()))?;
            let hex = v["result"].as_str().ok_or_else(|| EvmError::Rpc("No result field in eth_getTransactionCount".to_string()))?;
            tx.nonce = Some(U256::from_str_radix(hex.trim_start_matches("0x"), 16).map_err(|e| EvmError::Rpc(e.to_string()))?);
        }
        // Gas Price
        if tx.gas_price.is_none() {
            let body = json!({
                "jsonrpc": "2.0",
                "method": "eth_gasPrice",
                "params": [],
                "id": 1
            }).to_string();
            let resp = send_rpc(&self.provider.rpc_url, &body).await.map_err(|e| EvmError::Rpc(e.to_string()))?;
            let v: serde_json::Value = serde_json::from_str(&resp).map_err(|e| EvmError::Rpc(e.to_string()))?;
            let hex = v["result"].as_str().ok_or_else(|| EvmError::Rpc("No result field in eth_gasPrice".to_string()))?;
            tx.gas_price = Some(U256::from_str_radix(hex.trim_start_matches("0x"), 16).map_err(|e| EvmError::Rpc(e.to_string()))?);
        }
        // Chain ID
        if tx.chain_id.is_none() {
            tx.chain_id = Some(self.provider.chain_id);
        }
        // Gas (optional: estimate if missing)
        if tx.gas.is_none() {
            let body = json!({
                "jsonrpc": "2.0",
                "method": "eth_estimateGas",
                "params": [{
                    "to": format!("0x{:x}", tx.to),
                    "from": format!("0x{:x}", signer_addr),
                    "value": format!("0x{:x}", tx.value),
                    "data": format!("0x{}", hex::encode(&tx.data)),
                }],
                "id": 1
            }).to_string();
            let resp = send_rpc(&self.provider.rpc_url, &body).await.map_err(|e| EvmError::Rpc(e.to_string()))?;
            let v: serde_json::Value = serde_json::from_str(&resp).map_err(|e| EvmError::Rpc(e.to_string()))?;
            let hex = v["result"].as_str().ok_or_else(|| EvmError::Rpc("No result field in eth_estimateGas".to_string()))?;
            tx.gas = Some(U256::from_str_radix(hex.trim_start_matches("0x"), 16).map_err(|e| EvmError::Rpc(e.to_string()))?);
        }

        // 2. RLP encode unsigned transaction
        let rlp_unsigned = tx.rlp_encode_unsigned();

        // 3. Sign the RLP-encoded unsigned transaction
        let sig = signer.sign(&rlp_unsigned).await?;
        let (r, s, v) = parse_signature_rs_v(&sig, tx.chain_id.unwrap()).ok_or_else(|| EvmError::Signing("Invalid signature format".to_string()))?;

        // 4. RLP encode signed transaction (EIP-155)
        use rlp::RlpStream;
        let mut rlp_stream = RlpStream::new_list(9);
        rlp_stream.append(&tx.nonce.unwrap());
        rlp_stream.append(&tx.gas_price.unwrap());
        rlp_stream.append(&tx.gas.unwrap());
        rlp_stream.append(&tx.to);
        rlp_stream.append(&tx.value);
        rlp_stream.append(&tx.data.to_vec());
        rlp_stream.append(&tx.chain_id.unwrap());
        rlp_stream.append(&r);
        rlp_stream.append(&s);
        let raw_tx = rlp_stream.out().to_vec();

        // 5. Broadcast the raw transaction
        let raw_hex = format!("0x{}", hex::encode(&raw_tx));
        let body = json!({
            "jsonrpc": "2.0",
            "method": "eth_sendRawTransaction",
            "params": [raw_hex],
            "id": 1
        }).to_string();
        let resp = send_rpc(&self.provider.rpc_url, &body).await.map_err(|e| EvmError::Rpc(e.to_string()))?;
        let v: serde_json::Value = serde_json::from_str(&resp).map_err(|e| EvmError::Rpc(e.to_string()))?;
        let tx_hash_hex = v["result"].as_str().ok_or_else(|| EvmError::Rpc("No result field in eth_sendRawTransaction".to_string()))?;
        let tx_hash = H256::from_slice(&hex::decode(tx_hash_hex.trim_start_matches("0x")).map_err(|e| EvmError::Rpc(e.to_string()))?);
        Ok(tx_hash)
    }
}
