using System.Reflection;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Admin.Security;

namespace ZB.MOM.WW.OtOpcUa.Admin.Tests;

/// <summary>
///     Deterministic unit tests for the LDAP input-sanitization and DN-parsing helpers. Live LDAP
///     bind against the GLAuth dev instance is covered by the admin-browser smoke path, not here,
///     because unit runs must not depend on a running external service.
/// </summary>
[Trait("Category", "Unit")]
public sealed class LdapAuthServiceTests
{
    private static string EscapeLdapFilter(string input) =>
        (string)typeof(LdapAuthService)
            .GetMethod("EscapeLdapFilter", BindingFlags.NonPublic | BindingFlags.Static)!
            .Invoke(null, [input])!;

    private static string ExtractFirstRdnValue(string dn) =>
        (string)typeof(LdapAuthService)
            .GetMethod("ExtractFirstRdnValue", BindingFlags.NonPublic | BindingFlags.Static)!
            .Invoke(null, [dn])!;

    [Theory]
    [InlineData("alice", "alice")]
    [InlineData("a(b)c", "a\\28b\\29c")]
    [InlineData("wildcard*", "wildcard\\2a")]
    [InlineData("back\\slash", "back\\5cslash")]
    public void Escape_filter_replaces_control_chars(string input, string expected)
    {
        EscapeLdapFilter(input).ShouldBe(expected);
    }

    [Theory]
    [InlineData("ou=ReadOnly,ou=groups,dc=lmxopcua,dc=local", "ReadOnly")]
    [InlineData("cn=admin,dc=corp,dc=com", "admin")]
    [InlineData("ReadOnly", "ReadOnly")] // no '=' → pass through
    [InlineData("ou=OnlySegment", "OnlySegment")]
    public void Extract_first_RDN_strips_the_first_attribute_value(string dn, string expected)
    {
        ExtractFirstRdnValue(dn).ShouldBe(expected);
    }
}
