// Copyright 2012-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0

using Shouldly;
using ZB.MOM.NatsNet.Server.Internal;

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

/// <summary>
/// Tests for NatsLogger / ServerLogging — mirrors tests from server/log_test.go.
/// </summary>
public class NatsLoggerTests
{
    private sealed class TestLogger : INatsLogger
    {
        public List<string> Messages { get; } = [];

        public void Noticef(string format, params object[] args) => Messages.Add($"[INF] {string.Format(format, args)}");
        public void Warnf(string format, params object[] args) => Messages.Add($"[WRN] {string.Format(format, args)}");
        public void Fatalf(string format, params object[] args) => Messages.Add($"[FTL] {string.Format(format, args)}");
        public void Errorf(string format, params object[] args) => Messages.Add($"[ERR] {string.Format(format, args)}");
        public void Debugf(string format, params object[] args) => Messages.Add($"[DBG] {string.Format(format, args)}");
        public void Tracef(string format, params object[] args) => Messages.Add($"[TRC] {string.Format(format, args)}");
    }

    /// <summary>
    /// Mirrors TestSetLogger — verify logger assignment and atomic flags.
    /// </summary>
    [Fact] // T:2017
    public void SetLogger_ShouldSetLoggerAndFlags()
    {
        var logging = new ServerLogging();
        var testLog = new TestLogger();

        logging.SetLoggerV2(testLog, true, true, false);
        logging.IsDebug.ShouldBeTrue();
        logging.IsTrace.ShouldBeTrue();
        logging.IsTraceSysAcc.ShouldBeFalse();
        logging.GetLogger().ShouldBe(testLog);
    }

    /// <summary>
    /// Verify all log methods produce output when flags enabled.
    /// </summary>
    [Fact] // T:2017 (continuation)
    public void AllLogMethods_ShouldProduceOutput()
    {
        var logging = new ServerLogging();
        var testLog = new TestLogger();
        logging.SetLoggerV2(testLog, true, true, false);

        logging.Noticef("notice {0}", "test");
        logging.Errorf("error {0}", "test");
        logging.Warnf("warn {0}", "test");
        logging.Fatalf("fatal {0}", "test");
        logging.Debugf("debug {0}", "test");
        logging.Tracef("trace {0}", "test");

        testLog.Messages.Count.ShouldBe(6);
        testLog.Messages[0].ShouldContain("[INF]");
        testLog.Messages[1].ShouldContain("[ERR]");
        testLog.Messages[4].ShouldContain("[DBG]");
        testLog.Messages[5].ShouldContain("[TRC]");
    }

    /// <summary>
    /// Debug/Trace should not produce output when flags disabled.
    /// </summary>
    [Fact] // T:2017 (continuation)
    public void DebugTrace_ShouldBeNoOpWhenDisabled()
    {
        var logging = new ServerLogging();
        var testLog = new TestLogger();
        logging.SetLoggerV2(testLog, false, false, false);

        logging.Debugf("debug");
        logging.Tracef("trace");

        testLog.Messages.ShouldBeEmpty();
    }

    /// <summary>
    /// Verify null logger does not throw.
    /// </summary>
    [Fact]
    public void NullLogger_ShouldNotThrow()
    {
        var logging = new ServerLogging();
        Should.NotThrow(() => logging.Noticef("test"));
        Should.NotThrow(() => logging.Errorf("test"));
        Should.NotThrow(() => logging.Debugf("test"));
    }

    /// <summary>
    /// Verify rate-limited logging suppresses duplicate messages.
    /// </summary>
    [Fact] // T:2017 (RateLimitWarnf behavior)
    public void RateLimitWarnf_ShouldSuppressDuplicates()
    {
        var logging = new ServerLogging();
        var testLog = new TestLogger();
        logging.SetLoggerV2(testLog, false, false, false);

        logging.RateLimitWarnf("duplicate message");
        logging.RateLimitWarnf("duplicate message");
        logging.RateLimitWarnf("different message");

        // Should only log 2 unique messages, not 3.
        testLog.Messages.Count.ShouldBe(2);
    }

    /// <summary>
    /// Verify Errors/Errorc/Errorsc convenience methods.
    /// </summary>
    [Fact]
    public void ErrorVariants_ShouldFormatCorrectly()
    {
        var logging = new ServerLogging();
        var testLog = new TestLogger();
        logging.SetLoggerV2(testLog, false, false, false);

        logging.Errors("client", new Exception("conn reset"));
        logging.Errorc("TLS", new Exception("cert expired"));
        logging.Errorsc("route", "cluster", new Exception("timeout"));

        testLog.Messages.Count.ShouldBe(3);
        testLog.Messages[0].ShouldContain("client - conn reset");
        testLog.Messages[1].ShouldContain("TLS: cert expired");
        testLog.Messages[2].ShouldContain("route - cluster: timeout");
    }
}
