#![cfg(feature = "rpc")]

use std::sync::Arc;
use std::time::Duration;

use armdb::armour::Db;
use armdb::{CollectionMeta, Config, NoHook, RapiraCodec};
use armour_core::GetType;
use armour_rpc::RpcClient;
use rapira::Rapira;
use tempfile::tempdir;

#[derive(Clone, Debug, Rapira, GetType)]
struct TestUser {
    id: u64,
    name: String,
}

impl CollectionMeta for TestUser {
    type SelfId = [u8; 8];
    const NAME: &'static str = "test_users_schema";
    const VERSION: u16 = 7;
}

#[test]
fn get_schema_roundtrip_uds() {
    let db_dir = tempdir().unwrap();
    let sock_dir = tempdir().unwrap();
    let sock = sock_dir.path().join("armdb.sock");

    let db = Arc::new(Db::open(db_dir.path()).unwrap());
    let _tree = db
        .open_typed_tree::<TestUser, RapiraCodec, _>(Config::test(), NoHook, &[])
        .unwrap();

    db.listen_uds(sock.clone());

    // wait synchronously for the listener thread to bind.
    for _ in 0..100 {
        if sock.exists() {
            break;
        }
        std::thread::sleep(Duration::from_millis(20));
    }
    assert!(sock.exists(), "UDS socket never appeared");

    compio::runtime::RuntimeBuilder::new()
        .build()
        .unwrap()
        .block_on(async {
            let mut client = RpcClient::connect_uds_by_name(&sock, TestUser::NAME)
                .await
                .unwrap();
            let schema = client.get_schema().await.unwrap();
            assert_eq!(schema.name, TestUser::NAME);
            assert_eq!(schema.version, TestUser::VERSION);
            assert_eq!(schema.typ, TestUser::TYPE);
            assert_eq!(schema.key_scheme, <[u8; 8] as armdb::Key>::KEY_SCHEME);
        });
}

#[test]
fn get_schema_not_found() {
    let db_dir = tempdir().unwrap();
    let sock_dir = tempdir().unwrap();
    let sock = sock_dir.path().join("armdb.sock");

    let db = Arc::new(Db::open(db_dir.path()).unwrap());
    let _tree = db
        .open_typed_tree::<TestUser, RapiraCodec, _>(Config::test(), NoHook, &[])
        .unwrap();

    db.listen_uds(sock.clone());

    for _ in 0..100 {
        if sock.exists() {
            break;
        }
        std::thread::sleep(Duration::from_millis(20));
    }
    assert!(sock.exists(), "UDS socket never appeared");

    compio::runtime::RuntimeBuilder::new()
        .build()
        .unwrap()
        .block_on(async {
            // Bind under a collection name that doesn't exist on the server.
            let mut client = RpcClient::connect_uds_by_name(&sock, "nonexistent_coll")
                .await
                .unwrap();
            let err = client.get_schema().await;
            assert!(err.is_err(), "expected error for unknown collection");
        });
}
