using ZB.MOM.WW.MxGateway.Server.Security.Authentication;

namespace ZB.MOM.WW.MxGateway.Tests.Security.Authentication;

public sealed class ApiKeyConstraintSerializerTests
{
    /// <summary>Verifies that dashboard tags survive a serialize/deserialize round trip.</summary>
    [Fact]
    public void RoundTrip_WithDashboardTags_PreservesTags()
    {
        ApiKeyConstraints constraints = ApiKeyConstraints.Empty with
        {
            ReadSubtrees = ["Area1/*"],
            DashboardTags = ["team-a", "team-b"],
        };

        string? json = ApiKeyConstraintSerializer.Serialize(constraints);

        Assert.NotNull(json);
        Assert.Contains("dashboard_tags", json, StringComparison.Ordinal);

        ApiKeyConstraints restored = ApiKeyConstraintSerializer.Deserialize(json);

        Assert.Equal(["team-a", "team-b"], restored.DashboardTags);
        Assert.Equal(["Area1/*"], restored.ReadSubtrees);
    }

    /// <summary>
    ///     Verifies a key whose only per-key policy is a dashboard tag is still persisted: the
    ///     serializer drops empty constraints entirely, so the tag must count as non-empty.
    /// </summary>
    [Fact]
    public void Serialize_WithOnlyDashboardTags_IsNotTreatedAsEmpty()
    {
        ApiKeyConstraints constraints = ApiKeyConstraints.Empty with { DashboardTags = ["team-a"] };

        Assert.False(constraints.IsEmpty);
        Assert.NotNull(ApiKeyConstraintSerializer.Serialize(constraints));
    }

    /// <summary>Verifies that dashboard tags never register as read or write (data-access) constraints.</summary>
    [Fact]
    public void DashboardTags_AreNotDataAccessConstraints()
    {
        ApiKeyConstraints constraints = ApiKeyConstraints.Empty with { DashboardTags = ["team-a"] };

        Assert.False(constraints.HasReadConstraints);
        Assert.False(constraints.HasWriteConstraints);
    }

    /// <summary>
    ///     Verifies a row persisted before the dashboard-tag field existed still deserializes, with
    ///     every pre-existing constraint intact and an untagged (empty, never null) tag list.
    /// </summary>
    [Fact]
    public void Deserialize_LegacyJsonWithoutDashboardTags_YieldsUntaggedConstraints()
    {
        const string LegacyJson = """
            {
              "read_subtrees": ["Area1/*"],
              "write_subtrees": [],
              "read_tag_globs": [],
              "write_tag_globs": ["Pump_*"],
              "max_write_classification": 2,
              "browse_subtrees": ["Area1/*"],
              "read_alarm_only": true,
              "read_historized_only": false
            }
            """;

        ApiKeyConstraints constraints = ApiKeyConstraintSerializer.Deserialize(LegacyJson);

        Assert.Empty(constraints.DashboardTags);
        Assert.Equal(["Area1/*"], constraints.ReadSubtrees);
        Assert.Equal(["Pump_*"], constraints.WriteTagGlobs);
        Assert.Equal(2, constraints.MaxWriteClassification);
        Assert.Equal(["Area1/*"], constraints.BrowseSubtrees);
        Assert.True(constraints.ReadAlarmOnly);
        Assert.False(constraints.ReadHistorizedOnly);
    }

    /// <summary>Verifies an explicit JSON null for the tag list normalizes to untagged rather than null.</summary>
    [Fact]
    public void Deserialize_ExplicitNullDashboardTags_YieldsEmptyList()
    {
        const string Json = """
            {
              "read_subtrees": [],
              "write_subtrees": [],
              "read_tag_globs": [],
              "write_tag_globs": [],
              "max_write_classification": null,
              "browse_subtrees": [],
              "read_alarm_only": false,
              "read_historized_only": false,
              "dashboard_tags": null
            }
            """;

        Assert.Empty(ApiKeyConstraintSerializer.Deserialize(Json).DashboardTags);
    }

    /// <summary>Verifies null or whitespace constraint JSON deserializes to the untagged empty instance.</summary>
    [Fact]
    public void Deserialize_NullJson_YieldsEmptyConstraints()
    {
        Assert.Same(ApiKeyConstraints.Empty, ApiKeyConstraintSerializer.Deserialize(null));
        Assert.Empty(ApiKeyConstraints.Empty.DashboardTags);
    }
}
