//! Integration tests for NAT traversal public API
//!
//! This test module focuses on testing the public API of NAT traversal functionality.
//! It tests the high-level interfaces that users of the library will interact with.

use std::{
    net::{Ipv4Addr, SocketAddr, SocketAddrV4},
    time::Duration,
};

use ant_quic::{
    TransportConfig, VarInt,
    nat_traversal_api::{
        EndpointRole, NatTraversalConfig, NatTraversalEndpoint, NatTraversalEvent, PeerId,
    },
    quic_node::{QuicNodeConfig, QuicP2PNode},
};

#[tokio::test]
async fn test_nat_traversal_endpoint_creation() {
    // Test creating a client endpoint
    let client_config = NatTraversalConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec!["203.0.113.1:9000".parse().unwrap()],
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    let client_endpoint = NatTraversalEndpoint::new(client_config, None).await;
    // This might fail due to TLS configuration, but the API should be accessible
    match client_endpoint {
        Ok(_) => {
            println!("Client endpoint created successfully");
        }
        Err(e) => {
            println!("Client endpoint creation failed (expected): {}", e);
        }
    }

    // Test creating a bootstrap endpoint
    let bootstrap_config = NatTraversalConfig {
        role: EndpointRole::Bootstrap,
        bootstrap_nodes: vec![],
        max_candidates: 50,
        coordination_timeout: Duration::from_secs(5),
        enable_symmetric_nat: false,
        enable_relay_fallback: false,
        max_concurrent_attempts: 10,
    };

    let bootstrap_endpoint = NatTraversalEndpoint::new(bootstrap_config, None).await;
    match bootstrap_endpoint {
        Ok(_) => {
            println!("Bootstrap endpoint created successfully");
        }
        Err(e) => {
            println!("Bootstrap endpoint creation failed (expected): {}", e);
        }
    }
}

#[tokio::test]
async fn test_nat_traversal_config_validation() {
    // Test that config validation works
    let invalid_config = NatTraversalConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec![], // Invalid - client needs bootstrap nodes
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    let result = NatTraversalEndpoint::new(invalid_config, None).await;
    assert!(result.is_err(), "Expected error for invalid config");

    let valid_config = NatTraversalConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec!["203.0.113.1:9000".parse().unwrap()],
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    let result = NatTraversalEndpoint::new(valid_config, None).await;
    // May fail due to TLS setup, but config validation should pass
    match result {
        Ok(_) => {
            println!("Valid config accepted");
        }
        Err(e) => {
            // Should not be a config error
            assert!(
                !e.to_string().contains("bootstrap"),
                "Config validation should pass"
            );
            println!("Non-config error (expected): {}", e);
        }
    }
}

#[tokio::test]
async fn test_quic_node_creation() {
    // Test basic QuicP2PNode creation
    let config = QuicNodeConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec!["203.0.113.1:9000".parse().unwrap()],
        enable_coordinator: false,
        max_connections: 10,
        connection_timeout: Duration::from_secs(30),
        stats_interval: Duration::from_secs(60),
    };

    let node = QuicP2PNode::new(config).await;
    match node {
        Ok(node) => {
            println!("QUIC P2P node created successfully");

            // Test getting stats
            let stats = node.get_stats().await;
            assert_eq!(stats.active_connections, 0);
            assert_eq!(stats.successful_connections, 0);
            assert_eq!(stats.failed_connections, 0);
            println!("Node stats: {:?}", stats);
        }
        Err(e) => {
            println!("Node creation failed (may be expected): {}", e);
        }
    }
}

#[tokio::test]
async fn test_peer_id_functionality() {
    // Test PeerId creation and display
    let peer_id = PeerId([1u8; 32]);
    let display_str = format!("{}", peer_id);
    assert_eq!(display_str, "0101010101010101");

    // Test PeerId from array
    let peer_id2 = PeerId::from([2u8; 32]);
    let display_str2 = format!("{}", peer_id2);
    assert_eq!(display_str2, "0202020202020202");

    // Test equality
    assert_eq!(peer_id, PeerId([1u8; 32]));
    assert_ne!(peer_id, peer_id2);
}

