// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Copyright by contributors to this project.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)

//! An implementation of the [IETF Messaging Layer Security](https://messaginglayersecurity.rocks)
//! end-to-end encryption (E2EE) protocol.
//!
//! ## What is MLS?
//!
//! MLS is a new IETF end-to-end encryption standard that is designed to
//! provide transport agnostic, asynchronous, and highly performant
//! communication between a group of clients.
//!
//! ## MLS Protocol Features
//!
//! - Multi-party E2EE [group evolution](https://www.rfc-editor.org/rfc/rfc9420.html#name-cryptographic-state-and-evo)
//!   via a propose-then-commit mechanism.
//! - Asynchronous by design with pre-computed [key packages](https://www.rfc-editor.org/rfc/rfc9420.html#name-key-packages),
//!   allowing members to be added to a group while offline.
//! - Customizable credential system with built in support for X.509 certificates.
//! - [Extension system](https://www.rfc-editor.org/rfc/rfc9420.html#name-extensions)
//!   allowing for application specific data to be negotiated via the protocol.
//! - Strong forward secrecy and post compromise security.
//! - Crypto agility via support for multiple [cipher suites](https://www.rfc-editor.org/rfc/rfc9420.html#name-cipher-suites).
//! - Pre-shared key support.
//! - Subgroup branching.
//! - Group reinitialization for breaking changes such as protocol upgrades.
//!
//! ## Features
//!
//! - Easy to use client interface that can manage multiple MLS identities and groups.
//! - 100% RFC 9420 conformance with support for all default credential, proposal,
//!   and extension types.
//! - Support for WASM builds.
//! - Configurable storage for key packages, secrets and group state
//!   via traits along with provided "in memory" and SQLite implementations.
//! - Support for custom user proposal and extension types.
//! - Ability to create user defined credentials with custom validation
//!   routines that can bridge to existing credential schemes.
//! - OpenSSL and Rust Crypto based cipher suite implementations.
//! - Crypto agility with support for user defined cipher suite.
//! - Extensive test suite including security and interop focused tests against
//!   pre-computed test vectors.
//!
//! ## Crypto Providers
//!
//! For cipher suite descriptions see the RFC documentation [here](https://www.rfc-editor.org/rfc/rfc9420.html#name-mls-cipher-suites)
//!
//! | Name | Cipher Suites | X509 Support |
//! |------|---------------|--------------|
//! | OpenSSL | 1-7 | Stable |
//! | AWS-LC | 1,2,3,5,7 | Stable |
//! | Rust Crypto | 1,2,3 | ⚠️ Experimental |
//!
//! ## Security Notice
//!
//! This library has been validated for conformance to the RFC 9420 specification but has not yet received a full security audit by a 3rd party.

#![allow(clippy::enum_variant_names)]
#![allow(clippy::result_large_err)]
#![allow(clippy::nonstandard_macro_braces)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
extern crate alloc;

#[cfg(all(test, target_arch = "wasm32"))]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

#[cfg(all(test, target_arch = "wasm32"))]
use wasm_bindgen_test::wasm_bindgen_test as futures_test;

#[cfg(all(test, mls_build_async, not(target_arch = "wasm32")))]
use futures_test::test as futures_test;

#[cfg(test)]
macro_rules! hex {
    ($input:literal) => {
        hex::decode($input).expect("invalid hex value")
    };
}

#[cfg(test)]
macro_rules! load_test_case_json {
    ($name:ident, $generate:expr) => {
        load_test_case_json!($name, $generate, to_vec_pretty)
    };
    ($name:ident, $generate:expr, $to_json:ident) => {{
        #[cfg(any(target_arch = "wasm32", not(feature = "std")))]
        {
            // Do not remove `async`! (The goal of this line is to remove warnings
            // about `$generate` not being used. Actually calling it will make tests fail.)
            let _ = async { $generate };
            serde_json::from_slice(include_bytes!(concat!(
                env!("CARGO_MANIFEST_DIR"),
                "/test_data/",
                stringify!($name),
                ".json"
            )))
            .unwrap()
        }

        #[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
        {
            let path = concat!(
                env!("CARGO_MANIFEST_DIR"),
                "/test_data/",
                stringify!($name),
                ".json"
            );
            if !std::path::Path::new(path).exists() {
                std::fs::write(path, serde_json::$to_json(&$generate).unwrap()).unwrap();
            }
            serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap()
        }
    }};
}

mod cipher_suite {
    pub use mls_rs_core::crypto::CipherSuite;
}

pub use cipher_suite::CipherSuite;

mod protocol_version {
    pub use mls_rs_core::protocol_version::ProtocolVersion;
}

pub use protocol_version::ProtocolVersion;

pub mod client;
pub mod client_builder;
mod client_config;
/// Dependencies of [`CryptoProvider`] and [`CipherSuiteProvider`]
pub mod crypto;
/// Extension utilities and built-in extension types.
pub mod extension;
/// Tools to observe groups without being a member, useful
/// for server implementations.
#[cfg(feature = "external_client")]
#[cfg_attr(docsrs, doc(cfg(feature = "external_client")))]
pub mod external_client;
mod grease;
/// E2EE group created by a [`Client`].
pub mod group;
mod hash_reference;
/// Identity providers to use with [`ClientBuilder`](client_builder::ClientBuilder).
pub mod identity;
mod iter;
mod key_package;
pub(crate) mod map;
/// Pre-shared key support.
pub mod psk;
mod signer;
/// Storage providers to use with
/// [`ClientBuilder`](client_builder::ClientBuilder).
pub mod storage_provider;

pub use mls_rs_core::{
    crypto::{CipherSuiteProvider, CryptoProvider},
    group::GroupStateStorage,
    identity::IdentityProvider,
    key_package::KeyPackageStorage,
    psk::PreSharedKeyStorage,
};

/// Dependencies of [`MlsRules`].
pub mod mls_rules {
    pub use crate::group::{
        mls_rules::{
            CommitDirection, CommitOptions, CommitSource, DefaultMlsRules, EncryptionOptions,
        },
        proposal_filter::{ProposalBundle, ProposalInfo, ProposalSource},
    };

    #[cfg(feature = "by_ref_proposal")]
    pub use crate::group::proposal_ref::ProposalRef;
}

pub use mls_rs_core::extension::{Extension, ExtensionList};

pub use crate::{
    client::Client,
    group::{
        framing::{MlsMessage, MlsMessageDescription, WireFormat},
        mls_rules::MlsRules,
        Group,
    },
    key_package::{KeyPackage, KeyPackageRef},
};

/// Error types.
pub mod error {
    pub use crate::client::MlsError;
    pub use mls_rs_core::error::{AnyError, IntoAnyError};
    pub use mls_rs_core::extension::ExtensionError;
}

/// WASM compatible timestamp.
pub mod time {
    pub use mls_rs_core::time::*;
}

mod tree_kem;

pub use mls_rs_codec;

mod private {
    pub trait Sealed {}
}

use private::Sealed;

#[cfg(any(test, feature = "test_util"))]
#[doc(hidden)]
pub mod test_utils;

#[cfg(feature = "ffi")]
pub use safer_ffi_gen;
