//! Evil tests designed to break the HTTP transport layer.
//!
//! These tests simulate various attack vectors, edge cases, and malformed
//! responses that a robust HTTP client should handle gracefully.

use super::*;

// ============================================================================
// Test 1: Transfer-Encoding and Content-Length Disagreement
// ============================================================================

#[tokio::test]
async fn response_with_conflicting_transfer_encoding_and_content_length() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");
        // Send both Content-Length and Transfer-Encoding: chunked
        // This is a protocol violation per HTTP/1.1 RFC 7230 Section 3.3.3
        let response = [
            b"HTTP/1.1 200 OK\r\n".as_slice(),
            b"Content-Length: 1000\r\n".as_slice(),
            b"Transfer-Encoding: chunked\r\n".as_slice(),
            b"\r\n".as_slice(),
            b"5\r\n".as_slice(),
            b"hello\r\n".as_slice(),
            b"0\r\n".as_slice(),
            b"\r\n".as_slice(),
        ]
        .concat();
        socket
            .write_all(&response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    // Should handle gracefully - either succeed with chunked body or error
    assert_eq!(result.len(), 1);
    // The response should not cause a panic or hang
}

// ============================================================================
// Test 2: Response with 100 Set-Cookie Headers
// ============================================================================

#[tokio::test]
async fn response_with_100_set_cookie_headers() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        let mut response = b"HTTP/1.1 200 OK\r\n".to_vec();
        // Add 100 Set-Cookie headers (this creates a large header block)
        for i in 0..100 {
            response.extend_from_slice(
                format!("Set-Cookie: session{i}=value{i}; Path=/\r\n").as_bytes(),
            );
        }
        response.extend_from_slice(b"Content-Length: 2\r\n\r\nok");
        socket
            .write_all(&response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // May fail due to header size limits - that's acceptable graceful handling
    if let Ok(response) = &result[0] {
        // If it succeeds, verify we got some cookies
        assert!(
            response
                .data
                .header_map
                .iter()
                .any(|(k, _)| k.eq_ignore_ascii_case("set-cookie")),
            "Should have Set-Cookie headers"
        );
    } else {
        // Error is acceptable - headers too large causes request failure
        // This is graceful handling (not a panic)
    }
}

// ============================================================================
// Test 3: Gzip Response That Would Decompress to Huge Size
// ============================================================================

#[tokio::test]
async fn response_gzip_decompresses_to_huge_size() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Create a valid gzip bomb - highly compressed data
        // This is a gzip stream that decompresses to a large amount of zeros
        // Format: gzip header + compressed data
        let gzip_bomb: Vec<u8> = vec![
            0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // gzip header
            0xed, 0xc1, 0x01, 0x0d, 0x00, 0x00, 0x00, 0xc2, 0x20, 0xfb, 0xcf, 0xc0, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x7e, 0x93, 0xd1, 0x69, 0x03, 0x00, 0x00, 0x00,
        ];

        let response = format!(
            "HTTP/1.1 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: {}\r\n\r\n",
            gzip_bomb.len()
        );
        socket
            .write_all(response.as_bytes())
            .await
            .expect("operation should succeed");
        socket
            .write_all(&gzip_bomb)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(500), // Short timeout
        HttpTransportOptions {
            max_response_size: Some(1024 * 1024), // 1MB limit
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should either succeed with decompressed data or fail gracefully with size limit
    match &result[0] {
        Ok(response) => {
            // If it succeeds, body should be within limits
            assert!(
                response.data.body.len() <= 1024 * 1024,
                "Response body should not exceed max_response_size"
            );
        }
        Err(e) => {
            // If it fails, it should be a graceful error (size limit or decode error)
            let msg = e.to_string().to_lowercase();
            assert!(
                msg.contains("large") || msg.contains("size") || msg.contains("decode"),
                "Error should be about size limit or decoding: {msg}"
            );
        }
    }
}

// ============================================================================
// Test 4: Connection Sending 1 Byte Per Second (Slowloris-style)
// ============================================================================

