using ZB.MOM.WW.Auth.Abstractions.Ldap;
using ZB.MOM.WW.Auth.Ldap;

namespace ZB.MOM.WW.Auth.Ldap.Tests;

public class LdapOptionsValidatorTests
{
    private static LdapOptions Opts() => new()
    {
        Enabled = true,
        Server = "x",
        Transport = LdapTransport.None,
        AllowInsecure = true,
        SearchBase = "dc=x",
        ServiceAccountDn = "cn=svc,dc=x",
    };

    [Fact]
    public void Validator_Fails_PlainTransport_WhenNotAllowInsecure() =>
        Assert.True(new LdapOptionsValidator()
            .Validate(null, Opts() with { Transport = LdapTransport.None, AllowInsecure = false })
            .Failed);

    [Fact]
    public void Validator_Fails_WhenServerEmpty() =>
        Assert.True(new LdapOptionsValidator()
            .Validate(null, Opts() with { Server = "   " })
            .Failed);

    [Fact]
    public void Validator_Fails_WhenSearchBaseEmpty() =>
        Assert.True(new LdapOptionsValidator()
            .Validate(null, Opts() with { SearchBase = "" })
            .Failed);

    [Fact]
    public void Validator_FailureMessage_NamesOffendingField()
    {
        var result = new LdapOptionsValidator()
            .Validate(null, Opts() with { Server = "" });

        Assert.True(result.Failed);
        Assert.Contains(nameof(LdapOptions.Server), result.FailureMessage);
    }

    [Fact]
    public void Validator_Fails_WhenServiceAccountDnEmpty()
    {
        // I5: an empty ServiceAccountDn risks an anonymous bind, so it must be rejected
        // and the failure message must name the offending key.
        var result = new LdapOptionsValidator()
            .Validate(null, Opts() with { ServiceAccountDn = "   " });

        Assert.True(result.Failed);
        Assert.Contains(nameof(LdapOptions.ServiceAccountDn), result.FailureMessage);
    }

    [Fact]
    public void Validator_Succeeds_OnValidSecureConfig() =>
        Assert.False(new LdapOptionsValidator()
            .Validate(null, Opts() with
            {
                Transport = LdapTransport.Ldaps,
                AllowInsecure = false,
                Server = "s",
                SearchBase = "dc=x",
            })
            .Failed);

    [Fact]
    public void Validator_Succeeds_OnInsecureWhenAllowed() =>
        Assert.False(new LdapOptionsValidator()
            .Validate(null, Opts())
            .Failed);

    [Fact]
    public void Validator_Skips_AllChecks_WhenDisabled() =>
        // When LDAP is disabled its connection fields are inert; an otherwise-invalid
        // config (plaintext + blank Server/SearchBase/ServiceAccountDn) must still pass.
        Assert.False(new LdapOptionsValidator()
            .Validate(null, new LdapOptions
            {
                Enabled = false,
                Transport = LdapTransport.None,
                AllowInsecure = false,
                Server = "",
                SearchBase = "",
                ServiceAccountDn = "",
            })
            .Failed);
}
