using ZB.MOM.WW.Secrets.Abstractions;
using ZB.MOM.WW.Secrets.MasterKey;

namespace ZB.MOM.WW.Secrets.Cli.Interactive;

/// <summary>
/// An <see cref="IMasterKeyProvider"/> over master-key material an operator pastes at the console —
/// the interactive lockout-recovery path when the configured KEK source (an environment variable or a
/// file) is not visible in the CLI's own shell.
/// </summary>
/// <remarks>
/// Subclasses <see cref="MasterKeyProviderBase"/> so the non-secret <see cref="IMasterKeyProvider.KekId"/>
/// is derived by the exact same scheme every other provider uses. That interop is load-bearing: a key
/// entered here must resolve to the same <c>kek_id</c> the service wrote rows under, or every decrypt
/// and re-wrap fails closed on a KEK mismatch. Pinned by
/// <c>LiteralMasterKeyProviderTests.Derives_same_kekid_as_environment_provider_for_same_material</c>.
/// </remarks>
public sealed class LiteralMasterKeyProvider : MasterKeyProviderBase
{
    private readonly byte[] _key;

    /// <summary>
    /// Creates the provider from base64-encoded 32-byte key material, validated eagerly so a bad paste
    /// is rejected at entry rather than on the first decrypt.
    /// </summary>
    /// <param name="base64Key">The base64-encoded 32-byte master key (KEK).</param>
    /// <param name="kekId">
    /// An optional explicit key id used verbatim as <see cref="IMasterKeyProvider.KekId"/>; when
    /// <see langword="null"/> or empty the id is derived from the key material, matching the other
    /// providers.
    /// </param>
    /// <exception cref="ArgumentException">
    /// <paramref name="base64Key"/> is null/whitespace, is not valid base64, or does not decode to
    /// exactly 32 bytes.
    /// </exception>
    public LiteralMasterKeyProvider(string base64Key, string? kekId = null)
        : base(new MasterKeyOptions { KekId = kekId })
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(base64Key);

        byte[] decoded;
        try
        {
            decoded = Convert.FromBase64String(base64Key.Trim());
        }
        catch (FormatException ex)
        {
            throw new ArgumentException(
                $"Master key is not valid base64: {ex.Message}", nameof(base64Key), ex);
        }

        if (decoded.Length != KeySizeBytes)
        {
            throw new ArgumentException(
                $"Master key must be exactly {KeySizeBytes} bytes (AES-256) but decoded to {decoded.Length} bytes.",
                nameof(base64Key));
        }

        _key = decoded;
    }

    /// <inheritdoc />
    protected override byte[] ResolveKeyBytes() => (byte[])_key.Clone();
}
