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

//! Client context for simplified RPC interaction.
//!
//! Provides a convenience wrapper that implements RpcClient for use
//! in benchmarks and tests without the full CLI configuration overhead.

use crate::{
    generated::{rpc_client::RpcClient, types::*},
    rpc::client::HttpRpcClient,
};

/// A simplified client context for benchmark and test usage.
///
/// This struct wraps an HttpRpcClient and provides a simple interface
/// for RPC operations without requiring full configuration management.
/// It's designed for use cases where you just need basic RPC functionality
/// without the complexity of the full CLI client context.
///
/// # Example
///
/// ```no_run
/// use rialo_cdk::ClientContext;
/// use rialo_cdk::RpcClient as _;
///
/// #[tokio::main]
/// async fn main() -> rialo_cdk::Result<()> {
///     let url = "http://127.0.0.1:4104".to_string();
///     let context = ClientContext::new(url);
///     
///     // Use it as an RpcClient
///     let block_height = context.get_block_height().await?;
///     println!("Block height: {}", block_height);
///     
///     Ok(())
/// }
/// ```
pub struct ClientContext {
    http_client: HttpRpcClient,
}

impl ClientContext {
    /// Creates a new ClientContext with the given RPC URL.
    ///
    /// # Arguments
    ///
    /// * `json_rpc_url` - The URL of the RPC endpoint to connect to (as a String)
    ///
    /// # Example
    ///
    /// ```no_run
    /// use rialo_cdk::ClientContext;
    ///
    /// let url = "http://127.0.0.1:4104".to_string();
    /// let context = ClientContext::new(url);
    /// ```
    pub fn new(json_rpc_url: String) -> Self {
        let http_client = HttpRpcClient::new(json_rpc_url);
        Self { http_client }
    }

    /// Creates a new ClientContext for benchmarks.
    ///
    /// This is a convenience method that creates a minimal context
    /// suitable for benchmark usage. It's functionally identical to `new()`,
    /// but the name makes the intent clearer in benchmark code.
    ///
    /// # Arguments
    ///
    /// * `json_rpc_url` - The URL of the RPC endpoint to connect to (as a String)
    ///
    /// # Example
    ///
    /// ```no_run
    /// use rialo_cdk::ClientContext;
    ///
    /// let url = "http://127.0.0.1:4104".to_string();
    /// let context = ClientContext::new_for_benchmarks(url);
    /// ```
    pub fn new_for_benchmarks(json_rpc_url: String) -> Self {
        Self::new(json_rpc_url)
    }

    /// Returns a reference to the underlying HTTP client.
    ///
    /// This can be useful if you need direct access to the HttpRpcClient's
    /// specific functionality beyond what the RpcClient trait provides.
    pub fn http_client(&self) -> &HttpRpcClient {
        &self.http_client
    }
    pub async fn call_with_json(
        &self,
        method: &str,
        params: serde_json::Value,
    ) -> crate::error::Result<serde_json::Value> {
        self.http_client.call_with_json(method, params).await
    }
}

// Implement RpcClient by delegating all methods to the inner HttpRpcClient
#[async_trait::async_trait]
impl RpcClient for ClientContext {
    async fn get_balance(&self, pubkey: &PublicKey) -> crate::error::Result<Kelvin> {
        self.http_client.get_balance(pubkey).await
    }

    async fn send_transaction(
        &self,
        transaction: &[u8],
        options: Option<SendTransactionOptions>,
    ) -> crate::error::Result<Signature> {
        self.http_client
            .send_transaction(transaction, options)
            .await
    }

    async fn get_secret_sharing_pubkey(&self) -> crate::error::Result<SecretSharingPubkey> {
        self.http_client.get_secret_sharing_pubkey().await
    }

    async fn send_admin_transaction(
        &self,
        request: &SubmitEpochChangeRequest,
    ) -> crate::error::Result<SubmitEpochChangeResponse> {
        self.http_client.send_admin_transaction(request).await
    }

