// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::{collections::HashMap, str::FromStr};

use async_trait::async_trait;
use rialo_cdk::{
    error::{Result, RialoError},
    generated::{rpc_client::RpcClient, types::*},
};
use serde_json::Value;

pub struct MockRpcClient {
    pub responses: HashMap<String, Value>,
}

impl MockRpcClient {
    #[allow(dead_code)]
    pub fn new_with_responses(responses: Vec<(&str, Value)>) -> Self {
        let mut map = HashMap::new();
        for (method, response) in responses {
            map.insert(method.to_string(), response);
        }
        Self { responses: map }
    }
    pub async fn call_with_json(&self, method: &str, _params: Value) -> Result<Value> {
        match self.responses.get(method) {
            Some(response) => Ok(response.clone()),
            None => {
                let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                    "No mock response for {method}"
                ));
                Err(RialoError::Rpc(error_details))
            }
        }
    }
}

#[async_trait]
impl RpcClient for MockRpcClient {
    async fn get_balance(&self, _pubkey: &PublicKey) -> Result<Kelvin> {
        let response = self.call_with_json("getBalance", Value::Null).await?;
        let val = response
            .get("value")
            .and_then(|v| v.as_u64())
            .ok_or_else(|| {
                let error_details = rialo_cdk::error::RpcErrorDetails::from_message(
                    "Invalid mock response format for getBalance".to_string(),
                );
                RialoError::Rpc(error_details)
            })?;
        Ok(val)
    }

