using NATS.Server.Auth;
using NATS.Server.Imports;

namespace NATS.Server.Tests;

public class ResponseRoutingTests
{
    [Fact]
    public void GenerateReplyPrefix_creates_unique_prefix()
    {
        var prefix1 = ResponseRouter.GenerateReplyPrefix();
        var prefix2 = ResponseRouter.GenerateReplyPrefix();

        prefix1.ShouldStartWith("_R_.");
        prefix2.ShouldStartWith("_R_.");
        prefix1.ShouldNotBe(prefix2);
        prefix1.Length.ShouldBeGreaterThan(4);
    }

    [Fact]
    public void GenerateReplyPrefix_ends_with_dot()
    {
        var prefix = ResponseRouter.GenerateReplyPrefix();

        prefix.ShouldEndWith(".");
        // Format: "_R_." + 10 chars + "." = 15 chars
        prefix.Length.ShouldBe(15);
    }

    [Fact]
    public void Singleton_response_import_removed_after_delivery()
    {
        var exporter = new Account("exporter");
        exporter.AddServiceExport("api.test", ServiceResponseType.Singleton, null);

        var replyPrefix = ResponseRouter.GenerateReplyPrefix();
        var responseSi = new ServiceImport
        {
            DestinationAccount = exporter,
            From = replyPrefix + ">",
            To = "_INBOX.original.reply",
            IsResponse = true,
            ResponseType = ServiceResponseType.Singleton,
        };
        exporter.Exports.Responses[replyPrefix] = responseSi;

        exporter.Exports.Responses.ShouldContainKey(replyPrefix);

        // Simulate singleton delivery cleanup
        ResponseRouter.CleanupResponse(exporter, replyPrefix, responseSi);

        exporter.Exports.Responses.ShouldNotContainKey(replyPrefix);
    }

    [Fact]
    public void CreateResponseImport_registers_in_exporter_responses()
    {
        var exporter = new Account("exporter");
        var importer = new Account("importer");
        exporter.AddServiceExport("api.test", ServiceResponseType.Singleton, null);

        var originalSi = new ServiceImport
        {
            DestinationAccount = exporter,
            From = "api.test",
            To = "api.test",
            Export = exporter.Exports.Services["api.test"],
            ResponseType = ServiceResponseType.Singleton,
        };

        var responseSi = ResponseRouter.CreateResponseImport(exporter, originalSi, "_INBOX.abc123");

        responseSi.IsResponse.ShouldBeTrue();
        responseSi.ResponseType.ShouldBe(ServiceResponseType.Singleton);
        responseSi.To.ShouldBe("_INBOX.abc123");
        responseSi.DestinationAccount.ShouldBe(exporter);
        responseSi.From.ShouldEndWith(">");
        responseSi.Export.ShouldBe(originalSi.Export);

        // Should be registered in the exporter's response map
        exporter.Exports.Responses.Count.ShouldBe(1);
    }

    [Fact]
    public void CreateResponseImport_preserves_streamed_response_type()
    {
        var exporter = new Account("exporter");
        exporter.AddServiceExport("api.stream", ServiceResponseType.Streamed, null);

        var originalSi = new ServiceImport
        {
            DestinationAccount = exporter,
            From = "api.stream",
            To = "api.stream",
            Export = exporter.Exports.Services["api.stream"],
            ResponseType = ServiceResponseType.Streamed,
        };

        var responseSi = ResponseRouter.CreateResponseImport(exporter, originalSi, "_INBOX.xyz789");

        responseSi.ResponseType.ShouldBe(ServiceResponseType.Streamed);
    }

    [Fact]
    public void Multiple_response_imports_each_get_unique_prefix()
    {
        var exporter = new Account("exporter");
        exporter.AddServiceExport("api.test", ServiceResponseType.Singleton, null);

        var originalSi = new ServiceImport
        {
            DestinationAccount = exporter,
            From = "api.test",
            To = "api.test",
            Export = exporter.Exports.Services["api.test"],
            ResponseType = ServiceResponseType.Singleton,
        };

        var resp1 = ResponseRouter.CreateResponseImport(exporter, originalSi, "_INBOX.reply1");
        var resp2 = ResponseRouter.CreateResponseImport(exporter, originalSi, "_INBOX.reply2");

        exporter.Exports.Responses.Count.ShouldBe(2);
        resp1.To.ShouldBe("_INBOX.reply1");
        resp2.To.ShouldBe("_INBOX.reply2");
        resp1.From.ShouldNotBe(resp2.From);
    }

    [Fact]
    public void LatencyTracker_should_sample_respects_percentage()
    {
        var latency = new ServiceLatency { SamplingPercentage = 0, Subject = "latency.test" };
        LatencyTracker.ShouldSample(latency).ShouldBeFalse();

        var latency100 = new ServiceLatency { SamplingPercentage = 100, Subject = "latency.test" };
        LatencyTracker.ShouldSample(latency100).ShouldBeTrue();
    }

    [Fact]
    public void LatencyTracker_builds_latency_message()
    {
        var msg = LatencyTracker.BuildLatencyMsg("requester", "responder",
            TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(10));

        msg.Requestor.ShouldBe("requester");
        msg.Responder.ShouldBe("responder");
        msg.ServiceLatencyNanos.ShouldBeGreaterThan(0);
        msg.TotalLatencyNanos.ShouldBeGreaterThan(0);
    }
}
