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

use std::str::FromStr;

use crate::{
    error::{Result, RialoError},
    generated::{rpc_client::RpcClient, types::*},
};
/// Business-level mock RPC client for testing.
/// Returns business objects directly rather than mocking HTTP responses.
pub struct MockRpcClient {
    /// Pre-configured business object responses
    pub account_info: Option<AccountInfo>,
    pub balance: Kelvin,
    pub signature: Signature,
    pub block_height: u64,
    pub slot: u64,
    pub transaction: Option<TransactionResponse>,
    pub transaction_count: u64,
    pub rent_exemption: u64,
    pub signature_statuses: Vec<Option<SignatureStatus>>,
    pub subscription: Option<Subscription>,
    pub triggered_transactions: Vec<TriggeredTransaction>,
    /// Optional error message to return instead of success responses
    pub error_message: Option<String>,
}

/// Mock signature constant used in tests.
/// This is a valid base58-encoded signature format.
pub const MOCK_SIGNATURE: &str =
    "4rWVz1CniwxGQJTZkxLfKc9qCCPGAL1HKLHJWkAdbfdiJeXRJkpvHxHjvEZMtWrJdXKFKBSRVdGLKFMK2jsQCXWx";

/// Mock blockhash constant used in tests.
/// This is a valid base58-encoded hash format.
pub const MOCK_BLOCKHASH: &str = "EkSnNWid2cvwEVnVx9aBqawnmiCNiDgp3gUdkDPTKN1N";

/// Mock slot number used in tests.
pub const MOCK_SLOT: u64 = 123456789;

/// Mock transaction fee used in tests (in kelvin).
pub const MOCK_FEE: u64 = 5000;

/// Error message used in tests.
pub const ERROR_MESSAGE: &str = "Invalid signature";

impl Default for MockRpcClient {
    fn default() -> Self {
        let sig = Signature::from_str(MOCK_SIGNATURE).unwrap();
        let system_program = rialo_s_sdk::system_program::id();

        Self {
            account_info: Some(AccountInfo {
                kelvin: 100,
                owner: system_program,
                data: vec![],
                executable: false,
                rent_epoch: 0,
                space: 0,
            }),
            balance: 100,
            signature: sig,
            block_height: MOCK_SLOT,
            slot: MOCK_SLOT,
            transaction: Some(TransactionResponse {
                block_height: MOCK_SLOT,
                meta: TransactionStatusMetadata {
                    log_messages: Some(vec![]),
                    err: None,
                    fee: MOCK_FEE,
                },
                transaction: TransactionData {
                    signatures: vec![Signature::from_str(MOCK_SIGNATURE).unwrap()],
                    message: TransactionMessage {
                        header: MessageHeader {
                            num_required_signatures: 1,
                            num_readonly_signed_accounts: 0,
                            num_readonly_unsigned_accounts: 1,
                        },
                        account_keys: vec![
                            rialo_s_sdk::pubkey::Pubkey::new_unique(),
                            system_program,
                        ],
                        instructions: vec![],
                    },
                    valid_from: 0,
                },
                block_time: None,
            }),
            transaction_count: 1000,
            rent_exemption: 2039280,
            signature_statuses: vec![Some(SignatureStatus {
                slot: MOCK_SLOT,
                err: None,
                executed: true,
            })],
            subscription: None,
            triggered_transactions: vec![],
            error_message: None,
        }
    }
}

impl MockRpcClient {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn with_error(mut self, error_message: String) -> Self {
        self.error_message = Some(error_message);
        self
    }

    pub fn with_account_info(mut self, account_info: AccountInfo) -> Self {
        self.account_info = Some(account_info);
        self
    }

    pub fn with_no_account(mut self) -> Self {
        self.account_info = None;
        self
    }

    pub fn with_balance(mut self, balance: Kelvin) -> Self {
        self.balance = balance;
        self
    }

    pub fn with_signature(mut self, signature: Signature) -> Self {
        self.signature = signature;
        self
    }

    pub fn with_transaction(mut self, transaction: TransactionResponse) -> Self {
        self.transaction = Some(transaction);
        self
    }

    pub fn with_transaction_count(mut self, count: u64) -> Self {
        self.transaction_count = count;
        self
    }