#[tokio::test]
async fn test_nat_traversal_event_callback() {
    use std::collections::VecDeque;
    use std::sync::{Arc, Mutex};

    // Create a channel to collect events
    let events = Arc::new(Mutex::new(VecDeque::new()));
    let events_clone = events.clone();

    let config = NatTraversalConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec!["203.0.113.1:9000".parse().unwrap()],
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    // Create endpoint with event callback
    let endpoint = NatTraversalEndpoint::new(
        config,
        Some(Box::new(move |event| {
            events_clone.lock().unwrap().push_back(event);
        })),
    ).await;

    match endpoint {
        Ok(endpoint) => {
            println!("Endpoint with callback created successfully");

            // Test NAT traversal initiation
            let peer_id = PeerId([1; 32]);
            let coordinator = "203.0.113.1:9000".parse().unwrap();

            let result = endpoint.initiate_nat_traversal(peer_id, coordinator);
            match result {
                Ok(()) => {
                    println!("NAT traversal initiated successfully");

                    // Poll to trigger events
                    let _ = endpoint.poll(std::time::Instant::now());

                    // Check that events were generated
                    let collected_events = events.lock().unwrap();
                    if !collected_events.is_empty() {
                        println!("Events generated: {}", collected_events.len());
                    }
                }
                Err(e) => {
                    println!("NAT traversal initiation failed: {}", e);
                }
            }

            // Test statistics
            let stats = endpoint.get_statistics();
            match stats {
                Ok(stats) => {
                    println!(
                        "Statistics: active_sessions={}, bootstrap_nodes={}",
                        stats.active_sessions, stats.total_bootstrap_nodes
                    );
                }
                Err(e) => {
                    println!("Failed to get statistics: {}", e);
                }
            }
        }
        Err(e) => {
            println!("Endpoint creation failed: {}", e);
        }
    }
}

#[tokio::test]
async fn test_bootstrap_node_management() {
    let config = NatTraversalConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec!["203.0.113.1:9000".parse().unwrap()],
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    let endpoint = NatTraversalEndpoint::new(config, None).await;

    if let Ok(endpoint) = endpoint {
        // Test adding bootstrap nodes
        let new_node = "203.0.113.2:9000".parse().unwrap();
        let result = endpoint.add_bootstrap_node(new_node);
        match result {
            Ok(()) => {
                println!("Bootstrap node added successfully");

                // Test removing bootstrap nodes
                let result = endpoint.remove_bootstrap_node(new_node);
                match result {
                    Ok(()) => {
                        println!("Bootstrap node removed successfully");
                    }
                    Err(e) => {
                        println!("Failed to remove bootstrap node: {}", e);
                    }
                }
            }
            Err(e) => {
                println!("Failed to add bootstrap node: {}", e);
            }
        }
    }
}

#[tokio::test]
async fn test_transport_config_with_nat_traversal() {
    // Test that TransportConfig supports NAT traversal configuration
    let mut transport_config = TransportConfig::default();

    // This test verifies that the NAT traversal configuration can be set
    // The actual types needed are likely private, so we'll test what we can access
    println!("Transport config created successfully");

    // Test that we can configure initial MTU
    transport_config.initial_mtu(1500);
    println!("Initial MTU configured to 1500");

    // Test various configuration options
    transport_config.max_concurrent_bidi_streams(VarInt::from_u32(50));
    transport_config.max_concurrent_uni_streams(VarInt::from_u32(50));
    transport_config.stream_receive_window(VarInt::from_u32(1024 * 1024));
    transport_config.receive_window(VarInt::from_u32(2 * 1024 * 1024));
    transport_config.send_window(2 * 1024 * 1024);
    transport_config.initial_mtu(1500);
    transport_config.enable_segmentation_offload(true);

    println!("Transport config configured successfully");
}

