using NATS.Server;
using Shouldly;

namespace NATS.Server.Core.Tests;

/// <summary>
/// Tests for per-client trace delivery and echo control.
/// Go reference: server/client.go — c.trace, c.echo fields and TraceMsgDelivery / deliverMsg logic.
/// </summary>
public class ClientTraceTests
{
    // 1. TraceEnabled defaults to false
    [Fact]
    public void TraceEnabled_defaults_to_false()
    {
        var info = new ClientTraceInfo();
        info.TraceEnabled.ShouldBeFalse();
    }

    // 2. EchoEnabled defaults to true
    [Fact]
    public void EchoEnabled_defaults_to_true()
    {
        var info = new ClientTraceInfo();
        info.EchoEnabled.ShouldBeTrue();
    }

    // 3. TraceMsgDelivery records when enabled
    [Fact]
    public void TraceMsgDelivery_records_when_enabled()
    {
        var info = new ClientTraceInfo();
        info.TraceEnabled = true;

        info.TraceMsgDelivery("foo.bar", "client-1", 42);

        info.TraceLogCount.ShouldBe(1);
    }

    // 4. TraceMsgDelivery skips when disabled
    [Fact]
    public void TraceMsgDelivery_skips_when_disabled()
    {
        var info = new ClientTraceInfo();
        // TraceEnabled is false by default

        info.TraceMsgDelivery("foo.bar", "client-1", 42);

        info.TraceLogCount.ShouldBe(0);
    }

    // 5. TraceMsgDelivery captures subject, destination and payload size correctly
    [Fact]
    public void TraceMsgDelivery_captures_subject_destination_size()
    {
        var info = new ClientTraceInfo();
        info.TraceEnabled = true;
        var before = DateTime.UtcNow;

        info.TraceMsgDelivery("orders.new", "client-42", 128);

        var records = info.DrainTraceLog();
        records.Count.ShouldBe(1);
        var rec = records[0];
        rec.Subject.ShouldBe("orders.new");
        rec.Destination.ShouldBe("client-42");
        rec.PayloadSize.ShouldBe(128);
        rec.TimestampUtc.ShouldBeGreaterThanOrEqualTo(before);
        rec.TimestampUtc.ShouldBeLessThanOrEqualTo(DateTime.UtcNow);
    }

    // 6. ShouldEcho returns true when echo is enabled (same client)
    [Fact]
    public void ShouldEcho_true_when_echo_enabled()
    {
        var info = new ClientTraceInfo();
        info.EchoEnabled = true;

        info.ShouldEcho("client-1", "client-1").ShouldBeTrue();
    }

    // 7. ShouldEcho returns false when echo is disabled and same client
    [Fact]
    public void ShouldEcho_false_when_echo_disabled_same_client()
    {
        var info = new ClientTraceInfo();
        info.EchoEnabled = false;

        info.ShouldEcho("client-1", "client-1").ShouldBeFalse();
    }

    // 8. ShouldEcho returns true when echo is disabled but different client
    [Fact]
    public void ShouldEcho_true_when_echo_disabled_different_client()
    {
        var info = new ClientTraceInfo();
        info.EchoEnabled = false;

        info.ShouldEcho("client-1", "client-2").ShouldBeTrue();
    }

    // 9. DrainTraceLog returns records and clears the log
    [Fact]
    public void DrainTraceLog_returns_and_clears()
    {
        var info = new ClientTraceInfo();
        info.TraceEnabled = true;
        info.TraceMsgDelivery("a", "dest-a", 10);
        info.TraceMsgDelivery("b", "dest-b", 20);

        var drained = info.DrainTraceLog();

        drained.Count.ShouldBe(2);
        info.TraceLogCount.ShouldBe(0);
    }

    // 10. TraceLogCount reflects current entries before and after drain
    [Fact]
    public void TraceLogCount_reflects_current_entries()
    {
        var info = new ClientTraceInfo();
        info.TraceEnabled = true;

        info.TraceLogCount.ShouldBe(0);

        info.TraceMsgDelivery("x", "dest-x", 5);
        info.TraceMsgDelivery("y", "dest-y", 15);
        info.TraceMsgDelivery("z", "dest-z", 25);

        info.TraceLogCount.ShouldBe(3);

        info.DrainTraceLog();

        info.TraceLogCount.ShouldBe(0);
    }
}
