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

#[cfg(feature = "psk")]
use crate::psk::PreSharedKey;
#[cfg(any(feature = "secret_tree_access", feature = "private_message"))]
use crate::tree_kem::node::NodeIndex;
#[cfg(feature = "prior_epoch")]
use crate::{crypto::SignaturePublicKey, group::GroupContext, tree_kem::node::LeafIndex};
use alloc::vec::Vec;
use core::{
    fmt::{self, Debug},
    ops::Deref,
};
use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize};
use zeroize::Zeroizing;

#[cfg(all(feature = "prior_epoch", feature = "private_message"))]
use super::ciphertext_processor::GroupStateProvider;

#[cfg(any(feature = "secret_tree_access", feature = "private_message"))]
use crate::group::secret_tree::SecretTree;

#[cfg(feature = "prior_epoch")]
#[derive(Debug, Clone, MlsEncode, MlsDecode, MlsSize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct PriorEpoch {
    pub(crate) context: GroupContext,
    pub(crate) self_index: LeafIndex,
    pub(crate) secrets: EpochSecrets,
    pub(crate) signature_public_keys: Vec<Option<SignaturePublicKey>>,
    #[cfg(feature = "prior_epoch_membership_key")]
    pub(crate) membership_key: Vec<u8>,
}

#[cfg(feature = "prior_epoch")]
impl PriorEpoch {
    #[inline(always)]
    pub(crate) fn epoch_id(&self) -> u64 {
        self.context.epoch
    }

    #[inline(always)]
    pub(crate) fn group_id(&self) -> &[u8] {
        &self.context.group_id
    }
}

#[cfg(all(feature = "private_message", feature = "prior_epoch"))]
impl GroupStateProvider for PriorEpoch {
    fn group_context(&self) -> &GroupContext {
        &self.context
    }

    fn self_index(&self) -> LeafIndex {
        self.self_index
    }

    fn epoch_secrets_mut(&mut self) -> &mut EpochSecrets {
        &mut self.secrets
    }

    fn epoch_secrets(&self) -> &EpochSecrets {
        &self.secrets
    }
}

#[derive(Debug, Clone, PartialEq, MlsEncode, MlsDecode, MlsSize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct EpochSecrets {
    #[cfg(feature = "psk")]
    #[mls_codec(with = "mls_rs_codec::byte_vec")]
    pub(crate) resumption_secret: PreSharedKey,
    #[mls_codec(with = "mls_rs_codec::byte_vec")]
    pub(crate) sender_data_secret: SenderDataSecret,
    #[cfg(any(feature = "secret_tree_access", feature = "private_message"))]
    pub(crate) secret_tree: SecretTree<NodeIndex>,
}

#[derive(Clone, PartialEq, MlsEncode, MlsDecode, MlsSize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct SenderDataSecret(
    #[mls_codec(with = "mls_rs_codec::byte_vec")]
    #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::zeroizing_serde"))]
    Zeroizing<Vec<u8>>,
);

impl Debug for SenderDataSecret {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        mls_rs_core::debug::pretty_bytes(&self.0)
            .named("SenderDataSecret")
            .fmt(f)
    }
}

impl AsRef<[u8]> for SenderDataSecret {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Deref for SenderDataSecret {
    type Target = Vec<u8>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<Vec<u8>> for SenderDataSecret {
    fn from(bytes: Vec<u8>) -> Self {
        Self(Zeroizing::new(bytes))
    }
}

impl From<Zeroizing<Vec<u8>>> for SenderDataSecret {
    fn from(bytes: Zeroizing<Vec<u8>>) -> Self {
        Self(bytes)
    }
}

#[cfg(test)]
pub(crate) mod test_utils {
    use mls_rs_core::crypto::CipherSuiteProvider;

    use super::*;
    use crate::cipher_suite::CipherSuite;
    use crate::crypto::test_utils::test_cipher_suite_provider;

    #[cfg(any(feature = "secret_tree_access", feature = "private_message"))]
    use crate::group::secret_tree::test_utils::get_test_tree;

    #[cfg(feature = "prior_epoch")]
    use crate::group::test_utils::get_test_group_context_with_id;

    use crate::group::test_utils::random_bytes;

    pub(crate) fn get_test_epoch_secrets(cipher_suite: CipherSuite) -> EpochSecrets {
        let cs_provider = test_cipher_suite_provider(cipher_suite);

        #[cfg(any(feature = "secret_tree_access", feature = "private_message"))]
        let secret_tree = get_test_tree(random_bytes(cs_provider.kdf_extract_size()), 2);

        EpochSecrets {
            #[cfg(feature = "psk")]
            resumption_secret: random_bytes(cs_provider.kdf_extract_size()).into(),
            sender_data_secret: random_bytes(cs_provider.kdf_extract_size()).into(),
            #[cfg(any(feature = "secret_tree_access", feature = "private_message"))]
            secret_tree,
        }
    }

    #[cfg(feature = "prior_epoch")]
    pub(crate) fn get_test_epoch_with_id(
        group_id: Vec<u8>,
        cipher_suite: CipherSuite,
        id: u64,
    ) -> PriorEpoch {
        PriorEpoch {
            context: get_test_group_context_with_id(group_id, id, cipher_suite),
            self_index: LeafIndex::unchecked(0),
            secrets: get_test_epoch_secrets(cipher_suite),
            signature_public_keys: Default::default(),
            #[cfg(feature = "prior_epoch_membership_key")]
            membership_key: Default::default(),
        }
    }
}