#[tokio::test]
async fn connection_sending_one_byte_per_second() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Send response extremely slowly - 1 byte per 100ms
        let response = b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok";
        for byte in response {
            if socket.write_all(&[*byte]).await.is_err() {
                // Client disconnected (expected due to timeout) - this is fine
                return;
            }
            tokio::time::sleep(Duration::from_millis(100)).await;
        }
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(300), // Short timeout
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    // Server may have panicked or completed - that's fine, client should have timed out
    let _ = server.await;

    assert_eq!(result.len(), 1);
    // Should timeout gracefully
    assert!(
        result[0].is_err(),
        "Should timeout or error on slow response"
    );
}

// ============================================================================
// Test 5: Response with Null Bytes in Header Values
// ============================================================================

#[tokio::test]
async fn response_with_null_bytes_in_header_values() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Craft response with null byte in header value
        let response = b"HTTP/1.1 200 OK\r\nX-Evil: hello\x00world\r\nContent-Length: 2\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully - either succeed with sanitized header or error
    if let Ok(response) = &result[0] {
        // If succeeds, check headers don't contain null
        for (name, value) in &response.data.header_map {
            assert!(
                !name.contains('\x00'),
                "Header name should not contain null bytes"
            );
            assert!(
                !value.contains('\x00'),
                "Header value should not contain null bytes: {value:?}"
            );
        }
    } else {
        // Error is acceptable for malformed headers
    }
}

// ============================================================================
// Test 6: Proxy Authentication Required but Wrong Credentials
// ============================================================================

#[tokio::test]
async fn proxy_requires_auth_but_wrong_credentials() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let n = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");
        let request = String::from_utf8_lossy(&buf[..n]);

        // Check for proxy authorization
        if request.to_ascii_lowercase().contains("proxy-authorization") {
            // Return 407 with wrong auth requirement again
            let response = b"HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\"proxy\"\r\nContent-Length: 0\r\n\r\n";
            socket
                .write_all(response)
                .await
                .expect("operation should succeed");
        } else {
            // No auth provided
            let response = b"HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\"proxy\"\r\nContent-Length: 0\r\n\r\n";
            socket
                .write_all(response)
                .await
                .expect("operation should succeed");
        }
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(500),
        HttpTransportOptions {
            allow_internal: true,
            proxy_url: Some(format!("http://{addr}")),
            proxy_auth: Some(("wrong".to_string(), "credentials".to_string())),
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan("http://example.com/".to_string());
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should fail gracefully with proxy auth error
    assert!(result[0].is_err() || result[0].as_ref().unwrap().data.status == 407);
}

// ============================================================================
// Test 7: Redirect Loop of 100 Hops
// ============================================================================

#[tokio::test]
async fn redirect_loop_of_100_hops() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));

    let _server = tokio::spawn({
        let counter = Arc::clone(&counter);
        async move {
            for _ in 0..15 {
                let (mut socket, _) = listener.accept().await.expect("operation should succeed");
                let counter = Arc::clone(&counter);
                tokio::spawn(async move {
                    let mut buf = [0u8; 2048];
                    let _ = socket
                        .read(&mut buf)
                        .await
                        .expect("operation should succeed");

                    let count = counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;

                    // Always redirect to self, creating infinite loop
                    let response = format!(
                        "HTTP/1.1 302 Found\r\nLocation: http://127.0.0.1:{}/redirect\r\nContent-Length: 0\r\n\r\n",
                        addr.port()
                    );
                    socket
                        .write_all(response.as_bytes())
                        .await
                        .expect("operation should succeed");

                    // Stop after handling enough requests to detect loop
                    if count >= 15 {}
                });
            }
        }
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(500),
        HttpTransportOptions {
            allow_internal: true,
            disable_redirects: false,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = RequestPlan {
        requests: vec![RequestSpec {
            target: format!("http://{addr}/"),
            url: format!("http://{addr}/start"),
            method: "GET".to_string(),
            headers: HashMap::new(),
            body: None,
            template_refs: vec![("redirect-test".to_string(), 0)],
            follow_redirects: true,
            max_redirects: 10,
            cookie_reuse: true,
        }],
        target_count: 1,
        template_count: 1,
        dedup_savings: 0,
    };

    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    // Give server time to process
    tokio::time::sleep(Duration::from_millis(500)).await;

    assert_eq!(result.len(), 1);
    // Should either follow up to max_redirects or error gracefully
}

// ============================================================================
// Test 8: Response with Header Names Containing Spaces
// ============================================================================

#[tokio::test]
async fn response_with_header_names_containing_spaces() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Invalid: header names cannot contain spaces
        let response = b"HTTP/1.1 200 OK\r\nX Evil Header: value\r\nContent-Length: 2\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully - either error or sanitize
}

// ============================================================================
// Test 9: Response with Header Names Containing Control Characters
// ============================================================================

#[tokio::test]
async fn response_with_header_names_containing_control_chars() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Invalid: header names cannot contain control characters
        let response =
            b"HTTP/1.1 200 OK\r\nX-Evil\x01\x02Header: value\r\nContent-Length: 2\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully
}