    async fn get_account_info(&self, pubkey: &PublicKey) -> crate::error::Result<AccountInfo> {
        self.http_client.get_account_info(pubkey).await
    }

    async fn get_block_height(&self) -> crate::error::Result<u64> {
        self.http_client.get_block_height().await
    }

    async fn get_block_height_with_config(
        &self,
        min_context_slot: Option<u64>,
    ) -> crate::error::Result<u64> {
        self.http_client
            .get_block_height_with_config(min_context_slot)
            .await
    }

    async fn get_transaction(
        &self,
        signature: &Signature,
    ) -> crate::error::Result<TransactionResponse> {
        self.http_client.get_transaction(signature).await
    }

    async fn get_transaction_count(&self) -> crate::error::Result<u64> {
        self.http_client.get_transaction_count().await
    }

    async fn get_minimum_balance_for_rent_exemption(&self, size: u64) -> crate::error::Result<u64> {
        self.http_client
            .get_minimum_balance_for_rent_exemption(size)
            .await
    }

    async fn get_signature_statuses(
        &self,
        signatures: &[Signature],
    ) -> crate::error::Result<Vec<Option<SignatureStatus>>> {
        self.http_client.get_signature_statuses(signatures).await
    }

    async fn get_subscription(
        &self,
        subscriber: &PublicKey,
        nonce: &str,
    ) -> crate::error::Result<Subscription> {
        self.http_client.get_subscription(subscriber, nonce).await
    }

    async fn get_triggered_transactions(
        &self,
        subscription_account: &PublicKey,
        limit: Option<u32>,
    ) -> crate::error::Result<Vec<TriggeredTransaction>> {
        self.http_client
            .get_triggered_transactions(subscription_account, limit)
            .await
    }

    async fn get_workflow_lineage(
        &self,
        request: &GetWorkflowLineageRequest,
    ) -> crate::error::Result<GetWorkflowLineageResponse> {
        self.http_client.get_workflow_lineage(request).await
    }

    async fn request_airdrop(
        &self,
        pubkey: &PublicKey,
        amount: Kelvin,
    ) -> crate::error::Result<Signature> {
        self.http_client.request_airdrop(pubkey, amount).await
    }

    async fn get_epoch_info(&self) -> crate::error::Result<EpochInfo> {
        self.http_client.get_epoch_info().await
    }

    async fn get_fee_for_message(&self, message: &str) -> crate::error::Result<FeeResponse> {
        self.http_client.get_fee_for_message(message).await
    }

    async fn get_multiple_accounts(
        &self,
        pubkeys: &[PublicKey],
    ) -> crate::error::Result<Vec<OptionalAccountInfo>> {
        self.http_client.get_multiple_accounts(pubkeys).await
    }

    async fn get_signatures_for_address(
        &self,
        address: &PublicKey,
        config: Option<GetSignaturesForAddressConfig>,
    ) -> crate::error::Result<Vec<SignatureInfo>> {
        self.http_client
            .get_signatures_for_address(address, config)
            .await
    }

    async fn is_blockhash_valid(
        &self,
        blockhash: &Hash,
    ) -> crate::error::Result<IsBlockhashValidResponse> {
        self.http_client.is_blockhash_valid(blockhash).await
    }

    async fn get_health(&self) -> crate::error::Result<String> {
        self.http_client.get_health().await
    }

    async fn get_validator_health(&self) -> crate::error::Result<ValidatorHealth> {
        self.http_client.get_validator_health().await
    }

    async fn get_connected_full_nodes(&self) -> crate::error::Result<Vec<ConnectedNode>> {
        self.http_client.get_connected_full_nodes().await
    }

    async fn get_transactions(
        &self,
        config: Option<GetTransactionsConfig>,
    ) -> crate::error::Result<Vec<TransactionInfo>> {
        self.http_client.get_transactions(config).await
    }

