namespace ZB.MOM.WW.Secrets.Abstractions;

/// <summary>
/// Envelope-encrypts and decrypts secret material. Each secret is sealed under a per-secret
/// data-encryption key (DEK) that is itself wrapped by the master key (KEK) from an
/// <see cref="IMasterKeyProvider"/>. The secret name is bound as additional authenticated
/// data (AAD) so ciphertext cannot be silently relocated to a different name.
/// </summary>
public interface ISecretCipher
{
    /// <summary>
    /// Envelope-encrypts <paramref name="plaintext"/>, binding it to <paramref name="name"/>
    /// as additional authenticated data.
    /// </summary>
    /// <param name="name">The secret name, bound as AAD.</param>
    /// <param name="plaintext">The secret plaintext to seal.</param>
    /// <param name="contentType">How the plaintext should be interpreted by consumers.</param>
    /// <returns>
    /// A <see cref="StoredSecret"/> carrying the ciphertext, nonce, tag, and wrapped DEK, with
    /// <see cref="StoredSecret.Revision"/> 0 and default timestamps — the store stamps the
    /// revision and timestamps on persist.
    /// </returns>
    StoredSecret Encrypt(SecretName name, string plaintext, SecretContentType contentType);

    /// <summary>
    /// Decrypts the value carried by a stored row, verifying its authentication tag and the
    /// name it was bound to.
    /// </summary>
    /// <param name="secret">The stored row to decrypt.</param>
    /// <returns>The decrypted plaintext.</returns>
    /// <exception cref="SecretDecryptionException">
    /// The authentication tag fails to verify, or the row references an unknown or unavailable KEK.
    /// </exception>
    string Decrypt(StoredSecret secret);

    /// <summary>
    /// Re-wraps the data-encryption key (DEK) of an existing row from <paramref name="oldKek"/> to
    /// <paramref name="newKek"/> — the KEK-rotation primitive. The DEK is unwrapped under the old
    /// master key and re-wrapped under the new one; the sealed body
    /// (<see cref="StoredSecret.Ciphertext"/>/<see cref="StoredSecret.Nonce"/>/<see cref="StoredSecret.Tag"/>)
    /// is <b>never</b> re-encrypted, and <see cref="StoredSecret.Revision"/>, the timestamps, the
    /// tombstone flag, and every other field are preserved verbatim. Only
    /// <see cref="StoredSecret.WrappedDek"/>/<see cref="StoredSecret.WrapNonce"/>/<see cref="StoredSecret.WrapTag"/>
    /// and <see cref="StoredSecret.KekId"/> change. Plaintext is never exposed.
    /// </summary>
    /// <remarks>
    /// This operation is <i>provider-explicit</i>: it uses only the two supplied providers and does
    /// not consult whatever KEK the cipher instance was constructed with, because a rotation spans
    /// two keys. The caller must have already established that <paramref name="secret"/> is wrapped
    /// by <paramref name="oldKek"/> (the row's <see cref="StoredSecret.KekId"/> equals
    /// <see cref="IMasterKeyProvider.KekId"/> of <paramref name="oldKek"/>); a mismatch fails closed.
    /// </remarks>
    /// <param name="secret">The stored row whose DEK should be re-wrapped.</param>
    /// <param name="oldKek">The provider for the KEK that currently wraps the row's DEK.</param>
    /// <param name="newKek">The provider for the KEK the DEK should be re-wrapped under.</param>
    /// <returns>
    /// A copy of <paramref name="secret"/> with the DEK re-wrapped under <paramref name="newKek"/>
    /// and <see cref="StoredSecret.KekId"/> set to the new KEK id; all other fields unchanged.
    /// </returns>
    /// <exception cref="SecretDecryptionException">
    /// The row is not wrapped by <paramref name="oldKek"/>, or the DEK fails to unwrap (wrong old
    /// key material or a tampered wrap).
    /// </exception>
    StoredSecret Rewrap(StoredSecret secret, IMasterKeyProvider oldKek, IMasterKeyProvider newKek);
}
