using System;
using System.Linq;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Runtime.Drivers;

namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;

/// <summary>
///     Regression coverage for Runtime-002: the artifact-decode is documented as lenient — "empty /
///     malformed blobs return an empty list / empty composition". A blob that is syntactically valid JSON
///     but carries a field of the WRONG JSON type (e.g. a string Enabled, a numeric Name) must NOT throw
///     <see cref="InvalidOperationException"/> out of the parse (the old code called GetString()/GetBoolean()
///     unguarded). It must degrade to the field default / a skipped row instead.
/// </summary>
public sealed class DeploymentArtifactMalformedTypeTests
{
    private static byte[] BlobOf(object snapshot) =>
        System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(snapshot);

    /// <summary>A string-typed Enabled ("true") must not throw; the row should still decode (default Enabled=true).</summary>
    [Fact]
    public void ParseDriverInstances_string_typed_Enabled_does_not_throw()
    {
        var blob = BlobOf(new
        {
            DriverInstances = new[]
            {
                new
                {
                    DriverInstanceRowId = Guid.NewGuid(),
                    DriverInstanceId = "d1",
                    Name = "drv-1",
                    DriverType = "Modbus",
                    Enabled = "true", // WRONG TYPE: string, not bool
                    DriverConfig = "{}",
                },
            },
        });

        var specs = Should.NotThrow(() => DeploymentArtifact.ParseDriverInstances(blob));
        specs.Count.ShouldBe(1);
        // A non-bool Enabled degrades to the field default (Enabled = true / "desired here").
        specs[0].Enabled.ShouldBeTrue();
        specs[0].DriverInstanceId.ShouldBe("d1");
    }

    /// <summary>A numeric-typed Name / DriverType must not throw; a row with an unusable required field
    /// (DriverType missing/wrong) is skipped, a row whose only wrong field is the optional Name keeps decoding.</summary>
    [Fact]
    public void ParseDriverInstances_numeric_typed_Name_does_not_throw()
    {
        var blob = BlobOf(new
        {
            DriverInstances = new[]
            {
                new
                {
                    DriverInstanceRowId = (object)Guid.NewGuid(),
                    DriverInstanceId = (object)"d1",
                    Name = (object)5, // WRONG TYPE: number, not string
                    DriverType = (object)"Modbus",
                    Enabled = (object)true,
                    DriverConfig = (object)"{}",
                },
            },
        });

        var specs = Should.NotThrow(() => DeploymentArtifact.ParseDriverInstances(blob));
        specs.Count.ShouldBe(1);
        // A non-string Name degrades to null → Name falls back to the DriverInstanceId.
        specs[0].Name.ShouldBe("d1");
    }

    /// <summary>The composition decode must also tolerate a wrong-typed UNS field (numeric UnsAreaId)
    /// without throwing — it degrades to skipping that row.</summary>
    [Fact]
    public void ParseComposition_numeric_typed_UnsAreaId_does_not_throw()
    {
        var blob = BlobOf(new
        {
            UnsAreas = new object[]
            {
                new { UnsAreaId = 42, Name = "Area-numeric" }, // WRONG TYPE: numeric id → skipped
                new { UnsAreaId = "area-ok", Name = "Area-OK" },
            },
        });

        var composition = Should.NotThrow(() => DeploymentArtifact.ParseComposition(blob));
        // The well-typed area survives; the numeric-id one is skipped (id resolves to null → dropped).
        composition.UnsAreas.Select(a => a.UnsAreaId).ShouldBe(new[] { "area-ok" });
    }

    /// <summary>A driver row whose DriverType is the wrong type is skipped (required field unusable),
    /// not a parse failure.</summary>
    [Fact]
    public void ParseDriverInstances_numeric_typed_DriverType_skips_row_without_throwing()
    {
        var blob = BlobOf(new
        {
            DriverInstances = new object[]
            {
                new
                {
                    DriverInstanceRowId = (object)Guid.NewGuid(),
                    DriverInstanceId = (object)"bad",
                    Name = (object)"drv-bad",
                    DriverType = (object)7, // WRONG TYPE: numeric → unusable required field
                    Enabled = (object)true,
                    DriverConfig = (object)"{}",
                },
                new
                {
                    DriverInstanceRowId = (object)Guid.NewGuid(),
                    DriverInstanceId = (object)"good",
                    Name = (object)"drv-good",
                    DriverType = (object)"Modbus",
                    Enabled = (object)true,
                    DriverConfig = (object)"{}",
                },
            },
        });

        var specs = Should.NotThrow(() => DeploymentArtifact.ParseDriverInstances(blob));
        specs.Select(s => s.DriverInstanceId).ShouldBe(new[] { "good" });
    }
}