#[tokio::test]
async fn test_var_int_functionality() {
    // Test VarInt creation and basic operations
    let var_int_1 = VarInt::from_u32(42);
    let var_int_2 = VarInt::from_u32(100);

    // Test that VarInt can be created
    println!("VarInt created successfully");

    // Test equality
    assert_eq!(var_int_1, VarInt::from_u32(42));
    assert_ne!(var_int_1, var_int_2);

    // Test various values
    let _small = VarInt::from_u32(0);
    let _medium = VarInt::from_u32(1000);
    let _large = VarInt::from_u32(1000000);

    // These should all be creatable without panicking
    println!("Various VarInt values created successfully");
}

#[tokio::test]
async fn test_endpoint_role_variants() {
    // Test all endpoint role variants
    let client_role = EndpointRole::Client;
    let server_role = EndpointRole::Server {
        can_coordinate: true,
    };
    let bootstrap_role = EndpointRole::Bootstrap;

    // Test that roles can be matched
    match client_role {
        EndpointRole::Client => println!("Client role matched"),
        _ => panic!("Client role should match"),
    }

    match server_role {
        EndpointRole::Server { can_coordinate } => {
            assert!(can_coordinate);
            println!("Server role matched with coordination capability");
        }
        _ => panic!("Server role should match"),
    }

    match bootstrap_role {
        EndpointRole::Bootstrap => println!("Bootstrap role matched"),
        _ => panic!("Bootstrap role should match"),
    }
}

#[tokio::test]
async fn test_nat_traversal_config_defaults() {
    // Test that default configuration is sensible
    let config = NatTraversalConfig::default();

    assert_eq!(config.role, EndpointRole::Client);
    assert_eq!(config.max_candidates, 8);
    assert_eq!(config.coordination_timeout, Duration::from_secs(10));
    assert!(config.enable_symmetric_nat);
    assert!(config.enable_relay_fallback);
    assert_eq!(config.max_concurrent_attempts, 3);
    assert!(config.bootstrap_nodes.is_empty());

    println!("Default NAT traversal config verified");
}

#[tokio::test]
async fn test_error_handling() {
    // Test various error conditions

    // Test invalid configurations
    let invalid_config = NatTraversalConfig {
        role: EndpointRole::Client,
        bootstrap_nodes: vec![], // Client needs bootstrap nodes
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    let result = NatTraversalEndpoint::new(invalid_config, None).await;
    assert!(
        result.is_err(),
        "Should fail for client without bootstrap nodes"
    );

    // Test that bootstrap endpoint doesn't need bootstrap nodes
    let bootstrap_config = NatTraversalConfig {
        role: EndpointRole::Bootstrap,
        bootstrap_nodes: vec![], // Bootstrap doesn't need bootstrap nodes
        max_candidates: 10,
        coordination_timeout: Duration::from_secs(10),
        enable_symmetric_nat: true,
        enable_relay_fallback: true,
        max_concurrent_attempts: 3,
    };

    let result = NatTraversalEndpoint::new(bootstrap_config, None).await;
    // May fail due to TLS setup, but not due to config validation
    match result {
        Ok(_) => println!("Bootstrap endpoint config validation passed"),
        Err(e) => {
            // Should not be a config error about bootstrap nodes
            assert!(
                !e.to_string().contains("bootstrap"),
                "Bootstrap endpoint shouldn't need bootstrap nodes"
            );
            println!("Non-config error (expected): {}", e);
        }
    }
}

// Helper function to create test peer IDs
fn test_peer_id(id: u8) -> PeerId {
    PeerId([id; 32])
}

// Helper function to create test socket addresses
fn test_socket_addr(ip: u8, port: u16) -> SocketAddr {
    SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 1, ip), port))
}
