namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;

/// <summary>
/// AES-GCM encryption envelope metadata for a bundle's content payload. Carried on
/// the bundle manifest (plaintext) so the importer can derive the per-bundle key and
/// initialise the cipher without prior knowledge of the passphrase.
/// <para>
/// Invariants are enforced in the constructor so a malformed envelope
/// (unknown algorithm, unsupported KDF, weak iteration count, null salt/IV) is
/// rejected at the type boundary rather than failing inside
/// <see cref="System.Security.Cryptography.AesGcm"/> with a misleading exception.
/// </para>
/// </summary>
public sealed record EncryptionMetadata
{
    /// <summary>The only AES symmetric algorithm the bundle format supports.</summary>
    public const string SupportedAlgorithm = "AES-256-GCM";

    /// <summary>The only key-derivation function the bundle format supports.</summary>
    public const string SupportedKdf = "PBKDF2-SHA256";

    /// <summary>
    /// PBKDF2 iteration-count floor — OWASP's documented minimum. The Transport design
    /// doc specifies <c>600_000</c> as the production value; this constant is the hard
    /// reject threshold below which the envelope is treated as malformed.
    /// </summary>
    public const int MinPbkdf2Iterations = 100_000;

    /// <summary>
    /// PBKDF2 iteration-count ceiling — guards against a hostile bundle declaring an
    /// absurd iteration count that would burn CPU on every unlock attempt.
    /// </summary>
    public const int MaxPbkdf2Iterations = 10_000_000;

    /// <summary>
    /// Initializes a new <see cref="EncryptionMetadata"/>. Each argument is validated
    /// against the documented contract; invalid values throw <see cref="ArgumentException"/>
    /// naming the offending field.
    /// </summary>
    /// <param name="Algorithm">Symmetric algorithm name; must equal <see cref="SupportedAlgorithm"/>.</param>
    /// <param name="Kdf">Key-derivation function name; must equal <see cref="SupportedKdf"/>.</param>
    /// <param name="Iterations">PBKDF2 iteration count; must lie in [<see cref="MinPbkdf2Iterations"/>, <see cref="MaxPbkdf2Iterations"/>].</param>
    /// <param name="SaltB64">Base64-encoded PBKDF2 salt; must be non-null (may be empty for the seed pattern used by BundleSerializer.Pack).</param>
    /// <param name="IvB64">Base64-encoded AES-GCM IV; must be non-null (may be empty for the seed pattern used by BundleSerializer.Pack).</param>
    /// <exception cref="ArgumentException">Thrown when any field violates the documented contract.</exception>
    public EncryptionMetadata(string Algorithm, string Kdf, int Iterations, string SaltB64, string IvB64)
    {
        if (!string.Equals(Algorithm, SupportedAlgorithm, StringComparison.Ordinal))
        {
            throw new ArgumentException(
                $"{nameof(Algorithm)} must be '{SupportedAlgorithm}'; got '{Algorithm}'.",
                nameof(Algorithm));
        }

        if (!string.Equals(Kdf, SupportedKdf, StringComparison.Ordinal))
        {
            throw new ArgumentException(
                $"{nameof(Kdf)} must be '{SupportedKdf}'; got '{Kdf}'.",
                nameof(Kdf));
        }

        if (Iterations < MinPbkdf2Iterations || Iterations > MaxPbkdf2Iterations)
        {
            throw new ArgumentException(
                $"{nameof(Iterations)} must be between {MinPbkdf2Iterations} and {MaxPbkdf2Iterations}; got {Iterations}.",
                nameof(Iterations));
        }

        ArgumentNullException.ThrowIfNull(SaltB64);
        ArgumentNullException.ThrowIfNull(IvB64);

        this.Algorithm = Algorithm;
        this.Kdf = Kdf;
        this.Iterations = Iterations;
        this.SaltB64 = SaltB64;
        this.IvB64 = IvB64;
    }

    /// <summary>Symmetric algorithm name (always <see cref="SupportedAlgorithm"/>).</summary>
    public string Algorithm { get; init; }

    /// <summary>Key-derivation function name (always <see cref="SupportedKdf"/>).</summary>
    public string Kdf { get; init; }

    /// <summary>PBKDF2 iteration count.</summary>
    public int Iterations { get; init; }

    /// <summary>Base64-encoded PBKDF2 salt.</summary>
    public string SaltB64 { get; init; }

    /// <summary>Base64-encoded AES-GCM IV.</summary>
    public string IvB64 { get; init; }
}
