use lesavka_common::lesavka::relay_client::RelayClient;
use tempfile::tempdir;
use tonic::transport::Channel;

async fn connect_with_retry(addr: std::net::SocketAddr) -> Channel {
    let endpoint = tonic::transport::Endpoint::from_shared(format!("http://{addr}"))
        .expect("endpoint")
        .tcp_nodelay(true);
    for _ in 0..40 {
        if let Ok(channel) = endpoint.clone().connect().await {
            return channel;
        }
        tokio::time::sleep(std::time::Duration::from_millis(25)).await;
    }
    panic!("failed to connect to local tonic server");
}

fn build_handler_for_tests() -> (tempfile::TempDir, Handler) {
    let dir = tempdir().expect("tempdir");
    let kb_path = dir.path().join("hidg0.bin");
    let ms_path = dir.path().join("hidg1.bin");
    std::fs::write(&kb_path, []).expect("create kb file");
    std::fs::write(&ms_path, []).expect("create ms file");

    let kb = tokio::fs::File::from_std(
        std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .open(&kb_path)
            .expect("open kb"),
    );
    let ms = tokio::fs::File::from_std(
        std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .open(&ms_path)
            .expect("open ms"),
    );

    (
        dir,
        Handler {
            kb: std::sync::Arc::new(tokio::sync::Mutex::new(Some(kb))),
            ms: std::sync::Arc::new(tokio::sync::Mutex::new(Some(ms))),
            gadget: UsbGadget::new("lesavka"),
            did_cycle: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
            camera_rt: std::sync::Arc::new(CameraRuntime::new()),
            upstream_media_rt: std::sync::Arc::new(UpstreamMediaRuntime::new()),
            calibration: std::sync::Arc::new(CalibrationStore::load(std::sync::Arc::new(
                UpstreamMediaRuntime::new(),
            ))),
            capture_power: CapturePowerManager::new(),
            eye_hubs: std::sync::Arc::new(
                tokio::sync::Mutex::new(std::collections::HashMap::new()),
            ),
        },
    )
}

async fn serve_handler(
    handler: Handler,
) -> (
    tokio::task::JoinHandle<()>,
    RelayClient<tonic::transport::Channel>,
) {
    let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind");
    let addr = listener.local_addr().expect("addr");
    drop(listener);

    let server = tokio::spawn(async move {
        let _ = tonic::transport::Server::builder()
            .add_service(RelayServer::new(handler))
            .serve(addr)
            .await;
    });
    let channel = connect_with_retry(addr).await;
    (server, RelayClient::new(channel))
}