    async fn send_transaction(
        &self,
        _transaction: &[u8],
        _options: Option<SendTransactionOptions>,
    ) -> Result<Signature> {
        let response = self.call_with_json("sendTransaction", Value::Null).await?;
        response
            .as_str()
            .ok_or_else(|| {
                let error_details = rialo_cdk::error::RpcErrorDetails::from_message(
                    "Invalid mock response format for sendTransaction".to_string(),
                );
                RialoError::Rpc(error_details)
            })
            .and_then(|signature_str| {
                Signature::from_str(signature_str).map_err(|e| {
                    let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                        "Invalid signature format: {e}"
                    ));
                    RialoError::Rpc(error_details)
                })
            })
    }

    async fn get_account_info(&self, _pubkey: &PublicKey) -> Result<AccountInfo> {
        let response = self.call_with_json("getAccountInfo", Value::Null).await?;
        serde_json::from_value(response.get("value").unwrap_or(&Value::Null).clone()).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse account info: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    /// Mock implementation of get_block_height for testing.
    /// Returns a mock finalized block height value configured in the MockRpcClient.
    async fn get_block_height(&self) -> Result<u64> {
        let response = self.call_with_json("getBlockHeight", Value::Null).await?;
        response.as_u64().ok_or_else(|| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(
                "Invalid mock response format for getBlockHeight".to_string(),
            );
            RialoError::Rpc(error_details)
        })
    }

    async fn get_block_height_with_config(&self, _min_context_slot: Option<u64>) -> Result<u64> {
        // For mocking, just delegate to the basic method
        self.get_block_height().await
    }

    async fn get_transaction(&self, _signature: &Signature) -> Result<TransactionResponse> {
        let response = self.call_with_json("getTransaction", Value::Null).await?;
        serde_json::from_value(response).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse transaction: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    async fn get_transaction_count(&self) -> Result<u64> {
        let response = self
            .call_with_json("getTransactionCount", Value::Null)
            .await?;
        response.as_u64().ok_or_else(|| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(
                "Invalid mock response format for getTransactionCount".to_string(),
            );
            RialoError::Rpc(error_details)
        })
    }

    async fn get_minimum_balance_for_rent_exemption(&self, _data_size: u64) -> Result<u64> {
        let response = self
            .call_with_json("getMinimumBalanceForRentExemption", Value::Null)
            .await?;
        response.as_u64().ok_or_else(|| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(
                "Invalid mock response format for getMinimumBalanceForRentExemption".to_string(),
            );
            RialoError::Rpc(error_details)
        })
    }

    async fn get_signature_statuses(
        &self,
        _signatures: &[Signature],
    ) -> Result<Vec<Option<SignatureStatus>>> {
        let response = self
            .call_with_json("getSignatureStatuses", Value::Null)
            .await?;
        serde_json::from_value(response.get("value").unwrap_or(&Value::Null).clone()).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse signature statuses: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    async fn get_triggered_transactions(
        &self,
        _subscription_account: &PublicKey,
        _limit: Option<u32>,
    ) -> Result<Vec<TriggeredTransaction>> {
        let response = self
            .call_with_json("getTriggeredTransactions", Value::Null)
            .await?;
        serde_json::from_value(response).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse triggered transactions: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    async fn request_airdrop(&self, _pubkey: &PublicKey, _amount: Kelvin) -> Result<Signature> {
        let response = self.call_with_json("requestAirdrop", Value::Null).await?;
        response
            .as_str()
            .ok_or_else(|| {
                let error_details = rialo_cdk::error::RpcErrorDetails::from_message(
                    "Invalid mock response format for requestAirdrop".to_string(),
                );
                RialoError::Rpc(error_details)
            })
            .and_then(|signature_str| {
                Signature::from_str(signature_str).map_err(|e| {
                    let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                        "Invalid signature format: {e}"
                    ));
                    RialoError::Rpc(error_details)
                })
            })
    }

    async fn get_epoch_info(&self) -> Result<EpochInfo> {
        Ok(EpochInfo {
            absolute_slot: 1000,
            block_height: 1000,
            epoch: 10,
            slot_index: 100,
            slots_in_epoch: 432000,
            transaction_count: Some(100000),
        })
    }

    async fn get_fee_for_message(&self, _message: &str) -> Result<FeeResponse> {
        Ok(FeeResponse { value: Some(5000) })
    }

    async fn get_multiple_accounts(
        &self,
        _pubkeys: &[PublicKey],
    ) -> Result<Vec<OptionalAccountInfo>> {
        Ok(vec![])
    }

    async fn get_workflow_lineage(
        &self,
        _request: &GetWorkflowLineageRequest,
    ) -> Result<GetWorkflowLineageResponse> {
        let response = self
            .call_with_json("getWorkflowLineage", Value::Null)
            .await?;
        serde_json::from_value(response).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse workflow lineage: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    async fn get_signatures_for_address(
        &self,
        _address: &PublicKey,
        _config: Option<GetSignaturesForAddressConfig>,
    ) -> Result<Vec<SignatureInfo>> {
        Ok(vec![])
    }

    async fn get_subscription(
        &self,
        _subscriber: &PublicKey,
        _nonce: &str,
    ) -> Result<Subscription> {
        let response = self.call_with_json("getSubscription", Value::Null).await?;
        serde_json::from_value(response).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse subscription: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    async fn is_blockhash_valid(&self, _blockhash: &Hash) -> Result<IsBlockhashValidResponse> {
        Ok(IsBlockhashValidResponse {
            slot: 1000,
            is_valid: true,
        })
    }

    async fn get_health(&self) -> Result<String> {
        Ok("ok".to_string())
    }

    async fn get_validator_health(&self) -> Result<ValidatorHealth> {
        Ok(ValidatorHealth {
            status: "ok".to_string(),
        })
    }

    async fn get_connected_full_nodes(&self) -> Result<Vec<ConnectedNode>> {
        Ok(vec![])
    }

    async fn get_transactions(
        &self,
        _config: Option<GetTransactionsConfig>,
    ) -> Result<Vec<TransactionInfo>> {
        let response = self.call_with_json("getTransactions", Value::Null).await?;
        serde_json::from_value(response)
            .map_err(|e| RialoError::Transaction(format!("Failed to parse transactions: {e}")))
    }

    async fn get_secret_sharing_pubkey(&self) -> Result<SecretSharingPubkey> {
        Ok(SecretSharingPubkey {
            public_key: "01".repeat(32),
        })
    }

    async fn send_admin_transaction(
        &self,
        _request: &SubmitEpochChangeRequest,
    ) -> Result<SubmitEpochChangeResponse> {
        Ok(SubmitEpochChangeResponse { success: true })
    }

    async fn get_accounts_by_owner(
        &self,
        _owner: &PublicKey,
        _filter: Option<AccountFilter>,
        _config: Option<GetAccountsByOwnerConfig>,
    ) -> Result<(Vec<OwnerAccount>, Option<PaginationInfo>)> {
        Ok((vec![], None))
    }

    async fn get_config_hash_prefix(&self) -> Result<ConfigHashPrefix> {
        Ok(ConfigHashPrefix::new(1)) // Test value
    }

    async fn get_all_accounts(
        &self,
        _config: Option<GetAllAccountsConfig>,
    ) -> Result<Vec<AllAccountsEntry>> {
        Ok(vec![])
    }

    async fn get_block(
        &self,
        _block_height: u64,
        _config: Option<GetBlockConfig>,
    ) -> Result<BlockInfo> {
        let response = self.call_with_json("getBlock", Value::Null).await?;
        serde_json::from_value(response).map_err(|e| {
            let error_details = rialo_cdk::error::RpcErrorDetails::from_message(format!(
                "Failed to parse block: {e}"
            ));
            RialoError::Rpc(error_details)
        })
    }

    async fn get_blocks(&self, _start_height: u64, _end_height: Option<u64>) -> Result<Vec<u64>> {
        Ok(vec![])
    }

    async fn get_stake_account(&self, _pubkey: &PublicKey) -> Result<Option<StakeAccountInfo>> {
        Ok(None)
    }

    async fn get_validator_accounts(
        &self,
        _request: &GetValidatorAccountsRequest,
    ) -> Result<Vec<ValidatorAccountInfo>> {
        Ok(vec![])
    }

    async fn get_token_account_balance(&self, _pubkey: &PublicKey) -> Result<TokenBalance> {
        Ok(TokenBalance {
            amount: "0".to_string(),
            decimals: 9,
            ui_amount_string: "0".to_string(),
        })
    }

    async fn get_rex_requests(
        &self,
        _creator: &PublicKey,
        _nonce: Option<String>,
    ) -> Result<Vec<RexInfoAndDuties>> {
        Ok(vec![])
    }

    async fn get_rex_missed_duties(&self, _creator: &PublicKey, _nonce: &str) -> Result<Vec<u64>> {
        Ok(vec![])
    }

    async fn get_cluster_nodes(&self) -> Result<Vec<ClusterNodeInfo>> {
        Ok(vec![])
    }

    async fn get_connected_validators(&self) -> Result<Vec<u32>> {
        Ok(vec![])
    }

    async fn confirm_transaction(
        &self,
        sig: &str,
        _options: Option<ConfirmTransactionOptions>,
    ) -> Result<ConfirmedTransaction> {
        Ok(ConfirmedTransaction {
            signature: sig.to_string(),
            executed: true,
            err: None,
        })
    }

    async fn send_and_confirm_transaction(
        &self,
        _transaction: &[u8],
        _options: Option<SendAndConfirmOptions>,
    ) -> Result<ConfirmedTransaction> {
        Ok(ConfirmedTransaction {
            signature: "mock_signature".to_string(),
            executed: true,
            err: None,
        })
    }

    async fn request_airdrop_and_confirm(
        &self,
        _pubkey: &PublicKey,
        _amount: Kelvin,
    ) -> Result<ConfirmedTransaction> {
        Ok(ConfirmedTransaction {
            signature: "mock_airdrop_signature".to_string(),
            executed: true,
            err: None,
        })
    }
}
