using ScadaLink.Commons.Types.InboundApi;

namespace ScadaLink.Commons.Tests.Types;

/// <summary>
/// ConfigurationDatabase-012: the inbound-API bearer credential is stored as a
/// deterministic keyed hash (HMAC-SHA256 with a server-side pepper) rather than
/// plaintext. These tests pin the hasher contract that the entity, the validator,
/// and the management create-path all depend on.
/// </summary>
public class ApiKeyHasherTests
{
    [Fact]
    public void Hash_IsDeterministic_SameInputSameOutput()
    {
        var hasher = new ApiKeyHasher("a-sufficiently-long-server-pepper-value");

        var first = hasher.Hash("some-api-key-value");
        var second = hasher.Hash("some-api-key-value");

        Assert.Equal(first, second);
    }

    [Fact]
    public void Hash_DoesNotEqualPlaintext()
    {
        var hasher = new ApiKeyHasher("a-sufficiently-long-server-pepper-value");

        var hash = hasher.Hash("some-api-key-value");

        Assert.NotEqual("some-api-key-value", hash);
        Assert.DoesNotContain("some-api-key-value", hash);
    }

    [Fact]
    public void Hash_DifferentInputs_ProduceDifferentHashes()
    {
        var hasher = new ApiKeyHasher("a-sufficiently-long-server-pepper-value");

        Assert.NotEqual(hasher.Hash("key-one"), hasher.Hash("key-two"));
    }

    [Fact]
    public void Hash_DifferentPeppers_ProduceDifferentHashes()
    {
        var a = new ApiKeyHasher("a-sufficiently-long-server-pepper-value");
        var b = new ApiKeyHasher("a-different-but-equally-long-pepper-val");

        // The pepper binds the hash to the server: a stolen DB dump is useless
        // without the pepper because the same key hashes differently under it.
        Assert.NotEqual(a.Hash("same-api-key"), b.Hash("same-api-key"));
    }

    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData("too-short")]
    public void Constructor_MissingOrWeakPepper_FailsFast(string? pepper)
    {
        // The pepper must be present and of meaningful length; a missing or weak
        // pepper is a deployment misconfiguration and must fail loudly.
        Assert.Throws<ArgumentException>(() => new ApiKeyHasher(pepper!));
    }

    [Fact]
    public void Hash_NullInput_Throws()
    {
        var hasher = new ApiKeyHasher("a-sufficiently-long-server-pepper-value");

        Assert.Throws<ArgumentNullException>(() => hasher.Hash(null!));
    }

    [Fact]
    public void Default_IsUsableWithoutAPepper()
    {
        // The unpeppered default exists for tests and non-production wiring; it is
        // still a one-way HMAC-SHA256, just without the server-binding pepper.
        var hash = ApiKeyHasher.Default.Hash("some-api-key-value");

        Assert.NotEqual("some-api-key-value", hash);
        Assert.Equal(ApiKeyHasher.Default.Hash("some-api-key-value"), hash);
    }
}
