use super::*;

#[tokio::test]
async fn request_url_with_non_ascii_characters() {
    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_path = Arc::new(AsyncMutex::new(String::new()));
    let seen_path = Arc::clone(&received_path);

    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();

        if let Some(line) = request.lines().next() {
            let parts: Vec<&str> = line.split_whitespace().collect();
            if parts.len() >= 2 {
                *seen_path.lock().await = parts[1].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");

    let base = format!("http://{addr}");
    let plan = RequestPlan {
        requests: vec![RequestSpec {
            target: base.clone(),
            url: format!("{base}/%E6%97%A5%E6%9C%AC%E8%AA%9E/path"),
            method: "GET".to_string(),
            headers: HashMap::new(),
            body: None,
            template_refs: vec![("test-template".to_string(), 0)],
            follow_redirects: true,
            max_redirects: 10,
            cookie_reuse: true,
        }],
        target_count: 1,
        template_count: 1,
        dedup_savings: 0,
    };

    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);

    let path = received_path.lock().await;
    assert!(
        path.contains("%E6%97%A5")
            || path.contains("日本語")
            || path.contains("/%E6%97%A5%E6%9C%AC%E8%AA%9E/path"),
        "Path should contain encoded Japanese characters or be decoded, got: {}",
        *path
    );
}

#[tokio::test]
async fn response_content_length_larger_than_actual_body() {
    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 = "HTTP/1.1 200 OK\r\nContent-Length: 1000\r\n\r\nok";
        socket
            .write_all(response.as_bytes())
            .await
            .expect("operation should succeed");
        tokio::time::sleep(Duration::from_millis(100)).await;
    });

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

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

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

    assert_eq!(results.len(), 1);
}

#[tokio::test]
async fn response_content_length_smaller_than_actual_body() {
    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");
        socket
            .write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nokextra data here")
            .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 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);
    assert_eq!(responses[0].data.body, b"ok");
}

#[tokio::test]
async fn response_chunked_with_zero_length_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");
        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(),
            b"5\r\n".as_slice(),
            b"world\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 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);
    assert_eq!(responses[0].data.body, b"helloworld");
}

#[tokio::test]
async fn response_chunked_with_trailers() {
    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\n".as_slice(),
            b"Transfer-Encoding: chunked\r\n".as_slice(),
            b"Trailer: X-Trailer-Header\r\n".as_slice(),
            b"\r\n".as_slice(),
            b"4\r\n".as_slice(),
            b"test\r\n".as_slice(),
            b"0\r\n".as_slice(),
            b"X-Trailer-Header: trailer-value\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 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);
    assert_eq!(responses[0].data.body, b"test");
}

#[tokio::test]
async fn response_empty_chunked_body() {
    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 = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n";
        socket
            .write_all(response.as_bytes())
            .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 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);
    assert_eq!(
        responses[0].data.body.len(),
        0,
        "HEAD request should return empty body"
    );
}