    pub fn with_rent_exemption(mut self, rent_exemption: u64) -> Self {
        self.rent_exemption = rent_exemption;
        self
    }

    pub fn with_slot(mut self, slot: u64) -> Self {
        self.slot = slot;
        self
    }

    /// Sets the mock block height value that will be returned by get_block_height calls.
    pub fn with_block_height(mut self, block_height: u64) -> Self {
        self.block_height = block_height;
        self
    }

    pub fn with_subscription(mut self, subscription: Subscription) -> Self {
        self.subscription = Some(subscription);
        self
    }

    pub fn with_triggered_transactions(
        mut self,
        triggered_transactions: Vec<TriggeredTransaction>,
    ) -> Self {
        self.triggered_transactions = triggered_transactions;
        self
    }

    pub fn with_signature_statuses(
        mut self,
        signature_statuses: Vec<Option<SignatureStatus>>,
    ) -> Self {
        self.signature_statuses = signature_statuses;
        self
    }

    /// Generic JSON-RPC call — not part of the generated trait.
    pub async fn call_with_json(
        &self,
        _method: &str,
        _params: serde_json::Value,
    ) -> Result<serde_json::Value> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        // Return a generic success response - tests should use specific methods instead
        Ok(serde_json::json!({"success": true}))
    }
}

