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

//! Request types for node RPC handlers
//!
//! These types provide strongly-typed deserialization for RPC request parameters
//! in node handlers, replacing manual parameter parsing.

use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::{
    messages::send_transaction::SendTransactionConfig,
    validation::{
        validate_airdrop_amount_i64, validate_base58, validate_pubkey, validate_signature,
        validate_transaction_data,
    },
    GetBlockRequest, GetSignatureStatusesRequest,
};

/// Request type for getSubscription RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetSubscriptionRequest {
    #[validate(length(min = 1, message = "Subscriber pubkey cannot be empty"))]
    #[validate(custom(function = validate_pubkey))]
    pub subscriber: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub nonce: Option<String>,
}

impl GetSubscriptionRequest {
    pub fn new(subscriber: String, nonce: Option<String>) -> Self {
        Self { subscriber, nonce }
    }
}

/// Request type for getTriggeredTransactions RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetTriggeredTransactionsRequest {
    #[validate(length(min = 1, message = "Subscription pubkey cannot be empty"))]
    #[validate(custom(function = validate_pubkey))]
    pub subscription_pubkey: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<String>,
}

impl GetTriggeredTransactionsRequest {
    pub fn new(subscription_pubkey: String, limit: Option<String>) -> Self {
        Self {
            subscription_pubkey,
            limit,
        }
    }
}

/// Request type for sendTransaction RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate, PartialEq)]
pub struct SendTransactionRequest {
    #[validate(length(min = 1, message = "Transaction cannot be empty"))]
    #[validate(custom(function = validate_transaction_data))]
    pub transaction: String,

    #[validate(nested)]
    pub config: crate::messages::send_transaction::SendTransactionConfig,
}

impl SendTransactionRequest {
    pub fn new(
        transaction: String,
        config: crate::messages::send_transaction::SendTransactionConfig,
    ) -> Self {
        Self {
            transaction,
            config,
        }
    }
}

/// Request type for getTransaction RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetTransactionRequest {
    #[validate(length(min = 1, message = "Signature cannot be empty"))]
    #[validate(custom(function = validate_signature))]
    pub signature: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub config: Option<serde_json::Value>,
}

impl GetTransactionRequest {
    pub fn new(signature: String, config: Option<serde_json::Value>) -> Self {
        Self { signature, config }
    }

    pub fn new_simple(signature: String) -> Self {
        Self {
            signature,
            config: Some(serde_json::json!({"encoding": "json"})),
        }
    }
}

/// Request type for isBlockhashValid RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct IsBlockhashValidRequest {
    #[validate(length(min = 1, message = "Blockhash cannot be empty"))]
    #[validate(custom(function = validate_base58))]
    pub blockhash: String,
}

impl IsBlockhashValidRequest {
    pub fn new(blockhash: String) -> Self {
        Self { blockhash }
    }

    pub fn new_simple(blockhash: String) -> Self {
        Self { blockhash }
    }
}

/// Request type for requestAirdrop RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct RequestAirdropRequest {
    /// Public key of the account to airdrop to, in base58 encoding
    #[validate(length(min = 1, message = "Pubkey cannot be empty"))]
    #[validate(custom(function = validate_pubkey))]
    pub pubkey: String,

    /// Amount of kelvins to airdrop
    #[validate(custom(function = validate_airdrop_amount_i64))]
    pub kelvins: i64,
}

impl RequestAirdropRequest {
    pub fn new(pubkey: String, kelvins: i64) -> Self {
        Self { pubkey, kelvins }
    }
}

/// Generic RPC request wrapper that can handle both array and object parameter formats
#[derive(Debug, Clone)]
pub enum RpcRequestParams<T> {
    /// Single structured object parameter  
    Object(T),
    /// Array of parameters (for backward compatibility)
    Array(Vec<serde_json::Value>),
}

