/*
VPN Confinement Integration Test

This test validates that Nixarr services are properly confined to a VPN namespace
and cannot leak traffic when the VPN connection fails. It uses a 3-VM topology
to simulate real-world network conditions.

Network Topology:
┌──────────────┐    VLAN 2     ┌─────────────┐    VLAN 1     ┌─────────────┐
│internetClient│ ◄──────────── │   gateway   │ ◄──────────── │ nixarrHost  │
│  10.0.1.2    │               │ 10.0.1.1    │               │192.168.1.2  │
│ fd00:2::2    │               │192.168.1.1  │               │ fd00:1::2   │
└──────────────┘               │ fd00:2::1   │               └─────────────┘
                               │ fd00:1::1   │                       │
                               └─────────────┘                       │
                                      │                              │
                                 WireGuard tunnel                    │
                                 10.100.0.1 ◄────────────────────────┘
                                 fd00:100::1      VPN namespace
                                                 (10.100.0.2, fd00:100::2)

Test Coverage:
- VPN namespace isolation (transmission confined to wg namespace)
- IPv4 and IPv6 traffic routing through VPN tunnel
- Traffic leak prevention when VPN is down
- Port forwarding from external clients through gateway to VPN services
- DNS configuration in VPN namespace
- Service recovery after VPN reconnection

The test ensures that:
1. All transmission traffic goes through the VPN tunnel
2. Source IP is preserved (shows VPN client IP: 10.100.0.2/fd00:100::2)
3. No traffic leaks to host network when VPN fails
4. External port forwarding works correctly
5. Both IPv4 and IPv6 work identically through the tunnel
*/
{
  pkgs,
  nixosModules,
  lib ? pkgs.lib,
}: let
  # WireGuard configuration for the VPN gateway
  wgGatewayPort = 51820;

  # Generate real WireGuard keys
  wgGatewayPrivateKey = pkgs.runCommand "wg-gateway-private" {buildInputs = [pkgs.wireguard-tools];} ''
    wg genkey > $out
  '';
  wgGatewayPublicKey = pkgs.runCommand "wg-gateway-public" {buildInputs = [pkgs.wireguard-tools];} ''
    cat ${wgGatewayPrivateKey} | wg pubkey > $out
  '';

  wgClientPrivateKey = pkgs.runCommand "wg-client-private" {buildInputs = [pkgs.wireguard-tools];} ''
    wg genkey > $out
  '';
  wgClientPublicKey = pkgs.runCommand "wg-client-public" {buildInputs = [pkgs.wireguard-tools];} ''
    cat ${wgClientPrivateKey} | wg pubkey > $out
  '';

  # Network configuration
  wgGatewayAddr = "10.100.0.1";
  wgClientAddr = "10.100.0.2";
  wgSubnet = "10.100.0.0/24";

  # Fixed VM IPs
  gatewayIP = "192.168.1.1";
  nixarrHostIP = "192.168.1.2";

  # Internet client IPs
  internetClientIP = "10.0.1.2";
  internetGatewayIP = "10.0.1.1";

  # IPv6 addresses
  gatewayIPv6 = "fd00:1::1";
  nixarrHostIPv6 = "fd00:1::2";
  internetClientIPv6 = "fd00:2::2";
  internetGatewayIPv6 = "fd00:2::1";
  wgGatewayAddrV6 = "fd00:100::1";
  wgClientAddrV6 = "fd00:100::2";

  # Generate WireGuard config file for client
  wgClientConfig = pkgs.writeText "wg-client.conf" ''
    [Interface]
    PrivateKey = ${builtins.readFile wgClientPrivateKey}
    Address = ${wgClientAddr}/24, ${wgClientAddrV6}/64
    DNS = ${wgGatewayAddr}

    [Peer]
    PublicKey = ${builtins.readFile wgGatewayPublicKey}
    Endpoint = ${gatewayIP}:${toString wgGatewayPort}
    AllowedIPs = 0.0.0.0/0, ::/0
    PersistentKeepalive = 25
  '';
