using System.Text.Json;
using System.Text.Json.Serialization;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Forms;
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;

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

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

    [Fact]
    public void RoundTrip_PreservesKnownFields()
    {
        var original = new TwinCATDriverOptions
        {
            Timeout = TimeSpan.FromSeconds(4),
            UseNativeNotifications = false,
            EnableControllerBrowse = true,
            NotificationMaxDelayMs = 50,
            Probe = new TwinCATProbeOptions
            {
                Enabled = false,
                Interval = TimeSpan.FromSeconds(10),
                Timeout = TimeSpan.FromSeconds(3),
            },
            ProbeTimeoutSeconds = 20,
            Devices = [],
        };

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

        back.ShouldNotBeNull();
        back.Timeout.ShouldBe(TimeSpan.FromSeconds(4));
        back.UseNativeNotifications.ShouldBeFalse();
        back.EnableControllerBrowse.ShouldBeTrue();
        back.NotificationMaxDelayMs.ShouldBe(50);
        back.Probe.ShouldNotBeNull();
        back.Probe.Enabled.ShouldBeFalse();
        back.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(10));
        back.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(3));
        back.ProbeTimeoutSeconds.ShouldBe(20);
        back.Devices.ShouldBeEmpty();
    }

    [Fact]
    public void Deserialize_DropsUnknownFields()
    {
        var jsonWithExtra = """{"unknownField":"old-value","probeTimeoutSeconds":25}""";
        var optsSkip = new JsonSerializerOptions(_opts)
        {
            UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
        };
        var back = JsonSerializer.Deserialize<TwinCATDriverOptions>(jsonWithExtra, optsSkip);
        back.ShouldNotBeNull();
        back.ProbeTimeoutSeconds.ShouldBe(25);
    }

    [Fact]
    public void FormModel_RoundTrip_PreservesEditableFields()
    {
        var opts = new TwinCATDriverOptions
        {
            Timeout = TimeSpan.FromSeconds(3),
            UseNativeNotifications = true,
            EnableControllerBrowse = false,
            NotificationMaxDelayMs = 100,
            Probe = new TwinCATProbeOptions
            {
                Enabled = true,
                Interval = TimeSpan.FromSeconds(6),
                Timeout = TimeSpan.FromSeconds(2),
            },
            ProbeTimeoutSeconds = 15,
        };

        var form = TwinCATDriverForm.FormModel.FromOptions(opts);
        var roundTripped = form.ToOptions([]);

        roundTripped.Timeout.ShouldBe(TimeSpan.FromSeconds(3));
        roundTripped.UseNativeNotifications.ShouldBeTrue();
        roundTripped.EnableControllerBrowse.ShouldBeFalse();
        roundTripped.NotificationMaxDelayMs.ShouldBe(100);
        roundTripped.Probe.Enabled.ShouldBeTrue();
        roundTripped.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(6));
        roundTripped.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(2));
        roundTripped.ProbeTimeoutSeconds.ShouldBe(15);
    }

    // v3 Batch-2 (WP3): the embedded-Devices CollectionEditor + TwinCATDeviceRow retired — devices are now
    // separate Device entities authored via the raw-tree DeviceModal (TwinCATDeviceModel, HostAddress into
    // DeviceConfig). The former DeviceRow_* tests were removed. The channel-config FormModel now lives in
    // TwinCATDriverForm; the multi-device Devices array it preserves is covered below.

    [Fact]
    public void FormModel_ToOptions_SerializesDeviceList()
    {
        var form = TwinCATDriverForm.FormModel.FromOptions(new TwinCATDriverOptions());

        var devices = new[] { new TwinCATDeviceOptions("192.168.0.1.1.1:851", "PLC1") };

        var opts = form.ToOptions(devices);
        var json = JsonSerializer.Serialize(opts, _opts);
        var back = JsonSerializer.Deserialize<TwinCATDriverOptions>(json, _opts);

        back.ShouldNotBeNull();
        back.Devices.Count.ShouldBe(1);
        back.Devices[0].HostAddress.ShouldBe("192.168.0.1.1.1:851");
        back.Devices[0].DeviceName.ShouldBe("PLC1");
    }
}
