using System.Text.Json;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;

namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Types.Transport;

/// <summary>
/// M8 A1: focused shape / value-equality / round-trip tests for the Transport (#24)
/// name-mapping DTOs — <see cref="SiteMapping"/>, <see cref="ConnectionMapping"/>,
/// and <see cref="BundleNameMap"/>. These records cross the Central UI ⇆ ManagementActor
/// boundary (and may be persisted in an import session) via System.Text.Json, so a
/// positional/tuple slip would silently mis-map sites/connections during import.
/// Uses plain <see cref="JsonSerializer"/> (no dependency on the Transport project's
/// BundleJsonOptions) per the A1 task contract.
/// </summary>
public sealed class BundleNameMapTests
{
    private static readonly JsonSerializerOptions JsonOpts = new(JsonSerializerDefaults.Web);

    [Fact]
    public void Empty_HasNoSitesOrConnections()
    {
        var map = BundleNameMap.Empty;

        Assert.Empty(map.Sites);
        Assert.Empty(map.Connections);
    }

    [Fact]
    public void Empty_IsStableInstance()
    {
        // The Empty singleton must be reusable without aliasing surprises.
        Assert.Same(BundleNameMap.Empty, BundleNameMap.Empty);
    }

    [Fact]
    public void SiteMapping_ValueEquality_HoldsForIdenticalFields()
    {
        var a = new SiteMapping("site-east", MappingAction.MapToExisting, "site-1");
        var b = new SiteMapping("site-east", MappingAction.MapToExisting, "site-1");

        Assert.Equal(a, b);
        Assert.Equal(a.GetHashCode(), b.GetHashCode());
    }

    [Fact]
    public void SiteMapping_ValueEquality_DiffersWhenActionDiffers()
    {
        var mapTo = new SiteMapping("site-east", MappingAction.MapToExisting, "site-1");
        var createNew = new SiteMapping("site-east", MappingAction.CreateNew, null);

        Assert.NotEqual(mapTo, createNew);
    }

    [Fact]
    public void ConnectionMapping_ValueEquality_HoldsForIdenticalFields()
    {
        var a = new ConnectionMapping("site-east", "OPC-Main", MappingAction.MapToExisting, "OPC-Primary");
        var b = new ConnectionMapping("site-east", "OPC-Main", MappingAction.MapToExisting, "OPC-Primary");

        Assert.Equal(a, b);
        Assert.Equal(a.GetHashCode(), b.GetHashCode());
    }

    [Fact]
    public void MapToExisting_CarriesNonNullTarget()
    {
        var site = new SiteMapping("site-east", MappingAction.MapToExisting, "site-1");
        var conn = new ConnectionMapping("site-east", "OPC-Main", MappingAction.MapToExisting, "OPC-Primary");

        Assert.Equal(MappingAction.MapToExisting, site.Action);
        Assert.NotNull(site.TargetSiteIdentifier);
        Assert.Equal("site-1", site.TargetSiteIdentifier);

        Assert.Equal(MappingAction.MapToExisting, conn.Action);
        Assert.NotNull(conn.TargetConnectionName);
        Assert.Equal("OPC-Primary", conn.TargetConnectionName);
    }

    [Fact]
    public void CreateNew_LeavesTargetNull()
    {
        var site = new SiteMapping("site-west", MappingAction.CreateNew, null);
        var conn = new ConnectionMapping("site-west", "OPC-Aux", MappingAction.CreateNew, null);

        Assert.Null(site.TargetSiteIdentifier);
        Assert.Null(conn.TargetConnectionName);
    }

    [Fact]
    public void BundleNameMap_JsonRoundTrip_PreservesSitesAndConnections()
    {
        var map = new BundleNameMap(
            Sites: new[]
            {
                new SiteMapping("site-east", MappingAction.MapToExisting, "site-1"),
                new SiteMapping("site-west", MappingAction.CreateNew, null),
            },
            Connections: new[]
            {
                new ConnectionMapping("site-east", "OPC-Main", MappingAction.MapToExisting, "OPC-Primary"),
                new ConnectionMapping("site-west", "OPC-Aux", MappingAction.CreateNew, null),
            });

        var json = JsonSerializer.Serialize(map, JsonOpts);
        var rt = JsonSerializer.Deserialize<BundleNameMap>(json, JsonOpts);

        Assert.NotNull(rt);
        // BundleNameMap's record Equals is reference-based over its IReadOnlyList members,
        // and JSON deserialization yields List<T> rather than the original T[] — so the
        // collections never reference-equal. The records *within* the lists have proper
        // value-equality, so compare element-wise (SequenceEqual uses SiteMapping/
        // ConnectionMapping value-equality).
        Assert.True(map.Sites.SequenceEqual(rt!.Sites));
        Assert.True(map.Connections.SequenceEqual(rt.Connections));
    }

    [Fact]
    public void BundleNameMap_Empty_JsonRoundTrip_PreservesEmptiness()
    {
        var json = JsonSerializer.Serialize(BundleNameMap.Empty, JsonOpts);
        var rt = JsonSerializer.Deserialize<BundleNameMap>(json, JsonOpts);

        Assert.NotNull(rt);
        Assert.Empty(rt!.Sites);
        Assert.Empty(rt.Connections);
    }
}