in
  pkgs.nixosTest {
    name = "nixarr-vpn-confinement-test";

    # Disable interactive mode to avoid hanging
    interactive = false;

    nodes = {
      # Internet client VM - Simulates external services and clients
      internetClient = {
        config,
        pkgs,
        ...
      }: {
        virtualisation.vlans = [2]; # Connect to VLAN 2 (Internet)

        networking = {
          firewall.enable = false;
        };

        # Add route to VPN subnet
        boot.kernel.sysctl."net.ipv4.ip_forward" = 0; # internetClient doesn't forward

        # Enable systemd-networkd for proper route management
        systemd.network.enable = true;
        networking.useNetworkd = true;

        # Configure static routes to VPN subnet using systemd-networkd
        systemd.network.networks."40-eth1" = {
          matchConfig.Name = "eth1";
          networkConfig = {
            DHCP = "no";
          };
          address = [
            "${internetClientIP}/24"
            "${internetClientIPv6}/64"
          ];
          gateway = ["${internetGatewayIP}" "${internetGatewayIPv6}"];
          routes = [
            {
              Destination = "${wgSubnet}";
              Gateway = "${internetGatewayIP}";
            }
            {
              Destination = "fd00:100::/64";
              Gateway = "${internetGatewayIPv6}";
            }
          ];
        };

        # Web server that returns source IP for testing
        systemd.services.source-ip-server = {
          enable = true;
          wantedBy = ["multi-user.target"];
          after = ["network.target"];
          serviceConfig = {
            Type = "exec";
            ExecStart = let
              server = pkgs.writeText "server.py" ''
                import http.server
                import socketserver
                import socket

                class DualStackHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
                    address_family = socket.AF_INET6
                    def server_bind(self):
                        # Enable dual-stack support
                        self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
                        super().server_bind()

                class MyHandler(http.server.BaseHTTPRequestHandler):
                    def do_GET(self):
                        self.send_response(200)
                        self.end_headers()
                        self.wfile.write(f"Source: {self.client_address[0]}".encode())

                # Listen on all interfaces
                with DualStackHTTPServer(("::", 8080), MyHandler) as httpd:
                    httpd.serve_forever()
              '';
            in "${pkgs.python3}/bin/python3 ${server}";
            Restart = "always";
          };
        };

        environment.systemPackages = with pkgs; [
          netcat-gnu
          curl
          python3
        ];
      };

      # VPN Gateway VM - Acts as WireGuard server and internet gateway
      gateway = {
        config,
        pkgs,
        ...
      }: {
        virtualisation.vlans = [1 2]; # VLAN 1 for LAN, VLAN 2 for Internet

        networking = {
          interfaces.eth1 = {
            ipv4.addresses = [
              {
                address = gatewayIP;
                prefixLength = 24;
              }
            ];
            ipv6.addresses = [
              {
                address = gatewayIPv6;
                prefixLength = 64;
              }
            ];
          };

          interfaces.eth2 = {
            ipv4.addresses = [
              {
                address = internetGatewayIP;
                prefixLength = 24;
              }
            ];
            ipv6.addresses = [
              {
                address = internetGatewayIPv6;
                prefixLength = 64;
              }
            ];
          };

          firewall = {
            enable = true;
            allowedUDPPorts = [wgGatewayPort 51413];
            allowedTCPPorts = [51413];
          };

          wireguard.interfaces.wg0 = {
            ips = ["${wgGatewayAddr}/24" "${wgGatewayAddrV6}/64"];
            listenPort = wgGatewayPort;
            privateKeyFile = "${wgGatewayPrivateKey}";

            peers = [
              {
                publicKey = builtins.readFile wgClientPublicKey;
                allowedIPs = ["${wgClientAddr}/32" "${wgClientAddrV6}/128"];
              }
            ];
          };
        };

        # Enable IP forwarding
        boot.kernel.sysctl = {
          "net.ipv4.ip_forward" = 1;
          "net.ipv6.conf.all.forwarding" = 1;
        };

        # Port forwarding and firewall rules
        networking.firewall.extraCommands = ''
          # Allow WireGuard and testing traffic (IPv4)
          iptables -A INPUT -i eth1 -j ACCEPT
          iptables -A INPUT -i eth2 -j ACCEPT
          iptables -A INPUT -i wg0 -j ACCEPT

          # Allow WireGuard and testing traffic (IPv6)
          ip6tables -A INPUT -i eth1 -j ACCEPT
          ip6tables -A INPUT -i eth2 -j ACCEPT
          ip6tables -A INPUT -i wg0 -j ACCEPT

          # IPv6 forwarding rules - Allow forwarding between interfaces
          ip6tables -A FORWARD -i wg0 -o eth2 -j ACCEPT
          ip6tables -A FORWARD -i eth2 -o wg0 -j ACCEPT
          ip6tables -A FORWARD -i wg0 -o eth1 -j ACCEPT
          ip6tables -A FORWARD -i eth1 -o wg0 -j ACCEPT
          ip6tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

          # Note: No masquerading - we want to preserve source IPs for testing

          # Forward transmission peer port from internet to VPN client (this is the key test)
          iptables -t nat -A PREROUTING -i eth2 -p tcp --dport 51413 -j DNAT --to-destination ${wgClientAddr}:51413
          iptables -t nat -A PREROUTING -i eth2 -p udp --dport 51413 -j DNAT --to-destination ${wgClientAddr}:51413

          # Allow forwarded traffic
          iptables -A FORWARD -p tcp --dport 51413 -d ${wgClientAddr} -j ACCEPT
          iptables -A FORWARD -p udp --dport 51413 -d ${wgClientAddr} -j ACCEPT

          # Accept return traffic for established connections
          iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
        '';

        # Simple DNS server for testing
        services.dnsmasq = {
          enable = true;
          settings = {
            interface = "wg0";
            bind-interfaces = true;
            listen-address = wgGatewayAddr;
            # Log DNS queries for leak detection
            log-queries = true;
            log-facility = "/var/log/dnsmasq-queries.log";
            # Static DNS entries for testing
            address = [
              "/test.vpn.local/${wgGatewayAddr}"
              "/leak.test.local/1.2.3.4"
              "/transmission.local/${wgClientAddr}"
            ];
          };
        };

        # Ensure dnsmasq starts after WireGuard
        systemd.services.dnsmasq = {
          after = ["wireguard-wg0.service"];
          wants = ["wireguard-wg0.service"];
        };

        # No additional routing needed on gateway - it has direct interfaces to both networks

        # Install test utilities
        environment.systemPackages = with pkgs; [
          iptables
          netcat-gnu
          python3
          iproute2 # for ss command
          tcpdump
        ];
      };

      # Nixarr host with VPN-confined transmission
      nixarrHost = {
        config,
        pkgs,
        ...
      }: {
        imports = [nixosModules.default];

        virtualisation.vlans = [1]; # Connect to VLAN 1

        networking = {
          interfaces.eth1 = {
            ipv4.addresses = [
              {
                address = nixarrHostIP;
                prefixLength = 24;
              }
            ];
            ipv6.addresses = [
              {
                address = nixarrHostIPv6;
                prefixLength = 64;
              }
            ];
          };

          # Disable firewall for testing
          firewall.enable = false;

          # Add route to gateway
          defaultGateway = {
            address = gatewayIP;
            interface = "eth1";
          };
          defaultGateway6 = {
            address = gatewayIPv6;
            interface = "eth1";
          };
        };

        # Copy WireGuard config to the expected location
        system.activationScripts.setupWgConfig = ''
          mkdir -p /etc/wireguard
          cp ${wgClientConfig} /etc/wireguard/wg0.conf
          chmod 600 /etc/wireguard/wg0.conf
        '';

        # Minimal nixarr configuration with VPN
        nixarr = {
          enable = true;

          # Required directories
          mediaDir = "/data/media";
          stateDir = "/data/.state/nixarr";

          # Enable VPN
          vpn = {
            enable = true;
            wgConf = "/etc/wireguard/wg0.conf";
          };

          # Enable transmission with VPN
          transmission = {
            enable = true;
            vpn.enable = true;
            # Use specific peer port for testing
            peerPort = 51413;
            # Disable firewall opening since we're in VPN
            openFirewall = false;
          };

          # Disable all other services
          sonarr.enable = false;
          radarr.enable = false;
          lidarr.enable = false;
          readarr.enable = false;
          bazarr.enable = false;
          prowlarr.enable = false;
          jellyfin.enable = false;
          plex.enable = false;
          sabnzbd.enable = false;
          autobrr.enable = false;
          recyclarr.enable = false;
          jellyseerr.enable = false;
        };

        # Add IPv6 route for VPN namespace to reach internetClient via WireGuard
        systemd.network.networks."10-eth1" = {
          matchConfig.Name = "eth1";
          routes = [
            {
              routeConfig = {
                Destination = "fd00:2::/64"; # Route to internetClient network
                Gateway = gatewayIPv6; # Via gateway IPv6
              };
            }
          ];
        };

        # Install test utilities
        environment.systemPackages = with pkgs; [
          wireguard-tools
          dig
          curl
          iproute2
          iptables
          netcat-gnu
          tcpdump
        ];
      };
    };

    testScript = ''
      start_all()

      print("=== Waiting for VMs to boot ===")
      # Wait for all VMs to be ready
      internetClient.wait_for_unit("multi-user.target", timeout=60)
      gateway.wait_for_unit("multi-user.target", timeout=60)
      nixarrHost.wait_for_unit("multi-user.target", timeout=60)

      # Wait for web server on internetClient
      internetClient.wait_for_unit("source-ip-server.service")
      internetClient.wait_for_open_port(8080)

      # Wait for systemd-networkd to set up routes
      internetClient.wait_for_unit("systemd-networkd.service")


      print("=== Test 1: Basic connectivity between VMs ===")
      # First verify that nixarrHost can reach the gateway
      nixarrHost.succeed("ping -c 1 ${gatewayIP}")
      gateway.succeed("ping -c 1 ${nixarrHostIP}")


      print("=== Test 2: Check VPN namespace setup ===")
      # Check that wg namespace exists
      nixarrHost.succeed("ip netns list | grep -q wg")

      # The VPN namespace service should be running
      nixarrHost.wait_for_unit("wg.service")

      # Check if transmission is running
      nixarrHost.wait_for_unit("transmission.service", timeout=30)


      print("=== Test 3: Check WireGuard connectivity ===")
      # Check if WireGuard interface exists in namespace
      nixarrHost.succeed("ip netns exec wg ip link show wg0")

      # Check WireGuard status
      nixarrHost.succeed("ip netns exec wg wg show")

      # Test VPN tunnel connectivity
      nixarrHost.succeed("ip netns exec wg ping -c 3 ${wgGatewayAddr}")


      print("=== Test 4: Verify traffic routing through VPN ===")
      # Debug: See what the web server actually returns
      response = nixarrHost.succeed("ip netns exec wg curl -s http://${internetClientIP}:8080")
      print(f"Web server response: {response}")

      # Test traffic through VPN tunnel to internetClient - should show VPN client IP
      nixarrHost.succeed("ip netns exec wg curl -s http://${internetClientIP}:8080 | grep -q '${wgClientAddr}'")


      print("=== Test 5: Verify traffic routing through VPN ===")
      # IPv4 traffic to host network should be blocked (specific route handling)
      nixarrHost.fail("ip netns exec wg curl -s --max-time 2 http://${gatewayIP}:8080")

      # Debug IPv6 connectivity before main test
      print("=== Debug IPv6 connectivity ===")

      # Check IPv6 addresses in VPN namespace
      ipv6_addrs = nixarrHost.succeed("ip netns exec wg ip -6 addr show")
      print(f"VPN namespace IPv6 addresses:\n{ipv6_addrs}")

      # Check if VPN namespace can ping gateway IPv6
      try:
        nixarrHost.succeed("ip netns exec wg ping -6 -c 1 -W 3 ${wgGatewayAddrV6}")
        print("✓ VPN namespace can ping WireGuard gateway IPv6")
      except Exception as e:
        print(f"✗ VPN namespace cannot ping WireGuard gateway IPv6: {e}")

      # Check if VPN namespace can reach internetClient IPv6 (simple connectivity)
      try:
        result = nixarrHost.succeed("ip netns exec wg curl -6 -s --max-time 5 http://[${internetClientIPv6}]:8080")
        print(f"✓ VPN namespace can reach internetClient IPv6: {result}")
      except Exception as e:
        print(f"✗ VPN namespace cannot reach internetClient IPv6: {e}")

      # Check gateway IPv6 routes
      gw_routes = gateway.succeed("ip -6 route show")
      print(f"Gateway IPv6 routes:\n{gw_routes}")

      # Check internetClient IPv6 routes
      client_routes = internetClient.succeed("ip -6 route show")
      print(f"InternetClient IPv6 routes:\n{client_routes}")

      # IPv6 traffic should go through VPN tunnel (shows VPN client source)
      nixarrHost.succeed("ip netns exec wg curl -6 -s --max-time 2 http://[${internetClientIPv6}]:8080 | grep -q 'Source: fd00:100::2'")


      print("=== Test 6: Verify transmission is confined ===")
      # Check transmission is running and confined to VPN namespace
      nixarrHost.succeed("systemctl status transmission.service | grep -q 'Active: active'")


      print("=== Test 7: Interrupt VPN - Verify no connectivity ===")
      # Block WireGuard traffic on gateway using iptables
      gateway.succeed("iptables -I INPUT -p udp --dport ${toString wgGatewayPort} -j DROP")
      gateway.succeed("iptables -I OUTPUT -p udp --sport ${toString wgGatewayPort} -j DROP")

      # All connectivity should fail - no leaks!
      nixarrHost.fail("ip netns exec wg ping -c 1 -W 2 ${wgGatewayAddr}")
      nixarrHost.fail("ip netns exec wg curl -s --max-time 2 http://${internetClientIP}:8080")

      # DNS should also fail completely when VPN is down
      nixarrHost.fail("ip netns exec wg dig @${wgGatewayAddr} test.vpn.local +short +timeout=2")
      nixarrHost.fail("ip netns exec wg dig leak.test.local +short +timeout=2")
      print("✓ DNS queries fail when VPN is down - no fallback to host DNS")

      # Verify no traffic leaks to host network
      nixarrHost.fail("ip netns exec wg curl -s --max-time 2 http://${gatewayIP}:8080")


      print("=== Test 8: Restore VPN - Verify recovery ===")
      # Remove iptables blocks
      gateway.succeed("iptables -D INPUT -p udp --dport ${toString wgGatewayPort} -j DROP")
      gateway.succeed("iptables -D OUTPUT -p udp --sport ${toString wgGatewayPort} -j DROP")

      # Restart the wg namespace service to force reconnection
      nixarrHost.succeed("systemctl restart wg.service")
      nixarrHost.wait_for_unit("wg.service")

      # Verify VPN connectivity is restored
      nixarrHost.succeed("ip netns exec wg ping -c 3 ${wgGatewayAddr}")

      # Verify source IP is correct again - use internetClient since gateway has no web server
      nixarrHost.succeed("ip netns exec wg curl -s http://${internetClientIP}:8080 | grep -q '${wgClientAddr}'")


      print("=== Test 9: Verify DNS configuration ===")
      # Check that resolv.conf in namespace uses VPN DNS
      nixarrHost.succeed("ip netns exec wg cat /etc/resolv.conf | grep -q 'nameserver ${wgGatewayAddr}'")

      # Verify no host DNS servers are present
      nixarrHost.fail("ip netns exec wg cat /etc/resolv.conf | grep -q 'nameserver 10.0.2.3'")


      print("=== Test 9b: DNS leak test ===")

      # Debug: Check if dnsmasq is running on gateway
      gateway.succeed("systemctl status dnsmasq")
      gateway.succeed("ss -unpl | grep :53 || echo 'No DNS listener found'")

      # Debug: Check connectivity to DNS server
      nixarrHost.succeed("ip netns exec wg ping -c 1 ${wgGatewayAddr}")

      # Start tcpdump on host interface to detect DNS leaks
      nixarrHost.succeed("nohup tcpdump -i eth1 -n 'port 53' -w /tmp/dns-leak.pcap > /tmp/tcpdump-dns.log 2>&1 &")

      # Clear dnsmasq query log
      gateway.succeed("echo > /var/log/dnsmasq-queries.log || true")

      # Use dig instead of nslookup for more reliable DNS queries
      nixarrHost.succeed("ip netns exec wg dig @${wgGatewayAddr} test.vpn.local +short | grep -q ${wgGatewayAddr}")
      nixarrHost.succeed("ip netns exec wg dig @${wgGatewayAddr} leak.test.local +short | grep -q '1.2.3.4'")
      nixarrHost.succeed("ip netns exec wg dig @${wgGatewayAddr} transmission.local +short | grep -q ${wgClientAddr}")

      # Also test without specifying server (uses resolv.conf)
      nixarrHost.succeed("ip netns exec wg dig test.vpn.local +short | grep -q ${wgGatewayAddr}")

      # Wait for any potential leaked packets
      nixarrHost.succeed("pkill tcpdump || true")

      # Check if any DNS packets were captured on host interface
      dns_packets = nixarrHost.succeed("tcpdump -r /tmp/dns-leak.pcap -nn 2>/dev/null | wc -l").strip()
      if int(dns_packets) > 0:
          # Show what was captured for debugging
          captured = nixarrHost.succeed("tcpdump -r /tmp/dns-leak.pcap -nn 2>/dev/null || echo 'No packets'")
          print("DNS leak detected! Captured " + dns_packets + " packets:")
          print(captured)
          nixarrHost.fail("DNS queries leaked to host network")

      # Verify queries went through VPN by checking gateway's dnsmasq log
      gateway.succeed("grep -q 'test.vpn.local' /var/log/dnsmasq-queries.log")
      gateway.succeed("grep -q 'leak.test.local' /var/log/dnsmasq-queries.log")

      print("✓ No DNS leaks detected - all queries confined to VPN")

      # Clean up
      nixarrHost.succeed("rm -f /tmp/dns-leak.pcap /tmp/tcpdump-dns.log")


      print("=== Test 10: Port forwarding test ===")
      # Wait for transmission to be ready and listening
      nixarrHost.wait_for_open_port(9091)  # Web UI port

      # Check that transmission peer port is listening in namespace
      nixarrHost.succeed("ip netns exec wg ss -tlnp | grep -q ':51413'")
      nixarrHost.succeed("ip netns exec wg ss -ulnp | grep -q ':51413'")

      # Ensure WireGuard tunnel is active
      nixarrHost.succeed("ip netns exec wg ping -c 1 ${wgGatewayAddr}")

      # Debug: Print iptables rules and routing info
      print("=== Gateway NAT OUTPUT rules ===")
      output_rules = gateway.succeed("iptables -t nat -L OUTPUT -n -v")
      print(output_rules)

      print("=== Gateway NAT PREROUTING rules ===")
      prerouting_rules = gateway.succeed("iptables -t nat -L PREROUTING -n -v")
      print(prerouting_rules)

      print("=== Gateway routing table ===")
      routes = gateway.succeed("ip route show")
      print(routes)

      print("=== WireGuard status on gateway ===")
      wg_gateway = gateway.succeed("wg show")
      print(wg_gateway)

      print("=== WireGuard status on client ===")
      wg_client = nixarrHost.succeed("ip netns exec wg wg show")
      print(wg_client)

      # Test port forwarding through gateway
      print("=== Testing port forwarding ===")
      # First verify the NAT rules were actually applied
      output_nat = gateway.succeed("iptables -t nat -L OUTPUT | grep 51413 || echo 'No OUTPUT NAT rules found'")
      prerouting_nat = gateway.succeed("iptables -t nat -L PREROUTING | grep 51413 || echo 'No PREROUTING NAT rules found'")
      print(f"OUTPUT NAT check: {output_nat}")
      print(f"PREROUTING NAT check: {prerouting_nat}")

      # Debug connectivity and routing
      print("=== Testing connectivity from nixarrHost to gateway ===")
      nixarrHost.succeed("ping -c 1 ${gatewayIP}")

      # Debug FORWARD chain
      forward_rules = gateway.succeed("iptables -L FORWARD -n -v")
      print(f"Gateway FORWARD rules:\n{forward_rules}")

      # Check if gateway can reach VPN client (after handshake)
      gateway.succeed("wg show | grep -q 'latest handshake:'")

      # Debug port forwarding connectivity
      print("=== Debugging port forwarding ===")

      # First, ensure WireGuard tunnel is fully established
      gateway.succeed("wg")  # Force handshake if needed
      nixarrHost.succeed("ip netns exec wg wg")

      # Skip direct gateway->client test after restart (WireGuard asymmetry)
      print("=== Testing port forwarding ===")

      # Test from nixarrHost (outside VPN) through gateway DNAT
      print("Test: External client -> Gateway -> VPN Client (via DNAT)")

      # First, ensure client initiates some traffic to establish WireGuard state
      nixarrHost.succeed("ip netns exec wg ping -c 1 ${wgGatewayAddr}")

      # Start tcpdump in background using nohup to properly detach it
      gateway.succeed("nohup tcpdump -i any -n 'port 51413 or host ${wgClientAddr}' -w /tmp/capture.pcap > /tmp/tcpdump.log 2>&1 &")

      # Verify tcpdump is running
      gateway.succeed("pgrep tcpdump")
      print("Tcpdump started, now testing connection...")

      # Now test the connection - this should succeed through DNAT!
      # Test from internetClient to gateway's internet IP - this simulates external traffic
      # The connection should be forwarded through the VPN to nixarrHost's transmission
      internetClient.succeed("timeout 5 nc -z -v ${internetGatewayIP} 51413")
      print("Success: Port forwarding works!")

      # Stop tcpdump and analyze what happened
      gateway.succeed("pkill tcpdump")
      tcpdump_output = gateway.succeed("tcpdump -r /tmp/capture.pcap -nn 2>/dev/null || echo 'No packets captured'")
      print(f"Tcpdump results:\n{tcpdump_output}")

      # Verify transmission can reach external services through VPN tunnel
      nixarrHost.succeed("ip netns exec wg curl -s http://${internetClientIP}:8080 | grep -q '${wgClientAddr}'")

      # Verify port is NOT accessible from host network (outside VPN)
      nixarrHost.fail("timeout 2 nc -z -v localhost 51413")


      print("=== Test 11: IPv6 leak test ===")
      # Verify IPv6 connectivity between VMs
      nixarrHost.succeed("ping -6 -c 1 ${gatewayIPv6}")
      gateway.succeed("ping -6 -c 1 ${nixarrHostIPv6}")

      # Check if IPv6 is enabled in VPN namespace
      nixarrHost.succeed("ip netns exec wg ip -6 addr show")

      # Test IPv6 through VPN tunnel
      nixarrHost.succeed("ip netns exec wg ping -6 -c 1 ${wgGatewayAddrV6}")

      # Test IPv6 traffic routing - should go through VPN tunnel to internetClient
      nixarrHost.succeed("ip netns exec wg curl -6 -s --max-time 2 http://[${internetClientIPv6}]:8080")
      nixarrHost.succeed("ip netns exec wg curl -6 -s http://[${internetClientIPv6}]:8080 | grep -q '${wgClientAddrV6}'")


      print("=== Test 12: IPv6 traffic test with VPN interruption ===")
      # Since WireGuard tunnel uses IPv4, blocking it affects both IPv4 and IPv6 traffic
      # The IPv6 traffic inside the tunnel should fail when we block the IPv4 WireGuard connection
      # This test verifies IPv6 behavior is tied to the VPN tunnel

      # Verify WireGuard is listening (debug what ports are actually open)
      print("=== Gateway listening ports ===")
      listening_ports = gateway.succeed("ss -unp")
      print(listening_ports)

      print("=== Looking for WireGuard port ${toString wgGatewayPort} ===")
      wg_port_check = gateway.succeed("ss -unp | grep :${toString wgGatewayPort} || echo 'WireGuard port not found'")
      print(wg_port_check)

      # The previous test (Test 7) already blocked IPv4 WireGuard and verified it works
      # So IPv6 through the tunnel should also be blocked after IPv4 VPN disruption
      # Let's verify IPv6 still works before disruption
      nixarrHost.succeed("ip netns exec wg ping -6 -c 1 ${wgGatewayAddrV6}")

      # Now use the same IPv4 blocking as Test 7
      gateway.succeed("iptables -I INPUT -p udp --dport ${toString wgGatewayPort} -j DROP")
      gateway.succeed("iptables -I OUTPUT -p udp --sport ${toString wgGatewayPort} -j DROP")

      # Both IPv4 and IPv6 connectivity through VPN should fail
      nixarrHost.fail("ip netns exec wg ping -c 1 -W 2 ${wgGatewayAddr}")
      nixarrHost.fail("ip netns exec wg ping -6 -c 1 -W 2 ${wgGatewayAddrV6}")
      nixarrHost.fail("ip netns exec wg curl -6 -s --max-time 2 http://[${internetClientIPv6}]:8080")


      print("=== Test 13: IPv6 VPN recovery ===")
      # Remove iptables blocks (IPv4, since that's what WireGuard uses)
      gateway.succeed("iptables -D INPUT -p udp --dport ${toString wgGatewayPort} -j DROP")
      gateway.succeed("iptables -D OUTPUT -p udp --sport ${toString wgGatewayPort} -j DROP")

      # Verify IPv6 VPN connectivity is restored
      nixarrHost.succeed("ip netns exec wg ping -6 -c 3 ${wgGatewayAddrV6}")

      # Verify source IPv6 is correct again
      nixarrHost.succeed("ip netns exec wg curl -6 -s http://[${internetClientIPv6}]:8080 | grep -q '${wgClientAddrV6}'")


      print("=== All tests passed! ===")
    '';
  }
