using NATS.Server.Auth;
using NATS.Server.Protocol;

namespace NATS.Server.Auth.Tests;

public class AuthServiceTests
{
    [Fact]
    public void IsAuthRequired_false_when_no_auth_configured()
    {
        var service = AuthService.Build(new NatsOptions());
        service.IsAuthRequired.ShouldBeFalse();
    }

    [Fact]
    public void IsAuthRequired_true_when_token_configured()
    {
        var service = AuthService.Build(new NatsOptions { Authorization = "mytoken" });
        service.IsAuthRequired.ShouldBeTrue();
    }

    [Fact]
    public void IsAuthRequired_true_when_username_configured()
    {
        var service = AuthService.Build(new NatsOptions { Username = "admin", Password = "pass" });
        service.IsAuthRequired.ShouldBeTrue();
    }

    [Fact]
    public void IsAuthRequired_true_when_users_configured()
    {
        var opts = new NatsOptions
        {
            Users = [new User { Username = "alice", Password = "secret" }],
        };
        var service = AuthService.Build(opts);
        service.IsAuthRequired.ShouldBeTrue();
    }

    [Fact]
    public void IsAuthRequired_true_when_nkeys_configured()
    {
        var opts = new NatsOptions
        {
            NKeys = [new NKeyUser { Nkey = "UABC" }],
        };
        var service = AuthService.Build(opts);
        service.IsAuthRequired.ShouldBeTrue();
    }

    [Fact]
    public void Authenticate_succeeds_when_no_auth_required()
    {
        var service = AuthService.Build(new NatsOptions());
        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Token = "anything" },
            Nonce = [],
        };

        var result = service.Authenticate(ctx);
        result.ShouldNotBeNull();
    }

    [Fact]
    public void Authenticate_token_success()
    {
        var service = AuthService.Build(new NatsOptions { Authorization = "mytoken" });
        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Token = "mytoken" },
            Nonce = [],
        };

        var result = service.Authenticate(ctx);
        result.ShouldNotBeNull();
        result.Identity.ShouldBe("token");
    }

    [Fact]
    public void Authenticate_token_failure()
    {
        var service = AuthService.Build(new NatsOptions { Authorization = "mytoken" });
        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Token = "wrong" },
            Nonce = [],
        };

        service.Authenticate(ctx).ShouldBeNull();
    }

    [Fact]
    public void Authenticate_simple_user_password_success()
    {
        var service = AuthService.Build(new NatsOptions { Username = "admin", Password = "pass" });
        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Username = "admin", Password = "pass" },
            Nonce = [],
        };

        var result = service.Authenticate(ctx);
        result.ShouldNotBeNull();
        result.Identity.ShouldBe("admin");
    }

    [Fact]
    public void Authenticate_multi_user_success()
    {
        var opts = new NatsOptions
        {
            Users = [
                new User { Username = "alice", Password = "secret1" },
                new User { Username = "bob", Password = "secret2" },
            ],
        };
        var service = AuthService.Build(opts);
        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Username = "bob", Password = "secret2" },
            Nonce = [],
        };

        var result = service.Authenticate(ctx);
        result.ShouldNotBeNull();
        result.Identity.ShouldBe("bob");
    }

    [Fact]
    public void NoAuthUser_fallback_when_no_creds()
    {
        var opts = new NatsOptions
        {
            Users = [
                new User { Username = "default", Password = "unused" },
            ],
            NoAuthUser = "default",
        };
        var service = AuthService.Build(opts);
        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions(),
            Nonce = [],
        };

        var result = service.Authenticate(ctx);
        result.ShouldNotBeNull();
        result.Identity.ShouldBe("default");
    }

    [Fact]
    public void NKeys_tried_before_users()
    {
        var opts = new NatsOptions
        {
            NKeys = [new NKeyUser { Nkey = "UABC" }],
            Users = [new User { Username = "alice", Password = "secret" }],
        };
        var service = AuthService.Build(opts);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Username = "alice", Password = "secret" },
            Nonce = [],
        };

        var result = service.Authenticate(ctx);
        result.ShouldNotBeNull();
        result.Identity.ShouldBe("alice");
    }
}
