using System.Text.Json;
using System.Text.Json.Serialization;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers;
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests;

public sealed class HistorianWonderwareDriverPageFormSerializationTests
{
    private static readonly JsonSerializerOptions _opts = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented = false,
    };

    [Fact]
    public void RoundTrip_PreservesKnownFields()
    {
        var original = new WonderwareHistorianClientOptions(
            PipeName: "otopcua-historian-prod",
            SharedSecret: "t0ps3cr3t",
            PeerName: "OtOpcUa-Primary",
            ConnectTimeout: TimeSpan.FromSeconds(20),
            CallTimeout: TimeSpan.FromSeconds(60))
        {
            ProbeTimeoutSeconds = 25,
        };

        var json = JsonSerializer.Serialize(original, _opts);
        var back = JsonSerializer.Deserialize<WonderwareHistorianClientOptions>(json, _opts);

        back.ShouldNotBeNull();
        back.PipeName.ShouldBe("otopcua-historian-prod");
        back.SharedSecret.ShouldBe("t0ps3cr3t");
        back.PeerName.ShouldBe("OtOpcUa-Primary");
        back.ConnectTimeout.ShouldBe(TimeSpan.FromSeconds(20));
        back.CallTimeout.ShouldBe(TimeSpan.FromSeconds(60));
        back.EffectiveConnectTimeout.ShouldBe(TimeSpan.FromSeconds(20));
        back.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(60));
        back.ProbeTimeoutSeconds.ShouldBe(25);
    }

    [Fact]
    public void RoundTrip_NullTimeouts_UsesDefaults()
    {
        var original = new WonderwareHistorianClientOptions(
            PipeName: "otopcua-historian",
            SharedSecret: "secret");

        var json = JsonSerializer.Serialize(original, _opts);
        var back = JsonSerializer.Deserialize<WonderwareHistorianClientOptions>(json, _opts);

        back.ShouldNotBeNull();
        back.ConnectTimeout.ShouldBeNull();
        back.CallTimeout.ShouldBeNull();
        back.EffectiveConnectTimeout.ShouldBe(TimeSpan.FromSeconds(10));
        back.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(30));
    }

    [Fact]
    public void Deserialize_DropsUnknownFields()
    {
        var jsonWithExtra = """
            {
              "unknownField": "old-value",
              "pipeName": "otopcua-historian",
              "sharedSecret": "s3cr3t",
              "probeTimeoutSeconds": 20
            }
            """;

        var optsWithSkip = new JsonSerializerOptions(_opts)
        {
            UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
        };

        var back = JsonSerializer.Deserialize<WonderwareHistorianClientOptions>(jsonWithExtra, optsWithSkip);
        back.ShouldNotBeNull();
        back.ProbeTimeoutSeconds.ShouldBe(20);
        back.PipeName.ShouldBe("otopcua-historian");
    }

    [Fact]
    public void FormModel_RoundTrip_PreservesAllFields()
    {
        // Construct a record with non-default values for every property and verify
        // that WonderwareHistorianClientFormModel.FromRecord → ToRecord is lossless.
        var original = new WonderwareHistorianClientOptions(
            PipeName: "otopcua-historian-prod",
            SharedSecret: "sup3rs3cr3t",
            PeerName: "OtOpcUa-Redundant",
            ConnectTimeout: TimeSpan.FromSeconds(18),
            CallTimeout: TimeSpan.FromSeconds(45))
        {
            ProbeTimeoutSeconds = 30,
        };

        var form = HistorianWonderwareDriverPage.WonderwareHistorianClientFormModel.FromRecord(original);
        var result = form.ToRecord();

        result.PipeName.ShouldBe("otopcua-historian-prod");
        result.SharedSecret.ShouldBe("sup3rs3cr3t");
        result.PeerName.ShouldBe("OtOpcUa-Redundant");
        result.ConnectTimeout.ShouldBe(TimeSpan.FromSeconds(18));
        result.CallTimeout.ShouldBe(TimeSpan.FromSeconds(45));
        result.EffectiveConnectTimeout.ShouldBe(TimeSpan.FromSeconds(18));
        result.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(45));
        result.ProbeTimeoutSeconds.ShouldBe(30);
    }
}