// ============================================================================
// Test 10: Request to URL with username:password@ in Authority
// ============================================================================

#[tokio::test]
async fn request_to_url_with_username_password_in_authority() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let received_auth = Arc::new(AsyncMutex::new(String::new()));
    let seen_auth = Arc::clone(&received_auth);

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 4096];
        let n = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");
        let request = String::from_utf8_lossy(&buf[..n]).to_string();

        // Check for Authorization header
        if let Some(line) = request
            .lines()
            .find(|l| l.to_ascii_lowercase().starts_with("authorization:"))
        {
            *seen_auth.lock().await = line.to_string();
        }

        socket
            .write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok")
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    // URL with embedded credentials
    let plan = single_request_plan(format!("http://admin:secret@127.0.0.1:{}/", addr.port()));
    let responses = transport
        .execute(&plan)
        .collect::<Vec<_>>()
        .await
        .into_iter()
        .collect::<std::result::Result<Vec<_>, secir::Error>>()
        .expect("operation should succeed");

    server.await.expect("operation should succeed");

    assert_eq!(responses.len(), 1);
    assert_eq!(responses[0].data.status, 200);
    // reqwest should handle URL credentials by converting to Authorization header
    // or stripping them, either way should not panic
}

// ============================================================================
// Test 11: Response with Negative Content-Length
// ============================================================================

#[tokio::test]
async fn response_with_negative_content_length() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        let response = b"HTTP/1.1 200 OK\r\nContent-Length: -1\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully - either error or treat as 0
}

// ============================================================================
// Test 12: Response with Extremely Large Content-Length
// ============================================================================

#[tokio::test]
async fn response_with_extremely_large_content_length() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Content-Length larger than usize max (or close to it)
        let response = b"HTTP/1.1 200 OK\r\nContent-Length: 9999999999999999999\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            max_response_size: Some(1024 * 1024),
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle overflow gracefully - either succeed or error
    // (reqwest/hyper may fail to decode with invalid Content-Length)
    if let Err(e) = &result[0] {
        let msg = e.to_string().to_lowercase();
        // Any error related to reading/decoding body is acceptable
        assert!(
            msg.contains("large")
                || msg.contains("size")
                || msg.contains("length")
                || msg.contains("decode")
                || msg.contains("body")
                || msg.contains("read"),
            "Should error gracefully: {msg}"
        );
    } else {
        // Succeeding is also OK if it respects max_response_size
    }
}

// ============================================================================
// Test 13: Malformed Chunked Encoding (Invalid Hex Size)
// ============================================================================

#[tokio::test]
async fn malformed_chunked_encoding_invalid_hex_size() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        let response =
            b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nNOTHEX\r\nhello\r\n0\r\n\r\n";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle invalid chunked encoding gracefully (either error or truncated ok)
    let _ = &result[0];
}

