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

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use rialo_cdk::generated::{rpc_client::RpcClient, types::*};

    use crate::mocks::MockRpcClient;

    const TEST_BASE58_STRING: &str = "EkSnNWid2cvwEVnVx9aBqawnmiCNiDgp3gUdkDJTKrRd";

    #[tokio::test]
    async fn test_get_balance() {
        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getBalance".to_string(),
            serde_json::json!({
                "context": {"slot": 100},
                "value": 1000
            }),
        );

        let client = MockRpcClient { responses };
        let balance = client
            .get_balance(&PublicKey::from_str(TEST_BASE58_STRING).unwrap())
            .await
            .unwrap();

        assert_eq!(balance, 1000);
    }

    #[tokio::test]
    async fn test_get_block_height() {
        let mut responses = std::collections::HashMap::new();
        responses.insert("getBlockHeight".to_string(), serde_json::json!(12345));

        let client = MockRpcClient { responses };
        let block_height = client.get_block_height().await.unwrap();

        assert_eq!(block_height, 12345);
    }

    #[tokio::test]
    async fn test_rpc_error_handling() {
        use rialo_cdk::error::{RialoError, RpcErrorDetails};

        // Test with empty responses (simulates network error or missing endpoint)
        let client = MockRpcClient {
            responses: std::collections::HashMap::new(),
        };

        // Test getBalance with missing response should return error
        let result = client
            .get_balance(&PublicKey::from_str(TEST_BASE58_STRING).unwrap())
            .await;

        assert!(result.is_err());

        // Verify it's an RPC error
        match result.unwrap_err() {
            RialoError::Rpc(rpc_error) => {
                // Should contain meaningful error message
                assert!(!rpc_error.message.is_empty());
            }
            other => panic!("Expected RPC error, got: {other:?}"),
        }

        // Test RpcErrorDetails formatting
        // Display only shows the message for clean output
        let error_details =
            RpcErrorDetails::new(Some(422), -32002, "Transaction is too large".to_string());
        let error_string = format!("{error_details}");

        assert_eq!(error_string, "Transaction is too large");
        // Verify structured details are accessible
        assert_eq!(error_details.status, Some(422));
        assert_eq!(error_details.code, -32002);

        // Test RialoError::Rpc formatting
        let rpc_error = RialoError::Rpc(error_details);
        let rpc_error_string = format!("{rpc_error}");

        assert!(rpc_error_string.starts_with("RPC error:"));
        assert!(rpc_error_string.contains("Transaction is too large"));
    }

    #[tokio::test]
    async fn test_get_subscription_success() {
        let subscriber = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        // Create test subscription data using generated types
        let test_subscription = Subscription {
            kind: SubscriptionKind::Persistent,
            topic: "test_topic".to_string(),
            instructions: vec![SubscriptionInstruction {
                program_id: "11111111111111111111111111111111".to_string(),
                accounts: vec![SubscriptionAccountMeta {
                    pubkey: TEST_BASE58_STRING.to_string(),
                    is_signer: true,
                    is_writable: false,
                }],
                data: vec![1, 2, 3],
            }],
            subscriber: TEST_BASE58_STRING.to_string(),
            event_account: Some("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string()),
            timestamp_range: Some((1640995200, 1640995300)), // Jan 1, 2022 timestamps
        };

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getSubscription".to_string(),
            serde_json::to_value(&test_subscription).unwrap(),
        );

        let client = MockRpcClient { responses };
        // Trait returns Subscription directly (no wrapper)
        let subscription = client
            .get_subscription(&subscriber, "test_nonce")
            .await
            .unwrap();

        assert!(matches!(subscription.kind, SubscriptionKind::Persistent));
        assert_eq!(subscription.topic, "test_topic");
        assert_eq!(subscription.subscriber, TEST_BASE58_STRING);
        assert_eq!(subscription.instructions.len(), 1);

        let instruction = &subscription.instructions[0];
        assert_eq!(instruction.program_id, "11111111111111111111111111111111");
        assert_eq!(instruction.accounts.len(), 1);
        assert_eq!(instruction.data, vec![1, 2, 3]);

        let account_meta = &instruction.accounts[0];
        assert_eq!(account_meta.pubkey, TEST_BASE58_STRING);
        assert!(account_meta.is_signer);
        assert!(!account_meta.is_writable);

        assert_eq!(
            subscription.event_account,
            Some("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string())
        );
        assert_eq!(subscription.timestamp_range, Some((1640995200, 1640995300)));
    }

    #[tokio::test]
    async fn test_get_subscription_rpc_error() {
        let subscriber = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        // Mock client with no response for getSubscription
        let client = MockRpcClient {
            responses: std::collections::HashMap::new(),
        };

        let result = client.get_subscription(&subscriber, "test_nonce").await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
    }

    #[tokio::test]
    async fn test_get_subscription_invalid_response_format() {
        let subscriber = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        let mut responses = std::collections::HashMap::new();
        // Invalid response format (missing subscription field)
        responses.insert(
            "getSubscription".to_string(),
            serde_json::json!({
                "context": { "slot": 12345 }
                // Missing "subscription" field
            }),
        );

        let client = MockRpcClient { responses };
        let result = client.get_subscription(&subscriber, "test_nonce").await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
    }

    #[tokio::test]
    async fn test_get_triggered_transactions_success() {
        let subscription_account = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        // Trait returns Vec<TriggeredTransaction> directly (no wrapper)
        let test_data = vec![
            TriggeredTransaction {
                signature: "2uaJgKrzq7b4d7ZkNGH5HCHh3VECfxfJ4LYGWKtgr8YuXv6JcKzQ3rZ7kP3hHm2L"
                    .to_string(),
                block_number: 12345,
            },
            TriggeredTransaction {
                signature: "4vbLhLqzr9c6f9ZmPIH7KDKh5XFDfygJ6MZHYLugr8ZvYw8KdMzR5sZ9mQ5jJn4M"
                    .to_string(),
                block_number: 12340,
            },
        ];

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getTriggeredTransactions".to_string(),
            serde_json::to_value(&test_data).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client
            .get_triggered_transactions(&subscription_account, None)
            .await
            .unwrap();

        assert_eq!(response.len(), 2);

        let first_tx = &response[0];
        assert_eq!(
            first_tx.signature,
            "2uaJgKrzq7b4d7ZkNGH5HCHh3VECfxfJ4LYGWKtgr8YuXv6JcKzQ3rZ7kP3hHm2L"
        );
        assert_eq!(first_tx.block_number, 12345);

        let second_tx = &response[1];
        assert_eq!(
            second_tx.signature,
            "4vbLhLqzr9c6f9ZmPIH7KDKh5XFDfygJ6MZHYLugr8ZvYw8KdMzR5sZ9mQ5jJn4M"
        );
        assert_eq!(second_tx.block_number, 12340);
    }

    #[tokio::test]
    async fn test_get_triggered_transactions_with_limit() {
        let subscription_account = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        let test_data = vec![TriggeredTransaction {
            signature: "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWGtKqjdu9Jhegoq4QpiqBmQJJuSycJoBmWN7LSpiLTs2SbsqBKu9kNgQoPx1".to_string(),
            block_number: 98765,
        }];

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getTriggeredTransactions".to_string(),
            serde_json::to_value(&test_data).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client
            .get_triggered_transactions(&subscription_account, Some(5))
            .await
            .unwrap();

        assert_eq!(response.len(), 1);
        assert_eq!(response[0].signature, "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWGtKqjdu9Jhegoq4QpiqBmQJJuSycJoBmWN7LSpiLTs2SbsqBKu9kNgQoPx1");
        assert_eq!(response[0].block_number, 98765);
    }

    #[tokio::test]
    async fn test_get_triggered_transactions_empty_response() {
        let subscription_account = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        let test_data: Vec<TriggeredTransaction> = vec![];

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getTriggeredTransactions".to_string(),
            serde_json::to_value(&test_data).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client
            .get_triggered_transactions(&subscription_account, None)
            .await
            .unwrap();

        assert_eq!(response.len(), 0);
    }

    #[tokio::test]
    async fn test_get_triggered_transactions_rpc_error() {
        let subscription_account = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        // Mock client with no response for getTriggeredTransactions
        let client = MockRpcClient {
            responses: std::collections::HashMap::new(),
        };

        let result = client
            .get_triggered_transactions(&subscription_account, None)
            .await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
    }

    #[tokio::test]
    async fn test_get_triggered_transactions_invalid_response_format() {
        let subscription_account = PublicKey::from_str(TEST_BASE58_STRING).unwrap();

        let mut responses = std::collections::HashMap::new();
        // Invalid response format (not a valid GetTriggeredTransactionsResponse)
        responses.insert(
            "getTriggeredTransactions".to_string(),
            serde_json::json!({
                "invalid_field": "invalid_data"
            }),
        );

        let client = MockRpcClient { responses };
        let result = client
            .get_triggered_transactions(&subscription_account, None)
            .await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
        // Check that the error message contains parsing failure info
        if let rialo_cdk::error::RialoError::Rpc(details) = error {
            assert!(details
                .message
                .contains("Failed to parse triggered transactions"));
        }
    }

    #[tokio::test]
    async fn test_get_triggered_transactions_serialization() {
        // Test that our TriggeredTransaction struct serializes correctly with blockNumber
        let tx = TriggeredTransaction {
            signature: "test_signature".to_string(),
            block_number: 42,
        };

        let json_value = serde_json::to_value(&tx).unwrap();
        let expected = serde_json::json!({
            "signature": "test_signature",
            "blockNumber": 42
        });

        assert_eq!(json_value, expected);

        // Test deserialization
        let deserialized: TriggeredTransaction = serde_json::from_value(expected).unwrap();
        assert_eq!(deserialized.signature, "test_signature");
        assert_eq!(deserialized.block_number, 42);
    }

    #[tokio::test]
    async fn test_get_workflow_lineage_simple_chain() {
        let signature = "sig1111111111111111111111111111111111111111111111111111111111111111";

        // Create test workflow lineage data (simple chain: Root -> Child -> Leaf)
        // Generated GetWorkflowLineageResponse wraps nodes in a lineage field
        let test_response = GetWorkflowLineageResponse {
            lineage: WorkflowLineage {
                workflow_nodes: vec![
                    WorkflowNode {
                        id: signature.to_string(),
                        depth: 0,
                        data: TransactionNodeData {
                            block_height: 100,
                            timestamp: 1640995200,
                            success: true,
                            instruction_count: 1,
                            instruction_program_ids: vec![
                                "11111111111111111111111111111111".to_string()
                            ],
                        },
                        subscriptions: vec![Subscription {
                        kind: SubscriptionKind::Persistent,
                        topic: "test.event".to_string(),
                        subscriber:
                            "user111111111111111111111111111111111111111111111111111111111111111"
                                .to_string(),
                        event_account: None,
                        timestamp_range: None,
                        instructions: vec![],
                    }],
                        triggered_by: None,
                        workflow_children: vec![
                            "sig2222222222222222222222222222222222222222222222222222222222222222"
                                .to_string(),
                        ],
                        has_more_children: false,
                    },
                    WorkflowNode {
                        id: "sig2222222222222222222222222222222222222222222222222222222222222222"
                            .to_string(),
                        depth: 1,
                        data: TransactionNodeData {
                            block_height: 101,
                            timestamp: 1640995260,
                            success: true,
                            instruction_count: 2,
                            instruction_program_ids: vec![
                                "22222222222222222222222222222222".to_string()
                            ],
                        },
                        subscriptions: vec![],
                        triggered_by: Some(TriggerInfo {
                            event: EventData {
                                topic: "test.event".to_string(),
                            },
                            subscription_pubkey:
                                "sub111111111111111111111111111111111111111111111111111111111111111"
                                    .to_string(),
                            timestamp_range: None,
                            event_account: None,
                        }),
                        workflow_children: vec![],
                        has_more_children: false,
                    },
                ],
            },
            leaves: vec![
                "sig2222222222222222222222222222222222222222222222222222222222222222".to_string(),
            ],
            truncated: false,
            truncation_reason: TruncationReason::None,
            continuation_hints: vec![],
        };

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getWorkflowLineage".to_string(),
            serde_json::to_value(&test_response).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client
            .get_workflow_lineage(&GetWorkflowLineageRequest {
                signature: signature.to_string(),
                max_depth: None,
                include_events: None,
            })
            .await
            .unwrap();

        assert_eq!(response.lineage.workflow_nodes.len(), 2);
        assert_eq!(response.leaves.len(), 1);
        assert!(!response.truncated);
        assert_eq!(response.truncation_reason, TruncationReason::None);

        // Verify root node
        let root = &response.lineage.workflow_nodes[0];
        assert_eq!(root.id, signature);
        assert_eq!(root.depth, 0);
        assert!(root.data.success);
        assert_eq!(root.subscriptions.len(), 1);
        assert_eq!(root.subscriptions[0].topic, "test.event");
        assert!(root.triggered_by.is_none()); // Root has no trigger

        // Verify child node
        let child = &response.lineage.workflow_nodes[1];
        assert_eq!(child.depth, 1);
        assert!(child.triggered_by.is_some());
        if let Some(trigger) = &child.triggered_by {
            assert_eq!(trigger.event.topic, "test.event");
        }
    }

    #[tokio::test]
    async fn test_get_workflow_lineage_complex_branching() {
        let signature = "root1111111111111111111111111111111111111111111111111111111111111111";

        // Create complex branching tree with multiple levels and branches
        let test_response = GetWorkflowLineageResponse {
            lineage: WorkflowLineage {
                workflow_nodes: vec![
                    WorkflowNode {
                        id: signature.to_string(),
                        depth: 0,
                        data: TransactionNodeData {
                            block_height: 100,
                            timestamp: 1640995200,
                            success: true,
                            instruction_count: 3,
                            instruction_program_ids: vec!["11111111111111111111111111111111".to_string()],
                        },
                        subscriptions: vec![
                            Subscription {
                                kind: SubscriptionKind::Persistent,
                                topic: "branching.event".to_string(),
                                subscriber: "userA11111111111111111111111111111111111111111111111111111111111111".to_string(),
                                event_account: Some("eventAcc111111111111111111111111111111111111111111111111111111111111".to_string()),
                                timestamp_range: Some((1640995000, 1640999000)),
                                instructions: vec![],
                            },
                            Subscription {
                                kind: SubscriptionKind::OneShot,
                                topic: "branching.event".to_string(),
                                subscriber: "userB11111111111111111111111111111111111111111111111111111111111111".to_string(),
                                event_account: None,
                                timestamp_range: None,
                                instructions: vec![],
                            },
                        ],
                        triggered_by: None,
                        workflow_children: vec![
                            "child111111111111111111111111111111111111111111111111111111111111111".to_string(),
                            "child222222222222222222222222222222222222222222222222222222222222222".to_string(),
                        ],
                        has_more_children: false,
                    },
                    WorkflowNode {
                        id: "child111111111111111111111111111111111111111111111111111111111111111".to_string(),
                        depth: 1,
                        data: TransactionNodeData {
                            block_height: 101,
                            timestamp: 1640995260,
                            success: true,
                            instruction_count: 2,
                            instruction_program_ids: vec!["22222222222222222222222222222222".to_string()],
                        },
                        subscriptions: vec![],
                        triggered_by: Some(TriggerInfo {
                            event: EventData {
                                topic: "branching.event".to_string(),
                            },
                            subscription_pubkey: "subA111111111111111111111111111111111111111111111111111111111111111".to_string(),
                            timestamp_range: None,
                            event_account: Some("eventAcc111111111111111111111111111111111111111111111111111111111111".to_string()),
                        }),
                        workflow_children: vec![],
                        has_more_children: false,
                    },
                    WorkflowNode {
                        id: "child222222222222222222222222222222222222222222222222222222222222222".to_string(),
                        depth: 1,
                        data: TransactionNodeData {
                            block_height: 101,
                            timestamp: 1640995280,
                            success: false, // This one failed
                            instruction_count: 1,
                            instruction_program_ids: vec!["33333333333333333333333333333333".to_string()],
                        },
                        subscriptions: vec![],
                        triggered_by: Some(TriggerInfo {
                            event: EventData {
                                topic: "branching.event".to_string(),
                            },
                            subscription_pubkey: "subB111111111111111111111111111111111111111111111111111111111111111".to_string(),
                            timestamp_range: None,
                            event_account: None,
                        }),
                        workflow_children: vec![],
                        has_more_children: true, // This node has more unexplored children
                    },
                ],
            },
            leaves: vec![
                "child111111111111111111111111111111111111111111111111111111111111111".to_string(),
                "child222222222222222222222222222222222222222222222222222222222222222".to_string(),
            ],
            truncated: true,
            truncation_reason: TruncationReason::MaxDepth,
            continuation_hints: vec!["child222222222222222222222222222222222222222222222222222222222222222".to_string()],
        };

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getWorkflowLineage".to_string(),
            serde_json::to_value(&test_response).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client
            .get_workflow_lineage(&GetWorkflowLineageRequest {
                signature: signature.to_string(),
                max_depth: Some(10),
                include_events: Some(true),
            })
            .await
            .unwrap();

        assert_eq!(response.lineage.workflow_nodes.len(), 3);
        assert_eq!(response.leaves.len(), 2);
        assert!(response.truncated);
        assert_eq!(response.truncation_reason, TruncationReason::MaxDepth);
        assert_eq!(response.continuation_hints.len(), 1);

        // Verify root has multiple subscriptions and children
        let root = &response.lineage.workflow_nodes[0];
        assert_eq!(root.subscriptions.len(), 2);
        assert_eq!(root.workflow_children.len(), 2);

        // Verify subscription with timestamp range
        let sub_with_range = &root.subscriptions[0];
        assert!(sub_with_range.timestamp_range.is_some());
        assert!(sub_with_range.event_account.is_some());

        // Verify failed transaction
        let failed_node = response
            .lineage
            .workflow_nodes
            .iter()
            .find(|n| !n.data.success)
            .unwrap();
        assert_eq!(
            failed_node.id,
            "child222222222222222222222222222222222222222222222222222222222222222"
        );
        assert!(failed_node.has_more_children);
    }

    #[tokio::test]
    async fn test_get_workflow_lineage_empty_response() {
        let signature = "empty111111111111111111111111111111111111111111111111111111111111111";

        let test_response = GetWorkflowLineageResponse {
            lineage: WorkflowLineage {
                workflow_nodes: vec![],
            },
            leaves: vec![],
            truncated: false,
            truncation_reason: TruncationReason::None,
            continuation_hints: vec![],
        };

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getWorkflowLineage".to_string(),
            serde_json::to_value(&test_response).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client
            .get_workflow_lineage(&GetWorkflowLineageRequest {
                signature: signature.to_string(),
                max_depth: None,
                include_events: None,
            })
            .await
            .unwrap();

        assert_eq!(response.lineage.workflow_nodes.len(), 0);
        assert_eq!(response.leaves.len(), 0);
        assert!(!response.truncated);
    }

    #[tokio::test]
    async fn test_get_workflow_lineage_rpc_error() {
        let signature = "error111111111111111111111111111111111111111111111111111111111111111";

        // Mock client with no response for getWorkflowLineage
        let client = MockRpcClient {
            responses: std::collections::HashMap::new(),
        };

        let result = client
            .get_workflow_lineage(&GetWorkflowLineageRequest {
                signature: signature.to_string(),
                max_depth: None,
                include_events: None,
            })
            .await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
    }

    #[tokio::test]
    async fn test_get_workflow_lineage_invalid_response_format() {
        let signature = "invalid1111111111111111111111111111111111111111111111111111111111111111";

        let mut responses = std::collections::HashMap::new();
        // Invalid response format (not a valid GetWorkflowLineageResponse)
        responses.insert(
            "getWorkflowLineage".to_string(),
            serde_json::json!({
                "invalid_field": "invalid_data"
            }),
        );

        let client = MockRpcClient { responses };
        let result = client
            .get_workflow_lineage(&GetWorkflowLineageRequest {
                signature: signature.to_string(),
                max_depth: None,
                include_events: None,
            })
            .await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
        // Check that the error message contains parsing failure info
        if let rialo_cdk::error::RialoError::Rpc(msg) = error {
            assert!(msg.message.contains("Failed to parse workflow lineage"));
        }
    }

    #[tokio::test]
    async fn test_get_workflow_lineage_serialization() {
        // Test that our workflow structures serialize correctly with proper field names
        let workflow_node = WorkflowNode {
            id: "test_signature".to_string(),
            depth: 1,
            data: TransactionNodeData {
                block_height: 100,
                timestamp: 1640995200,
                success: true,
                instruction_count: 2,
                instruction_program_ids: vec!["11111111111111111111111111111111".to_string()],
            },
            subscriptions: vec![],
            triggered_by: Some(TriggerInfo {
                event: EventData {
                    topic: "test.topic".to_string(),
                },
                subscription_pubkey: "test_subscriber".to_string(),
                timestamp_range: None,
                event_account: Some("test_event_account".to_string()),
            }),
            workflow_children: vec!["child1".to_string(), "child2".to_string()],
            has_more_children: true,
        };

        let json_value = serde_json::to_value(&workflow_node).unwrap();

        // Verify field name serialization (generated types use camelCase serde rename)
        assert_eq!(json_value["data"]["blockHeight"], 100);
        assert_eq!(json_value["data"]["instructionCount"], 2);
        assert_eq!(
            json_value["triggeredBy"]["eventAccount"],
            "test_event_account"
        );
        assert_eq!(json_value["workflowChildren"][0], "child1");
        assert_eq!(json_value["hasMoreChildren"], true);

        // Test deserialization
        let deserialized: WorkflowNode = serde_json::from_value(json_value).unwrap();
        assert_eq!(deserialized.id, "test_signature");
        assert_eq!(deserialized.depth, 1);
        assert_eq!(deserialized.data.block_height, 100);
        assert!(deserialized.has_more_children);
    }

    #[tokio::test]
    async fn test_get_transactions_default_config() {
        // Trait returns Vec<TransactionInfo> (generated type) directly
        let test_data: Vec<TransactionInfo> = vec![];

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getTransactions".to_string(),
            serde_json::to_value(&test_data).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client.get_transactions(None).await.unwrap();

        assert_eq!(response.len(), 0);
    }

    #[tokio::test]
    async fn test_get_transactions_with_transactions() {
        // Trait returns Vec<TransactionInfo> (generated type) directly
        let test_data: Vec<TransactionInfo> = vec![
            TransactionInfo {
                signature: "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW".to_string(),
                slot: 12345,
                block_time: Some(1640995200),
                err: None,
                version: None,
            },
            TransactionInfo {
                signature: "3Bxs9A1vAVi8n1hQrkVK7pn94BAmFWPKKDtkK5WwRXkacPZKqVm6gmnG6WtQReajvQZ9bdXLvEEsVJgS9zqjUBxG".to_string(),
                slot: 12346,
                block_time: Some(1640995260),
                err: None,
                version: None,
            },
        ];

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getTransactions".to_string(),
            serde_json::to_value(&test_data).unwrap(),
        );

        let client = MockRpcClient { responses };
        let response = client.get_transactions(None).await.unwrap();

        assert_eq!(response.len(), 2);
        assert_eq!(
            response[0].signature,
            "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
        );
        assert_eq!(response[0].slot, 12345);
        assert_eq!(response[0].block_time, Some(1640995200));
    }

    #[tokio::test]
    async fn test_get_transactions_with_config() {
        // Use generated GetTransactionsConfig (from generated::types::*) which only has limit + before
        let test_data: Vec<TransactionInfo> = vec![TransactionInfo {
            signature: "test_signature".to_string(),
            slot: 12300,
            block_time: None,
            err: None,
            version: None,
        }];

        let mut responses = std::collections::HashMap::new();
        responses.insert(
            "getTransactions".to_string(),
            serde_json::to_value(&test_data).unwrap(),
        );

        let client = MockRpcClient { responses };

        let config = GetTransactionsConfig {
            limit: Some(10),
            before: Some("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW".to_string()),
        };

        let response = client.get_transactions(Some(config)).await.unwrap();

        assert_eq!(response.len(), 1);
        assert_eq!(response[0].signature, "test_signature");
        assert_eq!(response[0].slot, 12300);
    }

    #[tokio::test]
    async fn test_get_transactions_rpc_error() {
        // Mock client with no response for getTransactions
        let client = MockRpcClient {
            responses: std::collections::HashMap::new(),
        };

        let result = client.get_transactions(None).await;

        assert!(result.is_err());
        let error = result.unwrap_err();
        assert!(matches!(error, rialo_cdk::error::RialoError::Rpc(_)));
    }
}
