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.AbCip;

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

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

    private static readonly JsonSerializerOptions TestJsonOpts = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
    };

    [Fact]
    public void RoundTrip_PreservesKnownFields()
    {
        var original = new AbCipDriverOptions
        {
            Timeout = TimeSpan.FromSeconds(5),
            EnableControllerBrowse = true,
            EnableAlarmProjection = true,
            AlarmPollInterval = TimeSpan.FromSeconds(2),
            EnableDeclarationOnlyUdtGrouping = true,
            Probe = new AbCipProbeOptions
            {
                Enabled = true,
                Interval = TimeSpan.FromSeconds(10),
                Timeout = TimeSpan.FromSeconds(3),
                ProbeTagPath = "Program:Main.HealthBit",
            },
            ProbeTimeoutSeconds = 10,
            Devices =
            [
                new AbCipDeviceOptions("ab://10.0.0.1/1,0", AbCipPlcFamily.ControlLogix, "PLC-1"),
            ],
            Tags =
            [
                new AbCipTagDefinition("Speed", "ab://10.0.0.1/1,0", "Motor1.Speed", AbCipDataType.Real, Writable: true),
            ],
        };

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

        back.ShouldNotBeNull();
        back.Timeout.ShouldBe(TimeSpan.FromSeconds(5));
        back.EnableControllerBrowse.ShouldBeTrue();
        back.EnableAlarmProjection.ShouldBeTrue();
        back.AlarmPollInterval.ShouldBe(TimeSpan.FromSeconds(2));
        back.EnableDeclarationOnlyUdtGrouping.ShouldBeTrue();
        back.Probe.Enabled.ShouldBeTrue();
        back.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(10));
        back.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(3));
        back.Probe.ProbeTagPath.ShouldBe("Program:Main.HealthBit");
        back.ProbeTimeoutSeconds.ShouldBe(10);
        back.Devices.Count.ShouldBe(1);
        back.Devices[0].HostAddress.ShouldBe("ab://10.0.0.1/1,0");
        back.Devices[0].PlcFamily.ShouldBe(AbCipPlcFamily.ControlLogix);
        back.Tags.Count.ShouldBe(1);
        back.Tags[0].Name.ShouldBe("Speed");
        back.Tags[0].DataType.ShouldBe(AbCipDataType.Real);
    }

    [Fact]
    public void Deserialize_DropsUnknownFields()
    {
        var jsonWithExtra = """{"unknownField":"old-value","probeTimeoutSeconds":10}""";

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

        var back = JsonSerializer.Deserialize<AbCipDriverOptions>(jsonWithExtra, optsWithSkip);
        back.ShouldNotBeNull();
        back.ProbeTimeoutSeconds.ShouldBe(10);
    }

    [Fact]
    public void DeviceRow_round_trips_through_definition()
    {
        var row = new AbCipDriverPage.AbCipDeviceRow
        {
            HostAddress = "ab://10.0.0.1/1,0", PlcFamily = AbCipPlcFamily.CompactLogix, DeviceName = "PLC-A",
        };
        var def = row.ToDefinition();
        var back = AbCipDriverPage.AbCipDeviceRow.FromDefinition(def);

        back.HostAddress.ShouldBe("ab://10.0.0.1/1,0");
        back.PlcFamily.ShouldBe(AbCipPlcFamily.CompactLogix);
        back.DeviceName.ShouldBe("PLC-A");
    }

    [Fact]
    public void DeviceRow_preserves_unedited_fields()
    {
        var original = new AbCipDeviceOptions(
            "ab://10.0.0.1/1,0", AbCipPlcFamily.ControlLogix, "PLC-A",
            AllowPacking: true, ConnectionSize: 4002);
        var row = AbCipDriverPage.AbCipDeviceRow.FromDefinition(original);
        row.HostAddress = "ab://10.0.0.2/1,0";

        var back = row.ToDefinition();
        back.HostAddress.ShouldBe("ab://10.0.0.2/1,0");
        back.AllowPacking.ShouldBe(true);
        back.ConnectionSize.ShouldBe(4002);
    }

    [Fact]
    public void TagRow_round_trips_through_definition()
    {
        var row = new AbCipDriverPage.AbCipTagRow
        {
            Name = "Speed", DeviceHostAddress = "ab://10.0.0.1/1,0", TagPath = "Motor1.Speed",
            DataType = AbCipDataType.Real, Writable = true,
        };
        var def = row.ToDefinition();
        var back = AbCipDriverPage.AbCipTagRow.FromDefinition(def);

        back.Name.ShouldBe("Speed");
        back.DeviceHostAddress.ShouldBe("ab://10.0.0.1/1,0");
        back.TagPath.ShouldBe("Motor1.Speed");
        back.DataType.ShouldBe(AbCipDataType.Real);
        back.Writable.ShouldBeTrue();
    }

    [Fact]
    public void TagRow_preserves_unedited_fields()
    {
        var original = new AbCipTagDefinition(
            "Speed", "ab://10.0.0.1/1,0", "Motor1.Speed", AbCipDataType.Structure,
            Writable: true, WriteIdempotent: true,
            Members: [new AbCipStructureMember("Sub", AbCipDataType.DInt)],
            SafetyTag: true);
        var row = AbCipDriverPage.AbCipTagRow.FromDefinition(original);
        row.Name = "Renamed";

        var back = row.ToDefinition();
        back.Name.ShouldBe("Renamed");
        back.WriteIdempotent.ShouldBeTrue();
        back.SafetyTag.ShouldBeTrue();
        back.Members.ShouldNotBeNull();
        back.Members!.Count.ShouldBe(1);
        back.Members[0].Name.ShouldBe("Sub");
    }

    [Fact]
    public void ValidateDeviceRow_rejects_duplicate_host()
    {
        var rows = new List<AbCipDriverPage.AbCipDeviceRow> { new() { HostAddress = "ab://10.0.0.1/1,0" } };
        AbCipDriverPage.AbCipDeviceRow.ValidateRow(new() { HostAddress = "ab://10.0.0.1/1,0" }, rows, null)
            .ShouldNotBeNull();
    }

    [Fact]
    public void ValidateTagRow_rejects_duplicate_name()
    {
        var rows = new List<AbCipDriverPage.AbCipTagRow> { new() { Name = "Speed" } };
        AbCipDriverPage.AbCipTagRow.ValidateRow(new() { Name = "Speed" }, rows, null)
            .ShouldNotBeNull();
    }

    [Fact]
    public void Device_and_tag_lists_survive_options_serialize_round_trip()
    {
        var devices = new List<AbCipDeviceOptions>
        {
            new("ab://10.0.0.1/1,0", AbCipPlcFamily.ControlLogix, "PLC-1"),
            new("ab://10.0.0.2/1,0", AbCipPlcFamily.CompactLogix, "PLC-2"),
        };
        var tags = new List<AbCipTagDefinition>
        {
            new("Speed", "ab://10.0.0.1/1,0", "Motor1.Speed", AbCipDataType.Real),
            new("Run", "ab://10.0.0.2/1,0", "Motor2.Run", AbCipDataType.Bool),
        };
        var opts = new AbCipDriverPage.FormModel().ToOptions(devices, tags);
        var json = JsonSerializer.Serialize(opts, TestJsonOpts);
        var back = JsonSerializer.Deserialize<AbCipDriverOptions>(json, TestJsonOpts)!;
        back.Devices.Count.ShouldBe(2);
        back.Devices[0].HostAddress.ShouldBe("ab://10.0.0.1/1,0");
        back.Tags.Count.ShouldBe(2);
        back.Tags[0].Name.ShouldBe("Speed");
    }
}
