using System;
using StellaOps.Authority.Plugin.Standard.Security;
using StellaOps.Cryptography;

namespace StellaOps.Authority.Plugin.Standard.Tests.Security;

public class CryptoPasswordHasherTests
{
    [Fact]
    public void Hash_EmitsArgon2idByDefault()
    {
        var options = CreateOptions();
        var hasher = new CryptoPasswordHasher(options, new DefaultCryptoProvider());

        var encoded = hasher.Hash("Secr3t!");

        Assert.StartsWith("$argon2id$", encoded, StringComparison.Ordinal);
    }

    [Fact]
    public void Verify_ReturnsSuccess_ForCurrentAlgorithm()
    {
        var options = CreateOptions();
        var provider = new DefaultCryptoProvider();
        var hasher = new CryptoPasswordHasher(options, provider);
        var encoded = hasher.Hash("Passw0rd!");

        var result = hasher.Verify("Passw0rd!", encoded);

        Assert.Equal(PasswordVerificationResult.Success, result);
    }

    [Fact]
    public void Verify_FlagsLegacyPbkdf2_ForRehash()
    {
        var options = CreateOptions();
        var provider = new DefaultCryptoProvider();
        var hasher = new CryptoPasswordHasher(options, provider);

        var legacy = new Pbkdf2PasswordHasher().Hash(
            "Passw0rd!",
            new PasswordHashOptions
            {
                Algorithm = PasswordHashAlgorithm.Pbkdf2,
                Iterations = 150_000
            });

        var result = hasher.Verify("Passw0rd!", legacy);

        Assert.Equal(PasswordVerificationResult.SuccessRehashNeeded, result);
    }

    [Fact]
    public void Verify_RejectsTamperedPayload()
    {
        var options = CreateOptions();
        var provider = new DefaultCryptoProvider();
        var hasher = new CryptoPasswordHasher(options, provider);

        var legacy = new Pbkdf2PasswordHasher().Hash(
            "Passw0rd!",
            new PasswordHashOptions
            {
                Algorithm = PasswordHashAlgorithm.Pbkdf2,
                Iterations = 160_000
            });

        var tampered = legacy + "corrupted";

        var result = hasher.Verify("Passw0rd!", tampered);

        Assert.Equal(PasswordVerificationResult.Failed, result);
    }

    [Fact]
    public void Verify_AllowsLegacyAlgorithmWhenConfigured()
    {
        var options = CreateOptions();
        options.PasswordHashing = options.PasswordHashing with
        {
            Algorithm = PasswordHashAlgorithm.Pbkdf2,
            Iterations = 200_000
        };

        var provider = new DefaultCryptoProvider();
        var hasher = new CryptoPasswordHasher(options, provider);

        var legacy = new Pbkdf2PasswordHasher().Hash(
            "Passw0rd!",
            new PasswordHashOptions
            {
                Algorithm = PasswordHashAlgorithm.Pbkdf2,
                Iterations = 200_000
            });

        var result = hasher.Verify("Passw0rd!", legacy);

        Assert.Equal(PasswordVerificationResult.Success, result);
    }

    private static StandardPluginOptions CreateOptions() => new()
    {
        PasswordPolicy = new PasswordPolicyOptions
        {
            MinimumLength = 8,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
            RequireSymbol = false
        },
        Lockout = new LockoutOptions
        {
            Enabled = true,
            MaxAttempts = 5,
            WindowMinutes = 15
        },
        PasswordHashing = new PasswordHashOptions
        {
            Algorithm = PasswordHashAlgorithm.Argon2id,
            MemorySizeInKib = 8 * 1024,
            Iterations = 2,
            Parallelism = 1
        }
    };
}
