//! Asynchronous ECMQV key exchange (optional feature).

#![allow(unused_imports)]

use alloc::vec::Vec;
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroizing;

use crate::{
    AgreementError, CurveParameters, EphemeralOnly, KdfParams, SharedInfo,
};
use crate::key_exchange::KeyExchange;

/// Asynchronous full ephemeral-only ECMQV handshake.
///
/// This function generates ephemeral keys for both parties, performs the
/// key exchange, and derives a shared secret.
///
/// # Parameters
/// - `rng`: cryptographically secure RNG.
/// - `params`: curve parameters.
/// - `alice_id`, `bob_id`: identifiers for domain separation.
/// - `session_data`: additional context.
/// - `kdf_params`: parameters for the KDF.
///
/// # Returns
/// The derived shared secret as a zeroized byte vector.
pub async fn async_ephemeral_only_handshake<R: RngCore + CryptoRng + Send>(
    rng: &mut R,
    params: CurveParameters,
    alice_id: &[u8],
    bob_id: &[u8],
    session_data: &[u8],
    kdf_params: KdfParams,
) -> Result<Zeroizing<Vec<u8>>, AgreementError> {
    let (alice_eph, alice_pub) = EphemeralOnly::generate_ephemeral(rng, &params)?;
    let (bob_eph, bob_pub) = EphemeralOnly::generate_ephemeral(rng, &params)?;

    let alice = EphemeralOnly::new(alice_eph, params);
    let bob = EphemeralOnly::new(bob_eph, params);

    let shared_info = SharedInfo {
        id_a: alice_id,
        id_b: bob_id,
        other_data: session_data,
    };

    let alice_secret = alice.agree(&bob_pub, None, &shared_info, kdf_params.clone())?;
    let bob_secret = bob.agree(&alice_pub, None, &shared_info, kdf_params)?;

    assert_eq!(alice_secret.as_slice(), bob_secret.as_slice());
    Ok(alice_secret)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{HashFunction, SharedInfo};
    use rand::rngs::OsRng;
    use tokio;

    #[test]
    fn test_async_ephemeral_only() {
        let rt = tokio::runtime::Builder::new_current_thread()
            .build()
            .expect("failed to create tokio runtime");
        rt.block_on(async {
            let mut rng = OsRng;
            let params = CurveParameters::Curve25519;
            let kdf_params = KdfParams {
                hash_fn: HashFunction::Sha256,
                output_len: 32,
                label: None,
                salt: None,
            };
            let result = async_ephemeral_only_handshake(
                &mut rng,
                params,
                b"alice",
                b"bob",
                b"test",
                kdf_params,
            )
            .await;
            assert!(result.is_ok());
            let secret = result.unwrap();
            assert_eq!(secret.len(), 32);
        });
    }
}