impl<T> RpcRequestParams<T>
where
    T: for<'de> serde::Deserialize<'de>,
{
    /// Try to deserialize from a JSON value
    pub fn from_value(value: serde_json::Value) -> Result<Self, serde_json::Error> {
        if value.is_array() {
            let array = value.as_array().unwrap().clone();
            Ok(RpcRequestParams::Array(array))
        } else {
            let object = serde_json::from_value::<T>(value)?;
            Ok(RpcRequestParams::Object(object))
        }
    }

    /// Get the structured object, converting from array if necessary
    pub fn into_object(self) -> Result<T, Box<dyn std::error::Error + Send + Sync>> {
        match self {
            RpcRequestParams::Object(obj) => Ok(obj),
            RpcRequestParams::Array(_array) => {
                // For now, return an error - specific handlers can implement array conversion
                Err("Array format not supported for this request type".into())
            }
        }
    }
}

/// Helper trait for converting array parameters to structured objects
pub trait FromArrayParams: Sized {
    /// Convert from array of JSON values to structured type
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>;
}

impl FromArrayParams for GetSubscriptionRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing subscriber parameter".into());
        }

        let subscriber = params[0]
            .as_str()
            .ok_or("Subscriber must be a string")?
            .to_string();

        let nonce = if let Some(nonce_param) = params.get(1) {
            if nonce_param.is_null() {
                None
            } else if let Some(nonce_str) = nonce_param.as_str() {
                Some(nonce_str.to_string())
            } else {
                return Err("Nonce must be a string".into());
            }
        } else {
            None
        };

        Ok(GetSubscriptionRequest::new(subscriber, nonce))
    }
}

impl FromArrayParams for GetTriggeredTransactionsRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing subscription_pubkey parameter".into());
        }

        let subscription_pubkey = params[0]
            .as_str()
            .ok_or("Subscription pubkey is not a string")?
            .to_string();

        let limit = params
            .get(1)
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        Ok(GetTriggeredTransactionsRequest::new(
            subscription_pubkey,
            limit,
        ))
    }
}

impl FromArrayParams for SendTransactionRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.len() > 2 {
            return Err(format!(
                "Expected one or two request params, got {}",
                params.len()
            ))?;
        }
        let transaction = params
            .first()
            .ok_or_else(|| "Missing the transaction parameter".to_string())?
            .as_str()
            .ok_or("Transaction must be a string")?
            .to_string();

        let config = match params.get(1) {
            Some(value) => serde_json::from_value::<SendTransactionConfig>(value.clone())
                .map_err(|e| format!("Failed to parse config: {e}"))?,
            None => SendTransactionConfig::default(),
        };
        Ok(SendTransactionRequest::new(transaction, config))
    }
}

impl FromArrayParams for GetTransactionRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing signature parameter".into());
        }

        let signature = params[0]
            .as_str()
            .ok_or("Signature must be a string")?
            .to_string();

        let config = if params.len() > 1 {
            Some(params[1].clone())
        } else {
            None
        };

        Ok(GetTransactionRequest::new(signature, config))
    }
}

impl FromArrayParams for IsBlockhashValidRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing blockhash parameter".into());
        }

        let blockhash = params[0]
            .as_str()
            .ok_or("Blockhash must be a string")?
            .to_string();

        Ok(IsBlockhashValidRequest::new(blockhash))
    }
}

impl FromArrayParams for RequestAirdropRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing pubkey parameter".into());
        }

        if params.len() < 2 {
            return Err("Missing kelvins parameter".into());
        }

        let pubkey = params[0]
            .as_str()
            .ok_or("Pubkey must be a string")?
            .to_string();

        let kelvins = params[1].as_i64().ok_or("Kelvins must be a number")?;

        Ok(RequestAirdropRequest::new(pubkey, kelvins))
    }
}

impl FromArrayParams for GetBlockRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing the block height parameter".into());
        }
        if params.len() > 2 {
            return Err("Too many parameters: expected either one or two".into());
        }

        if params.len() == 1 && params[0].is_object() {
            return Ok(serde_json::from_value(params[0].clone())?);
        }

        let block_height = params[0]
            .as_u64()
            .ok_or("The first parameter must be the block height")?;

        let config = match params.get(1) {
            Some(value) => Some(
                serde_json::from_value(value.clone())
                    .map_err(|e| format!("Failed to parse the config parameter: {e}"))?,
            ),
            None => None,
        };

        Ok(GetBlockRequest {
            block_height,
            config,
        })
    }
}

