pub mod tx;
pub mod validator_sets;
pub mod in_instructions;
pub mod dex;

#[macro_export]
macro_rules! serai_test {
  ($($name: ident: $test: expr)*) => {
    $(
      #[tokio::test]
      async fn $name() {
        use std::collections::HashMap;
        use dockertest::{
          PullPolicy, StartPolicy, LogOptions, LogAction, LogPolicy, LogSource, Image,
          TestBodySpecification, DockerTest,
        };

        serai_docker_tests::build("serai".to_string());

        let handle = concat!("serai_client-serai_node-", stringify!($name));

        let composition = TestBodySpecification::with_image(
          Image::with_repository("serai-dev-serai").pull_policy(PullPolicy::Never),
        )
        .replace_cmd(vec![
          "serai-node".to_string(),
          "--dev".to_string(),
          "--unsafe-rpc-external".to_string(),
          "--rpc-cors".to_string(),
          "all".to_string(),
        ])
        .replace_env(
          HashMap::from([
            ("RUST_LOG".to_string(), "runtime=debug".to_string()),
            ("KEY".to_string(), " ".to_string()),
          ])
        )
        .set_publish_all_ports(true)
        .set_handle(handle)
        .set_start_policy(StartPolicy::Strict)
        .set_log_options(Some(LogOptions {
          action: LogAction::Forward,
          policy: LogPolicy::Always,
          source: LogSource::Both,
        }));

        let mut test = DockerTest::new().with_network(dockertest::Network::Isolated);
        test.provide_container(composition);
        test.run_async(|ops| async move {
          // Sleep until the Substrate RPC starts
          let serai_rpc = ops.handle(handle).host_port(9944).unwrap();
          let serai_rpc = format!("http://{}:{}", serai_rpc.0, serai_rpc.1);
          // Bound execution to 60 seconds
          for _ in 0 .. 60 {
            tokio::time::sleep(core::time::Duration::from_secs(1)).await;
            let Ok(client) = Serai::new(serai_rpc.clone()).await else { continue };
            if client.latest_finalized_block_hash().await.is_err() {
              continue;
            }
            break;
          }
          #[allow(clippy::redundant_closure_call)]
          $test(Serai::new(serai_rpc).await.unwrap()).await;
        }).await;
      }
    )*
  }
}

#[macro_export]
macro_rules! serai_test_fast_epoch {
  ($($name: ident: $test: expr)*) => {
    $(
      #[tokio::test]
      async fn $name() {
        use std::collections::HashMap;
        use dockertest::{
          PullPolicy, StartPolicy, LogOptions, LogAction, LogPolicy, LogSource, Image,
          TestBodySpecification, DockerTest,
        };

        serai_docker_tests::build("serai-fast-epoch".to_string());

        let handle = concat!("serai_client-serai_node-", stringify!($name));

        let composition = TestBodySpecification::with_image(
          Image::with_repository("serai-dev-serai-fast-epoch").pull_policy(PullPolicy::Never),
        )
        .replace_cmd(vec![
          "serai-node".to_string(),
          "--dev".to_string(),
          "--unsafe-rpc-external".to_string(),
          "--rpc-cors".to_string(),
          "all".to_string(),
        ])
        .replace_env(
          HashMap::from([
            ("RUST_LOG".to_string(), "runtime=debug".to_string()),
            ("KEY".to_string(), " ".to_string()),
          ])
        )
        .set_publish_all_ports(true)
        .set_handle(handle)
        .set_start_policy(StartPolicy::Strict)
        .set_log_options(Some(LogOptions {
          action: LogAction::Forward,
          policy: LogPolicy::Always,
          source: LogSource::Both,
        }));

        let mut test = DockerTest::new().with_network(dockertest::Network::Isolated);
        test.provide_container(composition);
        test.run_async(|ops| async move {
          // Sleep until the Substrate RPC starts
          let serai_rpc = ops.handle(handle).host_port(9944).unwrap();
          let serai_rpc = format!("http://{}:{}", serai_rpc.0, serai_rpc.1);
          // Bound execution to 60 seconds
          for _ in 0 .. 60 {
            tokio::time::sleep(core::time::Duration::from_secs(1)).await;
            let Ok(client) = Serai::new(serai_rpc.clone()).await else { continue };
            if client.latest_finalized_block_hash().await.is_err() {
              continue;
            }
            break;
          }
          #[allow(clippy::redundant_closure_call)]
          $test(Serai::new(serai_rpc).await.unwrap()).await;
        }).await;
      }
    )*
  }
}
