use std::net::TcpListener;
use wtx::{
  executor::StdRuntime,
  misc::Uri,
  rng::{ChaCha20, CryptoSeedableRng},
  sync::Arc,
  tls::{CipherSuite, NamedGroup, TlsAcceptor, TlsConfig, TlsConnectorBuilder, TlsModeVerified},
};

const CIPHER_SUITE: CipherSuite = cfg_select! {
  feature = "aes128gcmsha256" => CipherSuite::Aes128GcmSha256,
  feature = "aes256gcmsha384" => CipherSuite::Aes256GcmSha384,
  feature = "chacha20poly1305sha256" => CipherSuite::Chacha20Poly1305Sha256,
  _ => compile_error!("Select a cipher suite!")
};
const NAMED_GROUP: NamedGroup = cfg_select! {
  feature = "secp256r1" => NamedGroup::Secp256r1,
  feature = "secp384r1" => NamedGroup::Secp384r1,
  feature = "x25519" => NamedGroup::X25519,
  _ => compile_error!("Select a named group!")
};
const TM: TlsModeVerified = TlsModeVerified::new();

#[wtx::main]
async fn main(runtime: Arc<StdRuntime>) -> wtx::Result<()> {
  let ca = wtx::misc::var("CA")?.into_bytes();
  let pk = wtx::misc::var("PK")?.into_bytes();
  let sk = wtx::misc::var("SK")?.into_bytes();

  let uri = Uri::new("127.0.0.1:8080");
  let mut client_rng = ChaCha20::from_std_random()?;
  let mut server_rng = ChaCha20::from_crypto_rng(&mut client_rng)?;

  let listener = TcpListener::bind(uri.hostname_with_implied_port())?;

  let client_jh = runtime.spawn(async move {
    let mut tls_config = TlsConfig::from_trust_anchors_pem(TM, [ca.as_slice()])?;
    tls_config.cipher_suites_mut().clear();
    tls_config.cipher_suites_mut().push(CIPHER_SUITE)?;
    tls_config.named_groups_mut().clear();
    tls_config.named_groups_mut().push(NAMED_GROUP)?;
    let _out =
      TlsConnectorBuilder::std(uri).build(tls_config, &mut client_rng).await?.connect().await?;
    wtx::Result::Ok(())
  })?;

  let tcp_stream = listener.accept()?.0;
  let tls_config = TlsConfig::from_keys_pem(TM, pk.as_slice(), sk.as_slice())?;
  let _out = TlsAcceptor::new(tls_config, &mut server_rng, tcp_stream).accept().await?;

  if let Err(err) = client_jh.await {
    panic!("Client Error: {err}");
  }

  Ok(())
}