    async fn get_accounts_by_owner(
        &self,
        owner: &PublicKey,
        filter: Option<crate::generated::types::AccountFilter>,
        config: Option<crate::generated::types::GetAccountsByOwnerConfig>,
    ) -> crate::error::Result<(Vec<OwnerAccount>, Option<PaginationInfo>)> {
        self.http_client
            .get_accounts_by_owner(owner, filter, config)
            .await
    }

    async fn get_config_hash_prefix(&self) -> crate::error::Result<ConfigHashPrefix> {
        self.http_client.get_config_hash_prefix().await
    }

    async fn confirm_transaction(
        &self,
        sig: &str,
        options: Option<ConfirmTransactionOptions>,
    ) -> crate::error::Result<ConfirmedTransaction> {
        self.http_client.confirm_transaction(sig, options).await
    }

    async fn send_and_confirm_transaction(
        &self,
        transaction: &[u8],
        options: Option<SendAndConfirmOptions>,
    ) -> crate::error::Result<ConfirmedTransaction> {
        self.http_client
            .send_and_confirm_transaction(transaction, options)
            .await
    }

    async fn request_airdrop_and_confirm(
        &self,
        pubkey: &PublicKey,
        amount: Kelvin,
    ) -> crate::error::Result<ConfirmedTransaction> {
        self.http_client
            .request_airdrop_and_confirm(pubkey, amount)
            .await
    }

    async fn get_all_accounts(
        &self,
        config: Option<GetAllAccountsConfig>,
    ) -> crate::error::Result<Vec<AllAccountsEntry>> {
        self.http_client.get_all_accounts(config).await
    }

    async fn get_block(
        &self,
        block_height: u64,
        config: Option<GetBlockConfig>,
    ) -> crate::error::Result<BlockInfo> {
        self.http_client.get_block(block_height, config).await
    }

    async fn get_blocks(
        &self,
        start_height: u64,
        end_height: Option<u64>,
    ) -> crate::error::Result<Vec<u64>> {
        self.http_client.get_blocks(start_height, end_height).await
    }

    async fn get_stake_account(
        &self,
        pubkey: &PublicKey,
    ) -> crate::error::Result<Option<StakeAccountInfo>> {
        self.http_client.get_stake_account(pubkey).await
    }

    async fn get_validator_accounts(
        &self,
        request: &GetValidatorAccountsRequest,
    ) -> crate::error::Result<Vec<ValidatorAccountInfo>> {
        self.http_client.get_validator_accounts(request).await
    }

    async fn get_token_account_balance(
        &self,
        pubkey: &PublicKey,
    ) -> crate::error::Result<TokenBalance> {
        self.http_client.get_token_account_balance(pubkey).await
    }

    async fn get_rex_requests(
        &self,
        creator: &PublicKey,
        nonce: Option<String>,
    ) -> crate::error::Result<Vec<RexInfoAndDuties>> {
        self.http_client.get_rex_requests(creator, nonce).await
    }

    async fn get_rex_missed_duties(
        &self,
        creator: &PublicKey,
        nonce: &str,
    ) -> crate::error::Result<Vec<u64>> {
        self.http_client.get_rex_missed_duties(creator, nonce).await
    }

    async fn get_cluster_nodes(&self) -> crate::error::Result<Vec<ClusterNodeInfo>> {
        self.http_client.get_cluster_nodes().await
    }

    async fn get_connected_validators(&self) -> crate::error::Result<Vec<u32>> {
        self.http_client.get_connected_validators().await
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_client_context_creation() {
        let url = "http://127.0.0.1:4104".to_string();
        let _context = ClientContext::new(url);
    }

    #[test]
    fn test_client_context_for_benchmarks() {
        let url = "http://127.0.0.1:4104".to_string();
        let _context = ClientContext::new_for_benchmarks(url);
    }

    #[tokio::test]
    async fn test_client_context_implements_rpc_client() {
        // This test just verifies that ClientContext implements RpcClient
        // We'll use a real context for testing
        let url = "http://127.0.0.1:4104".to_string();
        let context = ClientContext::new(url);
        let _ = context.get_block_height().await;
    }
}
