// GLAuth integration test — opt-in only.
//
// Prerequisites
// -------------
// 1. A running GLAuth instance (plaintext LDAP, no TLS).
//    A ready-made Docker Compose stack lives in the sibling repo:
//        ~/Desktop/ScadaBridge/infra/glauth
//    Start it with:  docker compose up -d
//    Default listen address: localhost:3893
//
// 2. Set the following environment variables before running:
//        ZB_LDAP_IT=1                           (required — gates the test)
//        ZB_LDAP_SERVER=localhost               (optional, default localhost)
//        ZB_LDAP_PORT=3893                      (optional, default 3893)
//        ZB_LDAP_BASE=dc=lmxopcua,dc=local      (optional)
//        ZB_LDAP_SVC_DN=cn=svc,dc=lmxopcua,dc=local   (service-account DN)
//        ZB_LDAP_SVC_PW=svcpass                 (service-account password)
//        ZB_LDAP_USER=alice                     (test user login)
//        ZB_LDAP_PW=alicepass                   (test user password)
//        ZB_LDAP_USERATTR=cn                    (optional, default cn)
//
// Run command:
//        ZB_LDAP_IT=1 ZB_LDAP_SVC_DN=... ZB_LDAP_SVC_PW=... \
//            ZB_LDAP_USER=... ZB_LDAP_PW=... \
//            dotnet test tests/ZB.MOM.WW.Auth.Ldap.Tests \
//            --filter "FullyQualifiedName~GLAuthIntegrationTests"
//
// Without ZB_LDAP_IT=1 the test is SKIPPED — it does not affect the normal CI run.

using System.Net.Sockets;
using ZB.MOM.WW.Auth.Abstractions.Ldap;
using ZB.MOM.WW.Auth.Ldap;

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

public sealed class GLAuthIntegrationTests
{
    /// <summary>
    /// Performs a real bind-then-search-then-bind against a live GLAuth instance.
    /// Verifies that authentication succeeds and that at least one LDAP group is returned.
    /// Skipped unless <c>ZB_LDAP_IT=1</c> is set; skipped again if the server is unreachable.
    /// </summary>
    [SkippableFact]
    public async Task Authenticate_AgainstRealGLAuth_Succeeds()
    {
        // ------------------------------------------------------------------ opt-in gate
        Skip.IfNot(
            Environment.GetEnvironmentVariable("ZB_LDAP_IT") == "1",
            "Set ZB_LDAP_IT=1 and a reachable GLAuth to run.");

        // ------------------------------------------------------------------ read config
        var server   = Environment.GetEnvironmentVariable("ZB_LDAP_SERVER")   ?? "localhost";
        var port     = int.TryParse(Environment.GetEnvironmentVariable("ZB_LDAP_PORT"), out var p) ? p : 3893;
        var baseDn   = Environment.GetEnvironmentVariable("ZB_LDAP_BASE")     ?? "dc=lmxopcua,dc=local";
        var svcDn    = Environment.GetEnvironmentVariable("ZB_LDAP_SVC_DN")   ?? "";
        var svcPw    = Environment.GetEnvironmentVariable("ZB_LDAP_SVC_PW")   ?? "";
        var user     = Environment.GetEnvironmentVariable("ZB_LDAP_USER")     ?? "";
        var pw       = Environment.GetEnvironmentVariable("ZB_LDAP_PW")       ?? "";
        var userAttr = Environment.GetEnvironmentVariable("ZB_LDAP_USERATTR") ?? "cn";

        // ------------------------------------------------------------------ reachability probe
        try
        {
            using var tcp = new TcpClient();
            // 3-second connect timeout to keep the test suite snappy when the server is absent
            var connectTask = tcp.ConnectAsync(server, port);
            if (!connectTask.Wait(TimeSpan.FromSeconds(3)))
                Skip.If(true, $"GLAuth not reachable at {server}:{port} (connect timed out).");
        }
        catch (Exception ex)
        {
            Skip.If(true, $"GLAuth not reachable at {server}:{port}: {ex.Message}");
        }

        // ------------------------------------------------------------------ build options
        var options = new LdapOptions
        {
            Enabled                = true,
            Server                 = server,
            Port                   = port,
            Transport              = LdapTransport.None,
            AllowInsecure          = true,
            SearchBase             = baseDn,
            ServiceAccountDn       = svcDn,
            ServiceAccountPassword = svcPw,
            UserNameAttribute      = userAttr,
            // GLAuth returns memberOf by default; keep the library default
            GroupAttribute         = "memberOf",
        };

        // ------------------------------------------------------------------ exercise the real service
        // Uses the public single-argument constructor, which wires up NovellLdapConnectionFactory
        // internally — no test seam involved.
        var svc = new LdapAuthService(options);
        var result = await svc.AuthenticateAsync(user, pw, default);

        // ------------------------------------------------------------------ assertions
        Assert.True(result.Succeeded,
            $"Authentication failed: {result.Failure} (server={server}:{port}, user={user})");
        Assert.NotEmpty(result.Groups);
    }
}
