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

use rs_zero::rpc::{
    RpcClientConfig, connect_channel, request_id_interceptor, serve_health_with_shutdown,
};
use tokio::sync::oneshot;
use tonic::service::Interceptor;
use tonic_health::pb::{HealthCheckRequest, health_client::HealthClient};

#[tokio::test]
async fn health_server_accepts_client_call() {
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
        .await
        .expect("bind");
    let addr: SocketAddr = listener.local_addr().expect("local addr");
    drop(listener);

    let (tx, rx) = oneshot::channel();
    let server = tokio::spawn(async move {
        serve_health_with_shutdown(addr, async {
            let _ = rx.await;
        })
        .await
    });

    tokio::time::sleep(Duration::from_millis(50)).await;

    let config = RpcClientConfig::new(format!("http://{addr}"));
    let channel = connect_channel(&config).await.expect("connect");
    let mut client = HealthClient::new(channel);
    let response = client
        .check(HealthCheckRequest {
            service: String::new(),
        })
        .await
        .expect("health check");

    assert_eq!(response.into_inner().status, 1);

    let _ = tx.send(());
    server.await.expect("join").expect("server");
}

#[tokio::test]
async fn client_timeout_is_applied_to_endpoint() {
    let config = RpcClientConfig {
        endpoint: "http://127.0.0.1:9".to_string(),
        connect_timeout: Duration::from_millis(10),
        request_timeout: Duration::from_millis(20),
        ..RpcClientConfig::new("http://127.0.0.1:9")
    };

    let result = connect_channel(&config).await;
    assert!(result.is_err());
}

#[test]
fn interceptor_adds_metadata() {
    let mut interceptor = request_id_interceptor();
    let request = interceptor.call(tonic::Request::new(())).expect("request");

    assert!(request.metadata().contains_key("x-request-id"));
}
