using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.MxGateway.Server.Configuration;
using ZB.MOM.WW.MxGateway.Server.Dashboard;

namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;

public sealed class DashboardAuthenticatorTests
{
    [Fact]
    public void EscapeLdapFilter_EscapesSpecialCharacters()
    {
        string escaped = DashboardAuthenticator.EscapeLdapFilter("a\\b*c(d)e\0f");

        Assert.Equal("a\\5cb\\2ac\\28d\\29e\\00f", escaped);
    }

    [Theory]
    [InlineData("GwAdmin", DashboardRoles.Admin)]
    [InlineData("gwadmin", DashboardRoles.Admin)]
    [InlineData("ou=GwAdmin,ou=groups,dc=lmxopcua,dc=local", DashboardRoles.Admin)]
    [InlineData("OtherGroup", null)]
    public void MapGroupsToRoles_ResolvesByShortNameAndDistinguishedName(
        string ldapGroup,
        string? expectedRole)
    {
        Dictionary<string, string> mapping = new(StringComparer.OrdinalIgnoreCase)
        {
            ["GwAdmin"] = DashboardRoles.Admin,
            ["GwReader"] = DashboardRoles.Viewer,
        };

        IReadOnlyList<string> roles = DashboardAuthenticator.MapGroupsToRoles([ldapGroup], mapping);

        if (expectedRole is null)
        {
            Assert.Empty(roles);
        }
        else
        {
            Assert.Equal(expectedRole, Assert.Single(roles));
        }
    }

    [Fact]
    public void MapGroupsToRoles_AdminPlusViewer_BothRolesEmitted()
    {
        Dictionary<string, string> mapping = new(StringComparer.OrdinalIgnoreCase)
        {
            ["GwAdmin"] = DashboardRoles.Admin,
            ["GwReader"] = DashboardRoles.Viewer,
        };

        IReadOnlyList<string> roles = DashboardAuthenticator.MapGroupsToRoles(
            ["GwAdmin", "GwReader"],
            mapping);

        Assert.Contains(DashboardRoles.Admin, roles);
        Assert.Contains(DashboardRoles.Viewer, roles);
    }

    [Fact]
    public void ExtractFirstRdnValue_ReturnsLeadingRdnValue()
    {
        string result = DashboardAuthenticator.ExtractFirstRdnValue(
            "CN=Gateway Admins,OU=Groups,DC=example,DC=com");

        Assert.Equal("Gateway Admins", result);
    }

    [Fact]
    public async Task AuthenticateAsync_LdapDisabled_ReturnsFailureWithoutRawCredentials()
    {
        DashboardAuthenticator authenticator = CreateAuthenticator(new GatewayOptions
        {
            Ldap = new LdapOptions
            {
                Enabled = false,
            },
        });

        DashboardAuthenticationResult result = await authenticator.AuthenticateAsync(
            "admin",
            "admin123",
            CancellationToken.None);

        Assert.False(result.Succeeded);
        Assert.Null(result.Principal);
        Assert.DoesNotContain("admin123", result.FailureMessage, StringComparison.Ordinal);
    }

    private static DashboardAuthenticator CreateAuthenticator(GatewayOptions options)
    {
        return new DashboardAuthenticator(
            Options.Create(options),
            NullLogger<DashboardAuthenticator>.Instance);
    }
}
