//! Ephemeral-only ECMQV key exchange mode.
//!
//! This mode uses only ephemeral keys (no static keys). It is suitable for
//! one-time session key agreement where forward secrecy is required but
//! authentication is not needed. The protocol reduces to standard ECDH
//! but retains the ECMQV structure for compatibility.

use rand_core::{CryptoRng, RngCore};

use crate::error::AgreementError;
use crate::key::{EphemeralKey, PublicKey};
use crate::key_exchange::{KeyExchange, perform_agreement, SharedSecret};
use crate::kdf::{KdfParams, SharedInfo};
use crate::params::CurveParameters;

/// Ephemeral-only ECMQV key exchange.
///
/// Both parties generate fresh ephemeral keys for each session.
/// No static keys are involved, providing perfect forward secrecy
/// but no authentication.
///
/// This is suitable for scenarios where parties do not have long-term
/// certificates or where authentication is handled by other means.
///
/// # Example
/// ```
/// # use ecmqv_rs::{EphemeralOnly, KeyExchange, CurveParameters, KdfParams, SharedInfo, HashFunction};
/// # use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let params = CurveParameters::Curve25519;
///
/// let (alice_eph, alice_pub) = EphemeralOnly::generate_ephemeral(&mut rng, &params).unwrap();
/// let (bob_eph, bob_pub) = EphemeralOnly::generate_ephemeral(&mut rng, &params).unwrap();
///
/// let alice = EphemeralOnly::new(alice_eph, params);
/// let bob = EphemeralOnly::new(bob_eph, params);
///
/// let info = SharedInfo {
///     id_a: b"alice",
///     id_b: b"bob",
///     other_data: b"session123",
/// };
/// let kdf = KdfParams {
///     hash_fn: HashFunction::Sha256,
///     output_len: 32,
///     label: None,
///     salt: None,
/// };
///
/// let alice_secret = alice.agree(&bob_pub, None, &info, kdf.clone()).unwrap();
/// let bob_secret = bob.agree(&alice_pub, None, &info, kdf).unwrap();
/// assert_eq!(alice_secret.as_slice(), bob_secret.as_slice());
/// ```
pub struct EphemeralOnly {
    own_ephemeral: EphemeralKey,
    params: CurveParameters,
}

impl EphemeralOnly {
    /// Creates a new `EphemeralOnly` instance from an existing ephemeral key.
    ///
    /// This is typically used after generating the ephemeral key with
    /// `KeyExchange::generate_ephemeral`.
    ///
    /// # Parameters
    /// - `own_ephemeral`: the local ephemeral key pair (secret + public).
    /// - `params`: the curve parameters for the key.
    pub fn new(own_ephemeral: EphemeralKey, params: CurveParameters) -> Self {
        Self { own_ephemeral, params }
    }
}

impl KeyExchange for EphemeralOnly {
    type SharedSecret = SharedSecret;

    /// Generates a fresh ephemeral key pair for the specified curve.
    ///
    /// # Errors
    /// Returns `UnsupportedCurve` if the curve is not enabled.
    fn generate_ephemeral<R: RngCore + CryptoRng>(
        rng: &mut R,
        params: &CurveParameters,
    ) -> Result<(EphemeralKey, PublicKey), AgreementError> {
        let ephemeral = EphemeralKey::generate(rng, *params)?;
        let public = ephemeral.public();
        Ok((ephemeral, public))
    }

    /// Performs ephemeral-only key agreement with a peer.
    ///
    /// The `peer_static` parameter is ignored (and should be `None`) because
    /// this mode does not use static keys.
    ///
    /// # Errors
    /// See `KeyExchange::agree` for possible error conditions.
    fn agree(
        &self,
        peer_ephemeral: &PublicKey,
        _peer_static: Option<&PublicKey>, // not used in ephemeral-only mode
        shared_info: &SharedInfo,
        kdf_params: KdfParams,
    ) -> Result<Self::SharedSecret, AgreementError> {
        // Ephemeral-only mode never uses static keys; ignore peer_static.
        perform_agreement(
            &self.own_ephemeral,
            None, // no own static key
            peer_ephemeral,
            None, // no peer static key
            &self.params,
            shared_info,
            kdf_params,
        )
    }
}

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

    #[test]
    fn test_ephemeral_only_roundtrip() {
        let params = CurveParameters::Curve25519;
        let mut rng = OsRng;

        let (alice_eph, alice_pub) =
            EphemeralOnly::generate_ephemeral(&mut rng, &params).unwrap();
        let (bob_eph, bob_pub) =
            EphemeralOnly::generate_ephemeral(&mut rng, &params).unwrap();

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

        let shared_info = SharedInfo {
            id_a: b"alice",
            id_b: b"bob",
            other_data: b"test",
        };
        let kdf_params = KdfParams {
            hash_fn: HashFunction::Sha256,
            output_len: 32,
            label: None,
            salt: None,
        };

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

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