//! Sealed sender: embed sender identity + Ed25519 signature inside the MLS
//! application payload so recipients can verify the sender from decrypted
//! content, independent of MLS framing.
//!
//! # Wire format
//!
//! ```text
//! [magic: 1 byte (0x53 = 'S')]
//! [sender_identity_key: 32 bytes (Ed25519 public key)]
//! [signature: 64 bytes (Ed25519)]
//! [inner_payload: variable (the original app_message bytes)]
//! ```
//!
//! The signature covers: `magic || sender_identity_key || inner_payload`.
//! Total overhead: 1 + 32 + 64 = 97 bytes per message.

use crate::error::CoreError;
use crate::identity::IdentityKeypair;

/// Magic byte identifying a sealed sender envelope.
pub const SEALED_MAGIC: u8 = 0x53; // 'S'

/// Fixed overhead: magic(1) + sender_key(32) + signature(64).
const SEALED_OVERHEAD: usize = 1 + 32 + 64;

/// Wrap an app_message payload in a sealed sender envelope.
///
/// Signs `magic || sender_key || payload` with the sender's Ed25519 key.
pub fn seal(identity: &IdentityKeypair, app_message_bytes: &[u8]) -> Vec<u8> {
    let sender_key = identity.public_key_bytes();

    // Build signing input
    let mut sign_input = Vec::with_capacity(1 + 32 + app_message_bytes.len());
    sign_input.push(SEALED_MAGIC);
    sign_input.extend_from_slice(&sender_key);
    sign_input.extend_from_slice(app_message_bytes);

    let signature = identity.sign_raw(&sign_input);

    let mut out = Vec::with_capacity(SEALED_OVERHEAD + app_message_bytes.len());
    out.push(SEALED_MAGIC);
    out.extend_from_slice(&sender_key);
    out.extend_from_slice(&signature);
    out.extend_from_slice(app_message_bytes);
    out
}

/// Unseal: verify the Ed25519 signature, return `(sender_identity_key, inner_app_message_bytes)`.
pub fn unseal(bytes: &[u8]) -> Result<([u8; 32], Vec<u8>), CoreError> {
    if bytes.len() < SEALED_OVERHEAD {
        return Err(CoreError::AppMessage(
            "sealed sender envelope too short".into(),
        ));
    }

    if bytes[0] != SEALED_MAGIC {
        return Err(CoreError::AppMessage(format!(
            "sealed sender: expected magic 0x{:02X}, got 0x{:02X}",
            SEALED_MAGIC, bytes[0]
        )));
    }

    let mut sender_key = [0u8; 32];
    sender_key.copy_from_slice(&bytes[1..33]);

    let mut signature = [0u8; 64];
    signature.copy_from_slice(&bytes[33..97]);

    let inner_payload = &bytes[97..];

    // Reconstruct signing input: magic || sender_key || inner_payload
    let mut sign_input = Vec::with_capacity(1 + 32 + inner_payload.len());
    sign_input.push(SEALED_MAGIC);
    sign_input.extend_from_slice(&sender_key);
    sign_input.extend_from_slice(inner_payload);

    IdentityKeypair::verify_raw(&sender_key, &sign_input, &signature)?;

    Ok((sender_key, inner_payload.to_vec()))
}

/// Check if bytes start with the sealed sender magic byte.
pub fn is_sealed(bytes: &[u8]) -> bool {
    bytes.first() == Some(&SEALED_MAGIC)
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;

    #[test]
    fn seal_unseal_round_trip() {
        let identity = IdentityKeypair::generate();
        let payload = b"hello sealed sender";
        let sealed = seal(&identity, payload);
        assert!(is_sealed(&sealed));

        let (sender_key, inner) = unseal(&sealed).unwrap();
        assert_eq!(sender_key, identity.public_key_bytes());
        assert_eq!(inner, payload);
    }

    #[test]
    fn unseal_tampered_payload_fails() {
        let identity = IdentityKeypair::generate();
        let payload = b"hello";
        let mut sealed = seal(&identity, payload);
        // Tamper with the inner payload
        if let Some(last) = sealed.last_mut() {
            *last ^= 0xFF;
        }
        assert!(unseal(&sealed).is_err());
    }

    #[test]
    fn unseal_wrong_sender_fails() {
        let alice = IdentityKeypair::generate();
        let bob = IdentityKeypair::generate();
        let payload = b"from alice";
        let mut sealed = seal(&alice, payload);
        // Replace sender key with Bob's
        let bob_key = bob.public_key_bytes();
        sealed[1..33].copy_from_slice(&bob_key);
        assert!(unseal(&sealed).is_err());
    }

    #[test]
    fn unseal_too_short_fails() {
        assert!(unseal(&[SEALED_MAGIC; 10]).is_err());
    }

    #[test]
    fn unseal_wrong_magic_fails() {
        let identity = IdentityKeypair::generate();
        let mut sealed = seal(&identity, b"test");
        sealed[0] = 0x00;
        assert!(unseal(&sealed).is_err());
    }

    #[test]
    fn non_sealed_detected() {
        assert!(!is_sealed(b"\x01\x01hello"));
        assert!(is_sealed(&[SEALED_MAGIC, 0, 0]));
    }

    #[test]
    fn empty_payload_round_trip() {
        let identity = IdentityKeypair::generate();
        let sealed = seal(&identity, b"");
        let (sender_key, inner) = unseal(&sealed).unwrap();
        assert_eq!(sender_key, identity.public_key_bytes());
        assert!(inner.is_empty());
    }
}
