use mysql_async::prelude::Queryable;
use tici::{common::connection_pool::TiDBConnectionPool, config::TiDBServerConfig};
use tici_system::readable::ReadableDuration;

#[tokio::test]
async fn test_pool() {
    let mut config = TiDBServerConfig::new_from_urls(&["mysql://root@127.0.0.1:4000/test"]);
    config.tidb_connection_retry_count = 0;
    let mut pool = TiDBConnectionPool::new(&config);

    for i in 0..4 {
        let mut c = pool.get_conn().await.unwrap();
        let s: Vec<String> = c.query("SELECT tidb_version()").await.unwrap();
        for v in s {
            println!("tidb version={:?} {}", v, i);
        }
    }

    // Append a new TiDB server to the pool
    // Now we use the same TiDB server for testing
    pool.append_server("mysql://root@127.0.0.1:4000/test")
        .await
        .unwrap();
    for i in 0..4 {
        let mut c = pool.get_conn().await.unwrap();
        let s: Vec<String> = c.query("SELECT tidb_version()").await.unwrap();
        for v in s {
            println!("tidb version={:?} {}", v, i);
        }
    }

    // stop the connection pool
    pool.stop().await.unwrap();

    // If the pool is stopped, get_conn should return an error
    for _ in 0..4 {
        let e = pool.get_conn().await;
        assert!(e.is_err());
    }
}

#[tokio::test]
async fn test_blacklisted() {
    let c = TiDBServerConfig {
        dsns: vec!["mysql://root@127.0.0.1:4000/test".to_string()],
        blacklist_tidb_server_threshold: 2,
        blacklist_duration: ReadableDuration::secs(1),
        cert_path: "".to_string(),
        tidb_connection_retry_count: 0,
        ..Default::default()
    };
    let mut pool = TiDBConnectionPool::new(&c);

    fail::cfg("tidb_backend_mock_get_conn_failed", "return(true)").unwrap();

    pool.get_conn().await.unwrap_err();
    fail::cfg("tidb_backend_mock_get_conn_failed", "return(false)").unwrap();
    // Only failed once, not blacklisted
    pool.get_conn().await.unwrap();

    fail::cfg("tidb_backend_mock_get_conn_failed", "return(true)").unwrap();
    pool.get_conn().await.unwrap_err();
    pool.get_conn().await.unwrap_err();
    fail::remove("tidb_backend_mock_get_conn_failed");
    // Must wait for blacklist_duration
    pool.get_conn().await.unwrap_err();

    tokio::time::sleep(std::time::Duration::from_millis(1500)).await;
    pool.get_conn().await.unwrap();

    pool.stop().await.unwrap();
}

#[tokio::test]
async fn test_demote() {
    let c = TiDBServerConfig {
        dsns: vec!["mysql://root@127.0.0.1:4000/test".to_string()],
        blacklist_tidb_server_threshold: 2,
        blacklist_duration: ReadableDuration::secs(1),
        cert_path: "".to_string(),
        tidb_connection_retry_count: 0,
        ..Default::default()
    };
    let mut pool = TiDBConnectionPool::new(&c);

    pool.append_server("mysql://root@127.0.0.1:9999/test")
        .await
        .unwrap();

    pool.get_conn().await.unwrap();
    pool.get_conn().await.unwrap_err();
    pool.demoting_servers(vec!["mysql://root@127.0.0.1:9999/test"].as_slice())
        .await
        .unwrap();
    pool.get_conn().await.unwrap();
    pool.demoting_servers(vec!["mysql://root@127.0.0.1:4000/test"].as_slice())
        .await
        .unwrap();
    pool.get_conn().await.unwrap_err();

    pool.stop().await.unwrap();
}