impl FromArrayParams for GetSignatureStatusesRequest {
    fn from_array_params(
        params: &[serde_json::Value],
    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        if params.is_empty() {
            return Err("Missing the signatures parameter".into());
        }
        if params.len() > 2 {
            return Err("Too many parameters: expected either one or two".into());
        }

        if params.len() == 1 && params[0].is_object() {
            return Ok(serde_json::from_value(params[0].clone())?);
        }

        let signatures: Vec<String> = serde_json::from_value(params[0].clone())
            .map_err(|e| format!("Failed to parse the signatures parameter: {e}"))?;

        let config = match params.get(1) {
            Some(value) => Some(
                serde_json::from_value(value.clone())
                    .map_err(|e| format!("Failed to parse the config parameter: {e}"))?,
            ),
            None => None,
        };

        Ok(GetSignatureStatusesRequest { signatures, config })
    }
}

#[cfg(test)]
mod tests {
    use serde_json::{from_value, json, to_value};

    use super::*;
    use crate::messages::send_transaction::{SendTransactionConfig, TransactionEncoding};

    #[test]
    fn test_get_subscription_request_serialization() {
        let request = GetSubscriptionRequest::new(
            "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri".to_string(),
            Some("test_nonce".to_string()),
        );

        let json = to_value(&request).unwrap();
        assert_eq!(
            json,
            json!({
                "subscriber": "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri",
                "nonce": "test_nonce"
            })
        );

        let deserialized: GetSubscriptionRequest = from_value(json).unwrap();
        assert_eq!(deserialized.subscriber, request.subscriber);
        assert_eq!(deserialized.nonce, request.nonce);
    }

    #[test]
    fn test_get_triggered_transactions_request_serialization() {
        let request = GetTriggeredTransactionsRequest::new(
            "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri".to_string(),
            Some("10".to_string()),
        );

        let json = to_value(&request).unwrap();
        assert_eq!(
            json,
            json!({
                "subscription_pubkey": "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri",
                "limit": "10"
            })
        );

        let deserialized: GetTriggeredTransactionsRequest = from_value(json).unwrap();
        assert_eq!(
            deserialized.subscription_pubkey,
            request.subscription_pubkey
        );
        assert_eq!(deserialized.limit, request.limit);
    }

    #[test]
    fn test_get_transaction_request_serialization() {
        let request = GetTransactionRequest::new_simple("signature123".to_string());

        let json = to_value(&request).unwrap();
        assert_eq!(
            json,
            json!({
                "signature": "signature123",
                "config": {"encoding": "json"}
            })
        );

        let deserialized: GetTransactionRequest = from_value(json).unwrap();
        assert_eq!(deserialized.signature, request.signature);
    }

    #[test]
    fn test_from_array_params_get_subscription() {
        let params = vec![
            json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
            json!("test_nonce"),
        ];

        let request = GetSubscriptionRequest::from_array_params(&params).unwrap();
        assert_eq!(
            request.subscriber,
            "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"
        );
        assert_eq!(request.nonce, Some("test_nonce".to_string()));
    }

    #[test]
    fn test_from_array_params_get_subscription_invalid_nonce() {
        let params = vec![
            json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
            json!(123), // Invalid nonce - should be a string, not a number
        ];

        let result = GetSubscriptionRequest::from_array_params(&params);
        assert!(result.is_err());
        assert_eq!(result.unwrap_err().to_string(), "Nonce must be a string");
    }

    #[test]
    fn test_from_array_params_get_triggered_transactions() {
        let params = vec![
            json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
            json!("5"),
        ];

        let request = GetTriggeredTransactionsRequest::from_array_params(&params).unwrap();
        assert_eq!(
            request.subscription_pubkey,
            "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"
        );
        assert_eq!(request.limit, Some("5".to_string()));
    }

    #[test]
    fn test_from_array_params_get_transaction() {
        let params = vec![json!("signature123"), json!({"encoding": "json"})];

        let request = GetTransactionRequest::from_array_params(&params).unwrap();
        assert_eq!(request.signature, "signature123");
        assert!(request.config.is_some());
    }