/// Implement RpcClient trait for MockRpcClient with business-level responses
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
impl RpcClient for MockRpcClient {
    async fn get_balance(&self, _pubkey: &PublicKey) -> Result<Kelvin> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.balance)
    }

    async fn send_transaction(
        &self,
        _transaction: &[u8],
        _options: Option<SendTransactionOptions>,
    ) -> Result<Signature> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.signature)
    }

    async fn get_account_info(&self, _pubkey: &PublicKey) -> Result<AccountInfo> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        match &self.account_info {
            Some(info) => Ok(info.clone()),
            None => Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message("Account not found".to_string()),
            )),
        }
    }

    /// Mock implementation of get_block_height for CLI testing.
    /// Returns the configured mock block height value or an error if error_message is set.
    async fn get_block_height(&self) -> Result<u64> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.block_height)
    }

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

    async fn get_transaction(&self, _signature: &Signature) -> Result<TransactionResponse> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        match &self.transaction {
            Some(tx) => Ok(tx.clone()),
            None => Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message("Transaction not found".to_string()),
            )),
        }
    }

    async fn get_transaction_count(&self) -> Result<u64> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.transaction_count)
    }

    async fn get_minimum_balance_for_rent_exemption(&self, _size: u64) -> Result<u64> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.rent_exemption)
    }

    async fn get_signature_statuses(
        &self,
        _signatures: &[Signature],
    ) -> Result<Vec<Option<SignatureStatus>>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.signature_statuses.clone())
    }

    async fn get_subscription(
        &self,
        _subscriber: &PublicKey,
        _nonce: &str,
    ) -> Result<Subscription> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        self.subscription.clone().ok_or_else(|| {
            RialoError::Rpc(crate::error::RpcErrorDetails::from_message(
                "Subscription not found".to_string(),
            ))
        })
    }

    async fn get_triggered_transactions(
        &self,
        _subscription_account: &PublicKey,
        _limit: Option<u32>,
    ) -> Result<Vec<TriggeredTransaction>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.triggered_transactions.clone())
    }

    async fn get_workflow_lineage(
        &self,
        _request: &GetWorkflowLineageRequest,
    ) -> Result<GetWorkflowLineageResponse> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(GetWorkflowLineageResponse {
            lineage: WorkflowLineage {
                workflow_nodes: vec![],
            },
            leaves: vec![],
            truncated: false,
            truncation_reason: TruncationReason::None,
            continuation_hints: vec![],
        })
    }

    async fn request_airdrop(&self, _pubkey: &PublicKey, _amount: Kelvin) -> Result<Signature> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(self.signature)
    }

    async fn get_epoch_info(&self) -> Result<EpochInfo> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(EpochInfo {
            absolute_slot: 166598,
            block_height: 166500,
            epoch: 27,
            slot_index: 2790,
            slots_in_epoch: 8192,
            transaction_count: Some(22),
        })
    }

    async fn get_fee_for_message(&self, _message: &str) -> Result<FeeResponse> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(FeeResponse { value: Some(5000) })
    }

    async fn get_multiple_accounts(
        &self,
        _pubkeys: &[PublicKey],
    ) -> Result<Vec<OptionalAccountInfo>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_signatures_for_address(
        &self,
        _address: &PublicKey,
        _config: Option<GetSignaturesForAddressConfig>,
    ) -> Result<Vec<SignatureInfo>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn is_blockhash_valid(&self, _blockhash: &Hash) -> Result<IsBlockhashValidResponse> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(IsBlockhashValidResponse {
            slot: self.slot,
            is_valid: true,
        })
    }

    async fn get_health(&self) -> Result<String> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok("ok".to_string())
    }

    async fn get_validator_health(&self) -> Result<ValidatorHealth> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(ValidatorHealth {
            status: "ok".to_string(),
        })
    }

    async fn get_connected_full_nodes(&self) -> Result<Vec<ConnectedNode>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_transactions(
        &self,
        _config: Option<GetTransactionsConfig>,
    ) -> Result<Vec<TransactionInfo>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_secret_sharing_pubkey(&self) -> Result<SecretSharingPubkey> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(SecretSharingPubkey {
            public_key: "01".repeat(32),
        })
    }

    async fn send_admin_transaction(
        &self,
        _request: &SubmitEpochChangeRequest,
    ) -> Result<SubmitEpochChangeResponse> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(SubmitEpochChangeResponse { success: true })
    }

    async fn get_accounts_by_owner(
        &self,
        _owner: &PublicKey,
        _filter: Option<crate::generated::types::AccountFilter>,
        _config: Option<crate::generated::types::GetAccountsByOwnerConfig>,
    ) -> Result<(Vec<OwnerAccount>, Option<PaginationInfo>)> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok((vec![], None))
    }

    async fn get_config_hash_prefix(&self) -> Result<ConfigHashPrefix> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(ConfigHashPrefix::new(1))
    }

    async fn confirm_transaction(
        &self,
        sig: &str,
        _options: Option<ConfirmTransactionOptions>,
    ) -> Result<ConfirmedTransaction> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(ConfirmedTransaction {
            signature: sig.to_string(),
            executed: true,
            err: None,
        })
    }

    async fn send_and_confirm_transaction(
        &self,
        _transaction: &[u8],
        _options: Option<SendAndConfirmOptions>,
    ) -> Result<ConfirmedTransaction> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(ConfirmedTransaction {
            signature: self.signature.to_string(),
            executed: true,
            err: None,
        })
    }

    async fn request_airdrop_and_confirm(
        &self,
        _pubkey: &PublicKey,
        _amount: Kelvin,
    ) -> Result<ConfirmedTransaction> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(ConfirmedTransaction {
            signature: self.signature.to_string(),
            executed: true,
            err: None,
        })
    }

    async fn get_all_accounts(
        &self,
        _config: Option<GetAllAccountsConfig>,
    ) -> Result<Vec<AllAccountsEntry>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_block(
        &self,
        _block_height: u64,
        _config: Option<GetBlockConfig>,
    ) -> Result<BlockInfo> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(BlockInfo {
            blockhash: MOCK_BLOCKHASH.to_string(),
            block_height: self.block_height,
            block_time: 0,
            transactions: None,
            signatures: None,
        })
    }

    async fn get_blocks(&self, _start_height: u64, _end_height: Option<u64>) -> Result<Vec<u64>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_stake_account(&self, _pubkey: &PublicKey) -> Result<Option<StakeAccountInfo>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(None)
    }

    async fn get_validator_accounts(
        &self,
        _request: &GetValidatorAccountsRequest,
    ) -> Result<Vec<ValidatorAccountInfo>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_token_account_balance(&self, _pubkey: &PublicKey) -> Result<TokenBalance> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        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>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_rex_missed_duties(&self, _creator: &PublicKey, _nonce: &str) -> Result<Vec<u64>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_cluster_nodes(&self) -> Result<Vec<ClusterNodeInfo>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }

    async fn get_connected_validators(&self) -> Result<Vec<u32>> {
        if let Some(error_msg) = &self.error_message {
            return Err(RialoError::Rpc(
                crate::error::RpcErrorDetails::from_message(error_msg.clone()),
            ));
        }
        Ok(vec![])
    }
}
