using System.Text.Json;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Mqtt;

namespace ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests;

public sealed class MqttDriverOptionsTests
{
    /// <summary>
    ///     The production instance, not a look-alike copy. A local clone would keep passing after
    ///     <see cref="MqttJson.Options"/> lost its <c>JsonStringEnumConverter</c> — testing the copy
    ///     instead of the thing every seam actually parses through.
    /// </summary>
    private static readonly JsonSerializerOptions J = MqttJson.Options;

    [Fact]
    public void Deserialize_SparkplugConfig_ReadsModeAndSubObject()
    {
        const string json = """
        { "host":"10.100.0.35","port":8883,"useTls":true,"mode":"SparkplugB",
          "sparkplug":{"groupId":"Plant1","hostId":"h1","requestRebirthOnGap":true} }
        """;
        var o = JsonSerializer.Deserialize<MqttDriverOptions>(json, J)!;
        o.Mode.ShouldBe(MqttMode.SparkplugB);
        o.UseTls.ShouldBeTrue();
        o.Sparkplug!.GroupId.ShouldBe("Plant1");
        o.Plain.ShouldBeNull();
    }

    [Fact]
    public void Serialize_WritesEnumsAsNames_NotOrdinals()
    {
        var s = JsonSerializer.Serialize(new MqttDriverOptions { Mode = MqttMode.Plain }, J);
        s.ShouldContain("\"Plain\"");
        s.ShouldNotContain("\"mode\":0");
    }

    // Pins the plan's cross-cutting rule ("never ship anonymous/public-broker defaults") as an
    // executable assertion — nothing else in this suite fails if UseTls / AllowUntrustedServerCertificate
    // (or the §5.1 numeric defaults) get flipped by an unrelated edit.
    [Fact]
    public void Defaults_SecurityCriticalKnobs_MatchPlan()
    {
        var o = new MqttDriverOptions();
        o.UseTls.ShouldBeTrue();
        o.AllowUntrustedServerCertificate.ShouldBeFalse();
        o.ConnectTimeoutSeconds.ShouldBe(15);
        o.ReconnectMinBackoffSeconds.ShouldBe(1);
        o.ReconnectMaxBackoffSeconds.ShouldBe(30);
    }

    // The production case: an operator-authored config blob that simply doesn't mention TLS must
    // never silently deserialize into an insecure instance.
    [Fact]
    public void Deserialize_PartialJsonOmittingTlsKnobs_KeepsSecureDefaults()
    {
        const string json = """{ "host":"10.100.0.35","port":8883 }""";
        var o = JsonSerializer.Deserialize<MqttDriverOptions>(json, J)!;
        o.UseTls.ShouldBeTrue();
        o.AllowUntrustedServerCertificate.ShouldBeFalse();
    }

    [Fact]
    public void ToString_DoesNotLeakPassword()
    {
        var o = new MqttDriverOptions { Password = "hunter2-supersecret" };
        var s = o.ToString();
        s.ShouldNotContain("hunter2-supersecret");
        s.ShouldContain("***");
    }
}