// ============================================================================
// Test 14: Response Headers Larger Than Max Buffer
// ============================================================================

#[tokio::test]
async fn response_headers_larger_than_typical_buffer() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Create a response with very large headers (10KB)
        let mut response = b"HTTP/1.1 200 OK\r\n".to_vec();
        for i in 0..100 {
            response
                .extend_from_slice(format!("X-Header-{}: {}\r\n", i, "x".repeat(100)).as_bytes());
        }
        response.extend_from_slice(b"Content-Length: 2\r\n\r\nok");
        socket
            .write_all(&response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(500),
        HttpTransportOptions {
            max_response_size: Some(1024 * 1024),
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle large headers gracefully
}

// ============================================================================
// Test 15: Infinite Chunked Response (Never-Ending Chunks)
// ============================================================================

#[tokio::test]
async fn infinite_chunked_response_never_ending() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        if socket
            .write_all(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n")
            .await
            .is_err()
        {
            return;
        }

        // Send chunks until client disconnects
        loop {
            if socket.write_all(b"5\r\nhello\r\n").await.is_err() {
                // Client disconnected (expected) - this is fine
                return;
            }
            tokio::time::sleep(Duration::from_millis(10)).await;
        }
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(200), // Short timeout
        HttpTransportOptions {
            max_response_size: Some(1024), // Small max size
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    // Server will be running forever - don't wait for it
    let () = server.abort();

    assert_eq!(result.len(), 1);
    // Should timeout or hit size limit gracefully
    match &result[0] {
        Err(_) => {
            // Error is expected (timeout or size limit)
        }
        Ok(response) => {
            // If succeeds, body should be within limits
            assert!(
                response.data.body.len() <= 1024,
                "Body should not exceed max_response_size"
            );
        }
    }
}

// ============================================================================
// Test 16: Connection Closed Abruptly Mid-Headers
// ============================================================================

#[tokio::test]
async fn connection_closed_abruptly_mid_headers() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Send incomplete response and close
        socket
            .write_all(b"HTTP/1.1 200 OK\r\nContent-")
            .await
            .expect("operation should succeed");
        // Abruptly close connection
        drop(socket);
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should error gracefully on connection close
    assert!(
        result[0].is_err(),
        "Should error on abrupt connection close"
    );
}

// ============================================================================
// Test 17: Response with Duplicate Content-Length Headers
// ============================================================================

#[tokio::test]
async fn response_with_duplicate_content_length_headers() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Multiple Content-Length headers with different values
        let response = b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Length: 100\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully - either error or use first/last value
}

// ============================================================================
// Test 18: Chunked Response with Missing Final Chunk
// ============================================================================

#[tokio::test]
async fn chunked_response_with_missing_final_chunk() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Send chunked response but forget the terminating 0\r\n\r\n
        let response = [
            b"HTTP/1.1 200 OK\r\n".as_slice(),
            b"Transfer-Encoding: chunked\r\n".as_slice(),
            b"\r\n".as_slice(),
            b"5\r\n".as_slice(),
            b"hello\r\n".as_slice(),
            // Missing: 0\r\n\r\n
        ]
        .concat();
        socket
            .write_all(&response)
            .await
            .expect("operation should succeed");
        // Close connection to signal end
        drop(socket);
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully - either succeed with received data or error
}

// ============================================================================
// Test 19: Response with Status Line Containing Control Characters
// ============================================================================

#[tokio::test]
async fn response_status_line_containing_control_chars() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Status line with control characters in reason phrase
        let response = b"HTTP/1.1 200 OK\x00\x01\x02\r\nContent-Length: 2\r\n\r\nok";
        socket
            .write_all(response)
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully
    if let Ok(response) = &result[0] {
        assert_eq!(response.data.status, 200);
    } else {
        // Error is also acceptable for malformed status line
    }
}

// ============================================================================
// Test 20: Request with URL Containing Null Bytes
// ============================================================================

