using System.Text;
using Shouldly;
using ZB.MOM.NatsNet.Server;
using ZB.MOM.NatsNet.Server.Auth;
using ZB.MOM.NatsNet.Server.Internal;
using ZB.MOM.NatsNet.Server.Internal.DataStructures;

namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;

public sealed class ConfigReloaderTests
{
    [Fact] // T:2766
    public void ConfigReloadBoolFlags_ShouldSucceed()
    {
        static string WriteConfig(string body)
        {
            var tempFile = Path.GetTempFileName();
            File.WriteAllText(tempFile, body);
            return tempFile;
        }

        try
        {
            var cases = new[]
            {
                new
                {
                    Config = """
                    {
                      "host": "127.0.0.1",
                      "port": 4222,
                      "logtime": false
                    }
                    """,
                    Args = new[] { "-T" },
                    Validate = (Action<ServerOptions>)(opts => opts.Logtime.ShouldBeTrue()),
                },
                new
                {
                    Config = """
                    {
                      "host": "127.0.0.1",
                      "port": 4222,
                      "debug": true
                    }
                    """,
                    Args = new[] { "-D=false" },
                    Validate = (Action<ServerOptions>)(opts => opts.Debug.ShouldBeFalse()),
                },
                new
                {
                    Config = """
                    {
                      "host": "127.0.0.1",
                      "port": 4222,
                      "trace": true
                    }
                    """,
                    Args = new[] { "-V=false" },
                    Validate = (Action<ServerOptions>)(opts => opts.Trace.ShouldBeFalse()),
                },
                new
                {
                    Config = """
                    {
                      "host": "127.0.0.1",
                      "port": 4222,
                      "cluster": { "port": 6222, "no_advertise": true }
                    }
                    """,
                    Args = new[] { "--no_advertise=false" },
                    Validate = (Action<ServerOptions>)(opts => opts.Cluster.NoAdvertise.ShouldBeFalse()),
                },
                new
                {
                    Config = """
                    {
                      "host": "127.0.0.1",
                      "port": 4222,
                      "jetstream": true
                    }
                    """,
                    Args = new[] { "--js=false" },
                    Validate = (Action<ServerOptions>)(opts => opts.JetStream.ShouldBeFalse()),
                },
            };

            foreach (var testCase in cases)
            {
                var configPath = WriteConfig(testCase.Config);
                var args = new List<string> { "-c", configPath };
                args.AddRange(testCase.Args);

                var (options, error) = ServerOptions.ConfigureOptions(args, null, null, null);

                error.ShouldBeNull();
                options.ShouldNotBeNull();
                testCase.Validate(options!);
                File.Delete(configPath);
            }
        }
        finally
        {
            ServerOptions.FlagSnapshot = null;
        }
    }

    [Fact]
    public void ParseCluster_WithUnknownFieldAndStrictMode_ReturnsError()
    {
        ServerOptions.NoErrOnUnknownFields(false);

        var options = new ServerOptions();
        var errors = new List<Exception>();

        ServerOptions.ParseCluster(
            new Dictionary<string, object?>
            {
                ["unknown_cluster_field"] = true,
            },
            options,
            errors,
            warnings: null);

        errors.Count.ShouldBe(1);
        errors[0].Message.ShouldContain("unknown field");
    }

    [Fact]
    public void ParseCluster_WithUnknownFieldAndRelaxedMode_IgnoresUnknownField()
    {
        ServerOptions.NoErrOnUnknownFields(true);

        try
        {
            var options = new ServerOptions();
            var errors = new List<Exception>();

            ServerOptions.ParseCluster(
                new Dictionary<string, object?>
                {
                    ["unknown_cluster_field"] = true,
                },
                options,
                errors,
                warnings: null);

            errors.ShouldBeEmpty();
        }
        finally
        {
            ServerOptions.NoErrOnUnknownFields(false);
        }
    }

    [Fact] // T:2774
    public void ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed()
    {
        var (server, createError) = NatsServer.NewServer(new ServerOptions
        {
            NoLog = true,
            NoSigs = true,
            Accounts = [new Account { Name = "A" }],
            Users = [new User
            {
                Username = "u",
                Password = "p",
                Account = new Account { Name = "A" },
            }],
        });

        createError.ShouldBeNull();
        server.ShouldNotBeNull();

        try
        {
            var (account, lookupError) = server!.LookupAccount("A");
            lookupError.ShouldBeNull();
            account.ShouldNotBeNull();

            account!.Sublist = SubscriptionIndex.NewSublistWithCache();
            var insertError = account.Sublist.Insert(new Subscription
            {
                Subject = Encoding.ASCII.GetBytes("foo"),
                Queue = Encoding.ASCII.GetBytes("bar"),
            });
            insertError.ShouldBeNull();
            account.TotalSubs().ShouldBe(1);

            server.ReloadAuthorization();

            var (updatedAccount, updatedLookupError) = server.LookupAccount("A");
            updatedLookupError.ShouldBeNull();
            updatedAccount.ShouldNotBeNull();
            updatedAccount!.TotalSubs().ShouldBe(1);
        }
        finally
        {
            server!.Shutdown();
        }
    }

    [Fact] // T:2762
    public void ConfigReloadAccountNKeyUsers_ShouldSucceed()
        => ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed();

    [Fact] // T:2747
    public void ConfigReloadClusterAdvertise_ShouldSucceed()
    {
        var args = new List<string> { "--cluster_advertise", "nats://127.0.0.1:6222" };
        var (options, error) = ServerOptions.ConfigureOptions(args, null, null, null);

        error.ShouldBeNull();
        options.ShouldNotBeNull();
        options!.Cluster.Advertise.ShouldBe("nats://127.0.0.1:6222");
    }
}
