using System.Reflection;
using Shouldly;
using ZB.MOM.NatsNet.Server.Auth.Ocsp;

namespace ZB.MOM.NatsNet.Server.Tests;

public sealed class NatsServerOcspCacheTests
{
    [Fact]
    public void InitOCSPResponseCache_LocalType_CreatesLocalDirCache()
    {
        var dir = CreateTempDir();
        try
        {
            var server = NewServer(new ServerOptions
            {
                NoSystemAccount = true,
                OcspCacheConfig = new OcspResponseCacheConfig
                {
                    Type = "local",
                    LocalStore = dir,
                    SaveInterval = 5,
                },
            });

            SetPrivateField(server, "_ocspPeerVerify", true);

            server.InitOCSPResponseCache();

            GetOcspCache(server).ShouldBeOfType<LocalDirCache>();
        }
        finally
        {
            Directory.Delete(dir, recursive: true);
        }
    }

    [Fact]
    public void InitOCSPResponseCache_NoneType_CreatesNoOpCache()
    {
        var server = NewServer(new ServerOptions
        {
            NoSystemAccount = true,
            OcspCacheConfig = new OcspResponseCacheConfig
            {
                Type = "none",
                LocalStore = "_rc_",
                SaveInterval = 5,
            },
        });

        SetPrivateField(server, "_ocspPeerVerify", true);

        server.InitOCSPResponseCache();

        GetOcspCache(server).ShouldBeOfType<NoOpCache>();
    }

    [Fact]
    public void StartStopOCSPResponseCache_WhenInitialized_TogglesOnlineState()
    {
        var dir = CreateTempDir();
        try
        {
            var server = NewServer(new ServerOptions
            {
                NoSystemAccount = true,
                OcspCacheConfig = new OcspResponseCacheConfig
                {
                    Type = "local",
                    LocalStore = dir,
                    SaveInterval = 5,
                },
            });

            SetPrivateField(server, "_ocspPeerVerify", true);
            server.InitOCSPResponseCache();
            var cache = GetOcspCache(server).ShouldBeOfType<LocalDirCache>();

            server.StartOCSPResponseCache();
            cache.Online().ShouldBeTrue();

            server.StopOCSPResponseCache();
            cache.Online().ShouldBeFalse();
        }
        finally
        {
            Directory.Delete(dir, recursive: true);
        }
    }

    private static NatsServer NewServer(ServerOptions options)
    {
        var (server, err) = NatsServer.NewServer(options);
        err.ShouldBeNull();
        server.ShouldNotBeNull();
        return server!;
    }

    private static IOcspResponseCache GetOcspCache(NatsServer server)
    {
        var field = typeof(NatsServer).GetField("_ocsprc", BindingFlags.Instance | BindingFlags.NonPublic);
        field.ShouldNotBeNull();
        var value = field!.GetValue(server);
        value.ShouldNotBeNull();
        return (IOcspResponseCache)value!;
    }

    private static void SetPrivateField(NatsServer server, string fieldName, object value)
    {
        var field = typeof(NatsServer).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
        field.ShouldNotBeNull();
        field!.SetValue(server, value);
    }

    private static string CreateTempDir()
    {
        var dir = Path.Combine(Path.GetTempPath(), "nats-ocsp-cache-" + Path.GetRandomFileName());
        Directory.CreateDirectory(dir);
        return dir;
    }
}