#[tokio::test]
async fn request_url_containing_null_bytes() {
    // This tests URL parsing resilience
    // A URL with null bytes should be rejected at the parsing stage

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    // URL with embedded null byte
    let malicious_url = "http://127.0.0.1/evil\x00path";

    let plan = single_request_plan(malicious_url.to_string());
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    assert_eq!(result.len(), 1);
    // Should reject the URL gracefully
    assert!(
        result[0].is_err(),
        "Should reject URL containing null bytes"
    );

    let err_msg = result[0].as_ref().unwrap_err().to_string().to_lowercase();
    assert!(
        err_msg.contains("invalid") || err_msg.contains("url") || err_msg.contains("parse"),
        "Error should indicate invalid URL: {err_msg}"
    );
}

// ============================================================================
// Additional Edge Case Tests
// ============================================================================

#[tokio::test]
async fn response_with_chunk_size_larger_than_actual_data() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // Claim chunk size is 1000 but only send 5 bytes
        let response = [
            b"HTTP/1.1 200 OK\r\n".as_slice(),
            b"Transfer-Encoding: chunked\r\n".as_slice(),
            b"\r\n".as_slice(),
            b"3E8\r\n".as_slice(), // 1000 in hex
            b"hello\r\n".as_slice(), // Only 5 bytes
                                   // Missing 995 bytes - connection will be closed
        ]
        .concat();
        socket
            .write_all(&response)
            .await
            .expect("operation should succeed");
        // Close connection
        drop(socket);
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle incomplete chunked data gracefully (either error or truncated ok)
    let _ = &result[0];
}

#[tokio::test]
async fn response_with_http_version_0_9() {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.expect("operation should succeed");
        let mut buf = [0u8; 2048];
        let _ = socket
            .read(&mut buf)
            .await
            .expect("operation should succeed");

        // HTTP/0.9 style response (no headers, just body)
        socket
            .write_all(b"This is an HTTP/0.9 response")
            .await
            .expect("operation should succeed");
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_secs(5),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    let plan = single_request_plan(format!("http://{addr}/"));
    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    server.await.expect("operation should succeed");

    assert_eq!(result.len(), 1);
    // Should handle gracefully - either parse as best effort or error
}

#[tokio::test]
async fn request_with_headers_containing_newlines() {
    // Test that headers with newlines are handled gracefully
    // This tests header injection protection

    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("operation should succeed");
    let addr = listener.local_addr().expect("operation should succeed");

    let server = tokio::spawn(async move {
        // Use timeout to avoid hanging
        let accept_future = listener.accept();
        let timeout = tokio::time::Duration::from_secs(2);
        if let Ok(Ok((mut socket, _))) = tokio::time::timeout(timeout, accept_future).await {
            let mut buf = [0u8; 4096];
            let _ = socket.read(&mut buf).await;
            let _ = socket
                .write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok")
                .await;
        }
    });

    let transport = HttpTransport::with_transport_options(
        1,
        Duration::from_millis(500),
        HttpTransportOptions {
            allow_internal: true,
            ..HttpTransportOptions::default()
        },
    )
    .expect("operation should succeed");

    // reqwest should sanitize headers containing newlines
    let mut headers = HashMap::new();
    headers.insert(
        "X-Custom".to_string(),
        "value\r\nInjected: header".to_string(),
    );

    let plan = RequestPlan {
        requests: vec![RequestSpec {
            target: format!("http://{addr}/"),
            url: format!("http://{addr}/"),
            method: "GET".to_string(),
            headers,
            body: None,
            template_refs: vec![("header-test".to_string(), 0)],
            follow_redirects: true,
            max_redirects: 10,
            cookie_reuse: true,
        }],
        target_count: 1,
        template_count: 1,
        dedup_savings: 0,
    };

    let result = transport.execute(&plan).collect::<Vec<_>>().await;

    // Don't wait for server - abort it
    let () = server.abort();

    assert_eq!(result.len(), 1);
    // reqwest should either reject or sanitize the header - no panic
}