    #[test]
    fn test_from_array_params_is_blockhash_valid() {
        let params = vec![json!("blockhash123")];

        let request = IsBlockhashValidRequest::from_array_params(&params).unwrap();
        assert_eq!(request.blockhash, "blockhash123");
    }

    #[test]
    fn test_from_array_params_request_airdrop() {
        let params = vec![
            json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"),
            json!(1000000),
        ];

        let request = RequestAirdropRequest::from_array_params(&params).unwrap();
        assert_eq!(
            request.pubkey,
            "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"
        );
        assert_eq!(request.kelvins, 1000000);
    }

    #[test]
    fn test_from_array_params_request_airdrop_missing_params() {
        let params = vec![json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri")];

        let result = RequestAirdropRequest::from_array_params(&params);
        assert!(result.is_err());
        assert_eq!(result.unwrap_err().to_string(), "Missing kelvins parameter");
    }

    #[test]
    fn test_from_array_params_request_airdrop_invalid_type() {
        let params = vec![
            json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"),
            json!("thousand"), // String instead of number
        ];

        let result = RequestAirdropRequest::from_array_params(&params);
        assert!(result.is_err());
        assert_eq!(result.unwrap_err().to_string(), "Kelvins must be a number");
    }

    #[test]
    fn test_send_transaction_from_array_missing_optional_config() {
        let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
        let params = vec![json!(encoded_tx)];
        assert_eq!(
            SendTransactionRequest::from_array_params(&params)
                .expect("failed to parse a transaction from params"),
            SendTransactionRequest {
                transaction: encoded_tx.to_string(),
                config: SendTransactionConfig::default()
            }
        );
    }

    #[test]
    fn test_send_transaction_from_array_default_config() {
        let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
        let params = vec![json!(encoded_tx), json!({})];
        assert_eq!(
            SendTransactionRequest::from_array_params(&params)
                .expect("failed to parse a transaction from params"),
            SendTransactionRequest {
                transaction: encoded_tx.to_string(),
                config: SendTransactionConfig::default(),
            }
        );
    }

    #[test]
    fn test_send_transaction_from_array_config() {
        let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
        let params = vec![
            json!(encoded_tx),
            json!({
                "encoding": "base58",
            }),
        ];
        assert_eq!(
            SendTransactionRequest::from_array_params(&params)
                .expect("failed to parse a transaction from params"),
            SendTransactionRequest {
                transaction: encoded_tx.to_string(),
                config: SendTransactionConfig::new(TransactionEncoding::Base58),
            }
        );
    }

    #[test]
    fn test_send_transaction_from_array_too_many_params() {
        let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
        let params = vec![json!(encoded_tx), json!({}), json!({})];
        SendTransactionRequest::from_array_params(&params).unwrap_err();
    }

    #[test]
    fn test_from_array_params_get_block_basic() {
        let params = vec![json!(123456)];

        let request = GetBlockRequest::from_array_params(&params).unwrap();
        assert_eq!(request.block_height, 123456);
        assert!(request.config.is_none());
    }

    #[test]
    fn test_from_array_params_get_block_with_config() {
        let params = vec![
            json!(123456),
            json!({
                "transactionDetails": "full"
            }),
        ];

        let request = GetBlockRequest::from_array_params(&params).unwrap();
        assert_eq!(request.block_height, 123456);
        assert!(request.config.is_some());
    }

    #[test]
    fn test_from_array_params_get_block_single_object() {
        let params = vec![json!({
            "blockHeight": 789012,
            "config": {
                "transactionDetails": "signatures"
            }
        })];

        let request = GetBlockRequest::from_array_params(&params).unwrap();
        assert_eq!(request.block_height, 789012);
        assert!(request.config.is_some());
    }

    #[test]
    #[should_panic(expected = "block height")]
    fn test_from_array_params_get_block_empty_params() {
        GetBlockRequest::from_array_params(&[]).unwrap();
    }

    #[test]
    #[should_panic(expected = "Too many parameters")]
    fn test_from_array_params_get_block_too_many_params() {
        GetBlockRequest::from_array_params(&[json!(123456), json!({}), json!({})]).unwrap();
    }

    #[test]
    #[should_panic(expected = "block height")]
    fn test_from_array_params_get_block_invalid_height_type() {
        GetBlockRequest::from_array_params(&[json!("not_a_number")]).unwrap();
    }

    #[test]
    #[should_panic(expected = "config parameter")]
    fn test_from_array_params_get_block_invalid_config() {
        GetBlockRequest::from_array_params(&[
            json!(123456),
            json!("invalid_config"), // Should be an object, not a string
        ])
        .unwrap();
    }

    #[test]
    #[should_panic(expected = "config parameter")]
    fn test_from_array_params_get_block_null_config() {
        GetBlockRequest::from_array_params(&[json!(123456), json!(null)]).unwrap();
    }

    #[test]
    fn test_from_array_params_get_signature_statuses_basic() {
        let signatures = vec![
            "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
            "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaB"
        ];
        let params = vec![json!(signatures)];

        let request = GetSignatureStatusesRequest::from_array_params(&params).unwrap();
        assert_eq!(request.signatures, signatures);
        assert!(request.config.is_none());
    }

    #[test]
    fn test_from_array_params_get_signature_statuses_with_config() {
        let signatures = vec![
            "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
        ];
        let config = json!({"searchTransactionHistory": true});
        let params = vec![json!(signatures), config.clone()];

        let request = GetSignatureStatusesRequest::from_array_params(&params).unwrap();
        assert_eq!(request.signatures, signatures);
        assert!(request.config.is_some());
    }

    #[test]
    fn test_from_array_params_get_signature_statuses_single_object() {
        let signatures = vec![
            "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
        ];
        let params = vec![json!({
            "signatures": signatures,
            "config": {"searchTransactionHistory": false}
        })];

        let request = GetSignatureStatusesRequest::from_array_params(&params).unwrap();
        assert_eq!(request.signatures, signatures);
        assert!(request.config.is_some());
    }

    #[test]
    fn test_from_array_params_get_signature_statuses_empty_signatures() {
        let request = GetSignatureStatusesRequest::from_array_params(&[json!([])]).unwrap();
        assert!(request.signatures.is_empty());
        assert!(request.config.is_none());
    }

    #[test]
    #[should_panic(expected = "Missing the signatures parameter")]
    fn test_from_array_params_get_signature_statuses_empty_params() {
        GetSignatureStatusesRequest::from_array_params(&[]).unwrap();
    }

    #[test]
    #[should_panic(expected = "Too many parameters")]
    fn test_from_array_params_get_signature_statuses_too_many_params() {
        GetSignatureStatusesRequest::from_array_params(&[json!(["sig1"]), json!({}), json!({})])
            .unwrap();
    }

    #[test]
    #[should_panic(expected = "Failed to parse the signatures parameter")]
    fn test_from_array_params_get_signature_statuses_invalid_signatures_type() {
        GetSignatureStatusesRequest::from_array_params(&[json!("not an array")]).unwrap();
    }

    #[test]
    #[should_panic(expected = "Failed to parse the config parameter")]
    fn test_from_array_params_get_signature_statuses_invalid_config() {
        GetSignatureStatusesRequest::from_array_params(&[
            json!(["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]),
            json!("invalid config")
        ]).unwrap();
    }

    #[test]
    fn test_from_array_params_get_signature_statuses_multiple_signatures() {
        let signatures: Vec<&str> = (0..100).map(|i| {
            if i % 2 == 0 {
                "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
            } else {
                "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaB"
            }
        }).collect();
        let params = vec![json!(signatures)];

        let request = GetSignatureStatusesRequest::from_array_params(&params).unwrap();
        assert_eq!(request.signatures.len(), 100);
        assert!(request.config.is_none());
    }

    #[test]
    #[should_panic(expected = "Failed to parse the config parameter")]
    fn test_from_array_params_get_signature_statuses_null_config() {
        GetSignatureStatusesRequest::from_array_params(&[
            json!(["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]),
            json!(null)]
        ).unwrap();
    }
}
