// Reference: golang/nats-server/server/route.go — S2/Snappy compression for routes
// Tests for RouteCompressionCodec: compression, decompression, negotiation, detection.

using System.Text;
using NATS.Server.Routes;

namespace NATS.Server.Clustering.Tests.Routes;

/// <summary>
/// Tests for route S2/Snappy compression codec, matching Go's route compression
/// behavior using IronSnappy.
/// </summary>
public class RouteS2CompressionTests
{
    [Fact]
    public void Compress_Fast_ProducesValidOutput()
    {
        var data = Encoding.UTF8.GetBytes("NATS route compression test payload");
        var compressed = RouteCompressionCodec.Compress(data, RouteCompressionLevel.Fast);

        compressed.ShouldNotBeNull();
        compressed.Length.ShouldBeGreaterThan(0);

        // Compressed output should be decompressible
        var decompressed = RouteCompressionCodec.Decompress(compressed);
        decompressed.ShouldBe(data);
    }

    [Fact]
    public void Compress_Decompress_RoundTrips()
    {
        var original = Encoding.UTF8.GetBytes("Hello NATS! This is a test of round-trip compression.");

        foreach (var level in new[] { RouteCompressionLevel.Fast, RouteCompressionLevel.Better, RouteCompressionLevel.Best })
        {
            var compressed = RouteCompressionCodec.Compress(original, level);
            var restored = RouteCompressionCodec.Decompress(compressed);
            restored.ShouldBe(original, $"Round-trip failed for level {level}");
        }
    }

    [Fact]
    public void Compress_EmptyData_ReturnsEmpty()
    {
        var result = RouteCompressionCodec.Compress(ReadOnlySpan<byte>.Empty, RouteCompressionLevel.Fast);
        result.ShouldBeEmpty();
    }

    [Fact]
    public void Compress_Off_ReturnsOriginal()
    {
        var data = Encoding.UTF8.GetBytes("uncompressed payload");
        var result = RouteCompressionCodec.Compress(data, RouteCompressionLevel.Off);

        result.ShouldBe(data);
    }

    [Fact]
    public void Decompress_CorruptedData_Throws()
    {
        var garbage = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04 };

        Should.Throw<Exception>(() => RouteCompressionCodec.Decompress(garbage));
    }

    [Fact]
    public void NegotiateCompression_BothOff_ReturnsOff()
    {
        var result = RouteCompressionCodec.NegotiateCompression("off", "off");
        result.ShouldBe(RouteCompressionLevel.Off);
    }

    [Fact]
    public void NegotiateCompression_OneFast_ReturnsFast()
    {
        // When both are fast, result is fast
        var result = RouteCompressionCodec.NegotiateCompression("fast", "fast");
        result.ShouldBe(RouteCompressionLevel.Fast);

        // When one is off, result is off (off wins)
        var result2 = RouteCompressionCodec.NegotiateCompression("fast", "off");
        result2.ShouldBe(RouteCompressionLevel.Off);
    }

    [Fact]
    public void NegotiateCompression_MismatchLevels_ReturnsMinimum()
    {
        // fast (1) vs best (3) => fast (minimum)
        var result = RouteCompressionCodec.NegotiateCompression("fast", "best");
        result.ShouldBe(RouteCompressionLevel.Fast);

        // better (2) vs best (3) => better (minimum)
        var result2 = RouteCompressionCodec.NegotiateCompression("better", "best");
        result2.ShouldBe(RouteCompressionLevel.Better);

        // fast (1) vs better (2) => fast (minimum)
        var result3 = RouteCompressionCodec.NegotiateCompression("fast", "better");
        result3.ShouldBe(RouteCompressionLevel.Fast);
    }

    [Fact]
    public void IsCompressed_ValidSnappy_ReturnsTrue()
    {
        var data = Encoding.UTF8.GetBytes("This is test data for Snappy compression detection");
        var compressed = RouteCompressionCodec.Compress(data, RouteCompressionLevel.Fast);

        RouteCompressionCodec.IsCompressed(compressed).ShouldBeTrue();
    }

    [Fact]
    public void IsCompressed_PlainText_ReturnsFalse()
    {
        var plainText = Encoding.UTF8.GetBytes("PUB test.subject 5\r\nhello\r\n");

        RouteCompressionCodec.IsCompressed(plainText).ShouldBeFalse();
    }

    [Fact]
    public void RoundTrip_LargePayload_Compresses()
    {
        // 10KB payload of repeated data should compress well
        var largePayload = new byte[10240];
        var pattern = Encoding.UTF8.GetBytes("NATS route payload ");
        for (var i = 0; i < largePayload.Length; i++)
            largePayload[i] = pattern[i % pattern.Length];

        var compressed = RouteCompressionCodec.Compress(largePayload, RouteCompressionLevel.Fast);

        // Compressed should be smaller than original for repetitive data
        compressed.Length.ShouldBeLessThan(largePayload.Length);

        // Round-trip should restore original
        var restored = RouteCompressionCodec.Decompress(compressed);
        restored.ShouldBe(largePayload);
    }
}
