// Tests for wildcard service export matching on Account.
// Go reference: accounts_test.go — getWildcardServiceExport, getServiceExport (accounts.go line 2849).

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

namespace NATS.Server.Auth.Tests.Auth;

public class WildcardExportTests
{
    private static Account CreateAccount(string name = "TestAccount") => new(name);

    // ──────────────────────────────────────────────────────────────────────────
    // GetWildcardServiceExport
    // ──────────────────────────────────────────────────────────────────────────

    [Fact]
    public void GetWildcardServiceExport_ExactMatch_ReturnsExport()
    {
        // Go ref: accounts.go getWildcardServiceExport — exact key in exports.services map
        var acct = CreateAccount();
        acct.AddServiceExport("orders.create", ServiceResponseType.Singleton, null);

        var result = acct.GetWildcardServiceExport("orders.create");

        result.ShouldNotBeNull();
        result.Subject.ShouldBe("orders.create");
        result.ResponseType.ShouldBe(ServiceResponseType.Singleton);
    }

    [Fact]
    public void GetWildcardServiceExport_StarWildcard_ReturnsExport()
    {
        // Go ref: accounts.go getWildcardServiceExport — isSubsetMatch with '*' wildcard
        var acct = CreateAccount();
        acct.AddServiceExport("orders.*", ServiceResponseType.Streamed, null);

        var result = acct.GetWildcardServiceExport("orders.create");

        result.ShouldNotBeNull();
        result.Subject.ShouldBe("orders.*");
        result.ResponseType.ShouldBe(ServiceResponseType.Streamed);
        result.IsWildcard.ShouldBeTrue();
    }

    [Fact]
    public void GetWildcardServiceExport_GtWildcard_ReturnsExport()
    {
        // Go ref: accounts.go getWildcardServiceExport — isSubsetMatch with '>' wildcard
        var acct = CreateAccount();
        acct.AddServiceExport("orders.>", ServiceResponseType.Chunked, null);

        var result = acct.GetWildcardServiceExport("orders.create.new");

        result.ShouldNotBeNull();
        result.Subject.ShouldBe("orders.>");
        result.ResponseType.ShouldBe(ServiceResponseType.Chunked);
        result.IsWildcard.ShouldBeTrue();
    }

    [Fact]
    public void GetWildcardServiceExport_NoMatch_ReturnsNull()
    {
        // Go ref: accounts.go getWildcardServiceExport — returns nil when no pattern matches
        var acct = CreateAccount();
        acct.AddServiceExport("payments.*", ServiceResponseType.Singleton, null);

        var result = acct.GetWildcardServiceExport("orders.create");

        result.ShouldBeNull();
    }

    // ──────────────────────────────────────────────────────────────────────────
    // GetAllServiceExports
    // ──────────────────────────────────────────────────────────────────────────

    [Fact]
    public void GetAllServiceExports_ReturnsAll()
    {
        // Go ref: accounts.go — exports.services map contains all registered exports
        var acct = CreateAccount();
        acct.AddServiceExport("svc.a", ServiceResponseType.Singleton, null);
        acct.AddServiceExport("svc.b.*", ServiceResponseType.Streamed, null);
        acct.AddServiceExport("svc.>", ServiceResponseType.Chunked, null);

        var all = acct.GetAllServiceExports();

        all.Count.ShouldBe(3);
        all.Select(e => e.Subject).ShouldContain("svc.a");
        all.Select(e => e.Subject).ShouldContain("svc.b.*");
        all.Select(e => e.Subject).ShouldContain("svc.>");
    }

    // ──────────────────────────────────────────────────────────────────────────
    // GetExactServiceExport
    // ──────────────────────────────────────────────────────────────────────────

    [Fact]
    public void GetExactServiceExport_Found()
    {
        // Go ref: accounts.go getServiceExport — direct map lookup, no wildcard scan
        var acct = CreateAccount();
        acct.AddServiceExport("orders.create", ServiceResponseType.Singleton, null);

        var result = acct.GetExactServiceExport("orders.create");

        result.ShouldNotBeNull();
        result.Subject.ShouldBe("orders.create");
    }

    [Fact]
    public void GetExactServiceExport_NotFound_ReturnsNull()
    {
        // Go ref: accounts.go getServiceExport — map lookup misses wildcard patterns
        var acct = CreateAccount();
        acct.AddServiceExport("orders.*", ServiceResponseType.Singleton, null);

        // "orders.create" is not an exact key in the map — only "orders.*" is
        var result = acct.GetExactServiceExport("orders.create");

        result.ShouldBeNull();
    }

    // ──────────────────────────────────────────────────────────────────────────
    // HasServiceExport
    // ──────────────────────────────────────────────────────────────────────────

    [Fact]
    public void HasServiceExport_ExactMatch_ReturnsTrue()
    {
        // Go ref: accounts.go — exact subject registered as an export
        var acct = CreateAccount();
        acct.AddServiceExport("orders.create", ServiceResponseType.Singleton, null);

        acct.HasServiceExport("orders.create").ShouldBeTrue();
    }

    [Fact]
    public void HasServiceExport_WildcardMatch_ReturnsTrue()
    {
        // Go ref: accounts.go — wildcard pattern covers the queried literal subject
        var acct = CreateAccount();
        acct.AddServiceExport("orders.>", ServiceResponseType.Singleton, null);

        acct.HasServiceExport("orders.create.urgent").ShouldBeTrue();
    }

    // ──────────────────────────────────────────────────────────────────────────
    // IsWildcard flag
    // ──────────────────────────────────────────────────────────────────────────

    [Theory]
    [InlineData("orders.*", true)]
    [InlineData("orders.>", true)]
    [InlineData("orders.*.create", true)]
    [InlineData("orders.create", false)]
    [InlineData("svc", false)]
    public void IsWildcard_DetectsWildcardSubjects(string subject, bool expectedWildcard)
    {
        // Go ref: accounts.go — wildcard subjects contain '*' or '>'
        var acct = CreateAccount();
        acct.AddServiceExport(subject, ServiceResponseType.Singleton, null);

        var result = acct.GetExactServiceExport(subject);

        result.ShouldNotBeNull();
        result.IsWildcard.ShouldBe(expectedWildcard);
    }
}
